From 7fc09046f21225576fb6fbc8027e11ac4885a0f3 Mon Sep 17 00:00:00 2001 From: "masayuki@d-toybox.com" Date: Sun, 2 Dec 2007 22:51:57 -0800 Subject: [PATCH] Bug 392785 overflowed underline sometimes is not repainted at scrolling r+sr=roc, b1.9=dbaron --- gfx/public/nsIDeviceContext.h | 15 ++- layout/base/nsCSSRendering.cpp | 142 +++++++++++++++--------- layout/base/nsCSSRendering.h | 49 ++++++-- layout/base/nsPresContext.h | 4 + layout/generic/nsBlockFrame.cpp | 11 +- layout/generic/nsHTMLContainerFrame.cpp | 20 ++-- layout/generic/nsInlineFrame.cpp | 5 +- layout/generic/nsLineLayout.cpp | 69 ++++++++++++ layout/generic/nsLineLayout.h | 6 + layout/generic/nsTextFrame.h | 32 ++++++ layout/generic/nsTextFrameThebes.cpp | 118 ++++++++++++++------ layout/style/nsStyleStruct.cpp | 14 +-- layout/xul/base/src/nsTextBoxFrame.cpp | 3 - 13 files changed, 363 insertions(+), 125 deletions(-) diff --git a/gfx/public/nsIDeviceContext.h b/gfx/public/nsIDeviceContext.h index c06646bcc520..3ace8f55fc49 100644 --- a/gfx/public/nsIDeviceContext.h +++ b/gfx/public/nsIDeviceContext.h @@ -168,10 +168,10 @@ const PRUint8 kUseAltDCFor_CREATERC_PAINT = 0x04; // Use when creating Renderin const PRUint8 kUseAltDCFor_SURFACE_DIM = 0x08; // Use it for getting the Surface Dimensions #endif -// 4dd372b6-ef19-4995-a7ac-ba3efd3f656f +// 92a1e76c-adbd-441e-aae6-243d6004e0ee #define NS_IDEVICE_CONTEXT_IID \ -{ 0x4dd372b6, 0xef19, 0x4995, \ - { 0xa7, 0xac, 0xba, 0x3e, 0xfd, 0x3f, 0x65, 0x6f } } +{ 0x92a1e76c, 0xadbd, 0x441e, \ + { 0xaa, 0xe6, 0x24, 0x3d, 0x60, 0x4, 0xe0, 0xee } } //a cross platform way of specifying a native palette handle typedef void * nsPalette; @@ -295,7 +295,14 @@ public: PRInt32 AppUnitsPerDevPixel() const { return mAppUnitsPerDevPixel; } /** - * Convert app units to device pixels which is used in gfx/thebes. + * Convert device pixels which is used for gfx/thebes to nearest (rounded) + * app units + */ + nscoord GfxUnitsToAppUnits(gfxFloat aGfxUnits) const + { return nscoord(NS_round(aGfxUnits * AppUnitsPerDevPixel())); } + + /** + * Convert app units to device pixels which is used for gfx/thebes. */ gfxFloat AppUnitsToGfxUnits(nscoord aAppUnits) const { return gfxFloat(aAppUnits) / AppUnitsPerDevPixel(); } diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 7fd1a8ef7c26..35b8aaa3da71 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -4352,24 +4352,26 @@ nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext, const gfxSize& aLineSize, const gfxFloat aAscent, const gfxFloat aOffset, - const gfxFloat aPreferredHeight, const PRUint8 aDecoration, const PRUint8 aStyle, const PRBool aIsRTL) { - if (aLineSize.width <= 0 || aLineSize.height <= 0 || - aStyle == NS_STYLE_BORDER_STYLE_NONE) + gfxRect rect = + GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset, + aDecoration, aStyle); + if (rect.IsEmpty()) return; + gfxFloat lineHeight = PR_MAX(NS_round(aLineSize.height), 1.0); PRBool contextIsSaved = PR_FALSE; - gfxFloat totalHeight = aLineSize.height; switch (aStyle) { case NS_STYLE_BORDER_STYLE_SOLID: + case NS_STYLE_BORDER_STYLE_DOUBLE: break; case NS_STYLE_BORDER_STYLE_DASHED: { aGfxContext->Save(); contextIsSaved = PR_TRUE; - gfxFloat dashWidth = aLineSize.height * DOT_LENGTH * DASH_LENGTH; + gfxFloat dashWidth = lineHeight * DOT_LENGTH * DASH_LENGTH; gfxFloat dash[2] = { dashWidth, dashWidth }; aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT); aGfxContext->SetDash(dash, 2, 0.0); @@ -4378,9 +4380,9 @@ nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext, case NS_STYLE_BORDER_STYLE_DOTTED: { aGfxContext->Save(); contextIsSaved = PR_TRUE; - gfxFloat dashWidth = aLineSize.height * DOT_LENGTH; + gfxFloat dashWidth = lineHeight * DOT_LENGTH; gfxFloat dash[2]; - if (aLineSize.height > 2.0) { + if (lineHeight > 2.0) { dash[0] = 0.0; dash[1] = dashWidth * 2.0; aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND); @@ -4391,74 +4393,41 @@ nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext, aGfxContext->SetDash(dash, 2, 0.0); break; } - case NS_STYLE_BORDER_STYLE_DOUBLE: - totalHeight *= 3.0; - break; default: NS_ERROR("Invalid style value!"); return; } - gfxFloat offset = aOffset; - switch (aDecoration) { - case NS_STYLE_TEXT_DECORATION_UNDERLINE: - break; - case NS_STYLE_TEXT_DECORATION_OVERLINE: - // The offset includes the preferred size, we should remove it - offset += aPreferredHeight; - // the bottom of the decoration line should be aligned to the top of the - // text. - offset -= totalHeight; - break; - case NS_STYLE_TEXT_DECORATION_LINE_THROUGH: { - // The offset includes the preferred size, we should remove it - offset += aPreferredHeight; - // the middle of the decoration line should be aligned to the middle of - // the original strike out offset. - offset -= PR_MAX(aPreferredHeight, (totalHeight / 2.0)); - break; - } - default: - NS_ERROR("Invalid decoration value!"); - if (contextIsSaved) - aGfxContext->Restore(); - return; - } - - // round to device pixels for suppressing the AA. - gfxFloat x = NS_round(aPt.x); - gfxFloat y = NS_round(aPt.y + aAscent) - NS_round(offset); - gfxFloat width = NS_round(aLineSize.width); - gfxFloat height = NS_round(aLineSize.height); // The y position should be set to the middle of the line. - y += height / 2; + rect.pos.y += lineHeight / 2; aGfxContext->SetColor(gfxRGBA(aColor)); - aGfxContext->SetLineWidth(height); + aGfxContext->SetLineWidth(lineHeight); switch (aStyle) { case NS_STYLE_BORDER_STYLE_SOLID: aGfxContext->NewPath(); - aGfxContext->MoveTo(gfxPoint(x, y)); - aGfxContext->LineTo(gfxPoint(x + width, y)); + aGfxContext->MoveTo(rect.TopLeft()); + aGfxContext->LineTo(rect.TopRight()); aGfxContext->Stroke(); break; case NS_STYLE_BORDER_STYLE_DOUBLE: aGfxContext->NewPath(); - aGfxContext->MoveTo(gfxPoint(x, y)); - aGfxContext->LineTo(gfxPoint(x + width, y)); - aGfxContext->MoveTo(gfxPoint(x, y + height * 2.0)); - aGfxContext->LineTo(gfxPoint(x + width, y + height * 2.0)); + aGfxContext->MoveTo(rect.TopLeft()); + aGfxContext->LineTo(rect.TopRight()); + rect.size.height -= lineHeight; + aGfxContext->MoveTo(rect.BottomLeft()); + aGfxContext->LineTo(rect.BottomRight()); aGfxContext->Stroke(); break; case NS_STYLE_BORDER_STYLE_DOTTED: case NS_STYLE_BORDER_STYLE_DASHED: aGfxContext->NewPath(); if (aIsRTL) { - aGfxContext->MoveTo(gfxPoint(x + width, y)); - aGfxContext->LineTo(gfxPoint(x, y)); + aGfxContext->MoveTo(rect.TopRight()); + aGfxContext->LineTo(rect.TopLeft()); } else { - aGfxContext->MoveTo(gfxPoint(x, y)); - aGfxContext->LineTo(gfxPoint(x + width, y)); + aGfxContext->MoveTo(rect.TopLeft()); + aGfxContext->LineTo(rect.TopRight()); } aGfxContext->Stroke(); aGfxContext->Restore(); @@ -4470,3 +4439,70 @@ nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext, } NS_ASSERTION(!contextIsSaved, "The gfxContext has been saved, but not restored!"); } + +nsRect +nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext, + const gfxSize& aLineSize, + const gfxFloat aAscent, + const gfxFloat aOffset, + const PRUint8 aDecoration, + const PRUint8 aStyle) +{ + NS_ASSERTION(aPresContext, "aPresContext is null"); + + gfxRect rect = + GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset, + aDecoration, aStyle); + // The rect values are already rounded to nearest device pixels. + nsRect r; + r.x = aPresContext->GfxUnitsToAppUnits(rect.X()); + r.y = aPresContext->GfxUnitsToAppUnits(rect.Y()); + r.width = aPresContext->GfxUnitsToAppUnits(rect.Width()); + r.height = aPresContext->GfxUnitsToAppUnits(rect.Height()); + return r; +} + +gfxRect +nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt, + const gfxSize& aLineSize, + const gfxFloat aAscent, + const gfxFloat aOffset, + const PRUint8 aDecoration, + const PRUint8 aStyle) +{ + gfxRect r; + r.pos.x = NS_round(aPt.x); + r.size.width = NS_round(aLineSize.width); + + gfxFloat basesize = NS_round(aLineSize.height); + basesize = PR_MAX(basesize, 1.0); + r.size.height = basesize; + if (aStyle == NS_STYLE_BORDER_STYLE_DOUBLE) { + gfxFloat gap = NS_round(basesize / 2.0); + gap = PR_MAX(gap, 1.0); + r.size.height = basesize * 2.0 + gap; + } else { + r.size.height = basesize; + } + + gfxFloat baseline = NS_round(aPt.y + aAscent); + gfxFloat offset = 0; + switch (aDecoration) { + case NS_STYLE_TEXT_DECORATION_UNDERLINE: + offset = NS_round(aOffset); + break; + case NS_STYLE_TEXT_DECORATION_OVERLINE: + offset = NS_round(aOffset - basesize + r.Height()); + break; + case NS_STYLE_TEXT_DECORATION_LINE_THROUGH: { + gfxFloat extra = NS_round(r.Height() / 2.0); + extra = PR_MAX(extra, basesize); + offset = NS_round(aOffset - basesize + extra); + break; + } + default: + NS_ERROR("Invalid decoration value!"); + } + r.pos.y = baseline - NS_round(offset); + return r; +} diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index b0631927dd3d..035e5be5dec3 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -41,6 +41,7 @@ #define nsCSSRendering_h___ #include "nsIRenderingContext.h" +#include "nsStyleConsts.h" #include "gfxContext.h" struct nsPoint; class nsStyleContext; @@ -201,8 +202,8 @@ public: /** * Function for painting the decoration lines for the text. - * NOTE: aPt, aLineSize, aAscent, aOffset and aPreferredHeight are non-rounded - * device pixels, not app units. + * NOTE: aPt, aLineSize, aAscent and aOffset are non-rounded device pixels, + * not app units. * input: * @param aGfxContext * @param aColor the color of the decoration line @@ -213,8 +214,6 @@ public: * @param aOffset the offset of the decoration line from * the baseline of the text (if the value is * positive, the line is lifted up) - * @param aPreferredHeight the preferred size of the decoration line by - * the font of the text * @param aDecoration which line will be painted. The value can be * NS_STYLE_TEXT_DECORATION_UNDERLINE or * NS_STYLE_TEXT_DECORATION_OVERLINE or @@ -223,8 +222,7 @@ public: * can be NS_STYLE_BORDER_STYLE_SOLID or * NS_STYLE_BORDER_STYLE_DOTTED or * NS_STYLE_BORDER_STYLE_DASHED or - * NS_STYLE_BORDER_STYLE_DOUBLE or - * NS_STYLE_BORDER_STYLE_NONE. + * NS_STYLE_BORDER_STYLE_DOUBLE. * @param aIsRTL when the text is RTL, it is true. */ static void PaintDecorationLine(gfxContext* aGfxContext, @@ -233,11 +231,42 @@ public: const gfxSize& aLineSize, const gfxFloat aAscent, const gfxFloat aOffset, - const gfxFloat aPreferredSize, const PRUint8 aDecoration, const PRUint8 aStyle, const PRBool aIsRTL); + /** + * Function for getting the decoration line rect for the text. + * NOTE: aLineSize, aAscent and aOffset are non-rounded device pixels, + * not app units. + * input: + * @param aPresContext + * @param aLineSize the width and the height of the decoration + * line + * @param aAscent the ascent of the text + * @param aOffset the offset of the decoration line from + * the baseline of the text (if the value is + * positive, the line is lifted up) + * @param aDecoration which line will be painted. The value can be + * NS_STYLE_TEXT_DECORATION_UNDERLINE or + * NS_STYLE_TEXT_DECORATION_OVERLINE or + * NS_STYLE_TEXT_DECORATION_LINE_THROUGH. + * @param aStyle the style of the decoration line. The value + * can be NS_STYLE_BORDER_STYLE_SOLID or + * NS_STYLE_BORDER_STYLE_DOTTED or + * NS_STYLE_BORDER_STYLE_DASHED or + * NS_STYLE_BORDER_STYLE_DOUBLE. + * output: + * @return the decoration line rect for the input, + * the each values are app units. + */ + static nsRect GetTextDecorationRect(nsPresContext* aPresContext, + const gfxSize& aLineSize, + const gfxFloat aAscent, + const gfxFloat aOffset, + const PRUint8 aDecoration, + const PRUint8 aStyle); + protected: static void PaintBackgroundColor(nsPresContext* aPresContext, @@ -271,6 +300,12 @@ protected: PRInt32 aNumPoints, nsRect* aGap); + static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt, + const gfxSize& aLineSize, + const gfxFloat aAscent, + const gfxFloat aOffset, + const PRUint8 aDecoration, + const PRUint8 aStyle); }; diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index a5e5ffad0bb8..997d2144331e 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -509,6 +509,10 @@ public: { return NSAppUnitsToIntPixels(aAppUnits, mDeviceContext->AppUnitsPerDevPixel()); } + // If there is a remainder, it is rounded to nearest app units. + nscoord GfxUnitsToAppUnits(gfxFloat aGfxUnits) const + { return mDeviceContext->GfxUnitsToAppUnits(aGfxUnits); } + gfxFloat AppUnitsToGfxUnits(nscoord aAppUnits) const { return mDeviceContext->AppUnitsToGfxUnits(aAppUnits); } diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index d800db99f8ad..7cdcd5c609e2 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -4001,6 +4001,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsRect combinedArea; aLineLayout.RelativePositionFrames(combinedArea); // XXXldb This returned width as -15, 2001-06-12, Bugzilla + if (aState.mPresContext->CompatibilityMode() != eCompatibility_NavQuirks) { + PRUint8 decorations; + nscolor underColor, overColor, strikeColor; + GetTextDecorations(aState.mPresContext, PR_TRUE, decorations, + underColor, overColor, strikeColor); + if (decorations) { + nsLineLayout::CombineTextDecorations(aState.mPresContext, decorations, + this, combinedArea); + } + } aLine->SetCombinedArea(combinedArea); if (addedBullet) { aLineLayout.RemoveBulletFrame(mBullet); @@ -5875,7 +5885,6 @@ nsBlockFrame::PaintTextDecorationLine(nsIRenderingContext& aRenderingContext, ctx, aColor, pt, size, PresContext()->AppUnitsToGfxUnits(aLine->GetAscent()), PresContext()->AppUnitsToGfxUnits(aOffset), - PresContext()->AppUnitsToGfxUnits(aSize), aDecoration, NS_STYLE_BORDER_STYLE_SOLID, isRTL); } } diff --git a/layout/generic/nsHTMLContainerFrame.cpp b/layout/generic/nsHTMLContainerFrame.cpp index ce581ca47f94..d83ae3cc3a7e 100644 --- a/layout/generic/nsHTMLContainerFrame.cpp +++ b/layout/generic/nsHTMLContainerFrame.cpp @@ -85,6 +85,7 @@ public: virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx, const nsRect& aDirtyRect); + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder); NS_DISPLAY_DECL_NAME("TextDecoration") private: nsLineBox* mLine; @@ -122,6 +123,12 @@ nsDisplayTextDecoration::Paint(nsDisplayListBuilder* aBuilder, } } +nsRect +nsDisplayTextDecoration::GetBounds(nsDisplayListBuilder* aBuilder) +{ + return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame); +} + nsresult nsHTMLContainerFrame::DisplayTextDecorations(nsDisplayListBuilder* aBuilder, nsDisplayList* aBelowTextDecorations, @@ -188,7 +195,7 @@ nsHTMLContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, } static PRBool -HasTextFrameDescendantOrInFlow(nsPresContext* aPresContext, nsIFrame* aFrame); +HasTextFrameDescendantOrInFlow(nsIFrame* aFrame); /*virtual*/ void nsHTMLContainerFrame::PaintTextDecorationLine( @@ -221,7 +228,6 @@ nsHTMLContainerFrame::PaintTextDecorationLine( nsCSSRendering::PaintDecorationLine( ctx, aColor, pt, size, PresContext()->AppUnitsToGfxUnits(aAscent), PresContext()->AppUnitsToGfxUnits(aOffset), - PresContext()->AppUnitsToGfxUnits(aSize), aDecoration, NS_STYLE_BORDER_STYLE_SOLID, isRTL); } @@ -297,14 +303,14 @@ nsHTMLContainerFrame::GetTextDecorations(nsPresContext* aPresContext, if (aDecorations) { // If this frame contains no text, we're required to ignore this property - if (!HasTextFrameDescendantOrInFlow(aPresContext, this)) { + if (!HasTextFrameDescendantOrInFlow(this)) { aDecorations = NS_STYLE_TEXT_DECORATION_NONE; } } } static PRBool -HasTextFrameDescendant(nsPresContext* aPresContext, nsIFrame* aParent) +HasTextFrameDescendant(nsIFrame* aParent) { for (nsIFrame* kid = aParent->GetFirstChild(nsnull); kid; kid = kid->GetNextSibling()) @@ -317,7 +323,7 @@ HasTextFrameDescendant(nsPresContext* aPresContext, nsIFrame* aParent) return PR_TRUE; } } - if (HasTextFrameDescendant(aPresContext, kid)) { + if (HasTextFrameDescendant(kid)) { return PR_TRUE; } } @@ -325,10 +331,10 @@ HasTextFrameDescendant(nsPresContext* aPresContext, nsIFrame* aParent) } static PRBool -HasTextFrameDescendantOrInFlow(nsPresContext* aPresContext, nsIFrame* aFrame) +HasTextFrameDescendantOrInFlow(nsIFrame* aFrame) { for (nsIFrame *f = aFrame->GetFirstInFlow(); f; f = f->GetNextInFlow()) { - if (HasTextFrameDescendant(aPresContext, f)) + if (HasTextFrameDescendant(f)) return PR_TRUE; } return PR_FALSE; diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index 2a087b78228f..e7153eba19d4 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -568,8 +568,7 @@ nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, // affect our height. fm->GetMaxAscent(aMetrics.ascent); fm->GetHeight(aMetrics.height); - // Include the text-decoration lines to the height. - // Currently, only underline is overflowable. + // Include the text-decoration lines to the height for readable. nscoord offset, size; fm->GetUnderline(offset, size); nscoord ascentAndUnderline = @@ -586,7 +585,7 @@ nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, aReflowState.mComputedBorderPadding.bottom; // For now our overflow area is zero. The real value will be - // computed during vertical alignment of the line we are on. + // computed in |nsLineLayout::RelativePositionFrames|. aMetrics.mOverflowArea.SetRect(0, 0, 0, 0); #ifdef NOISY_FINAL_SIZE diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index 3b5a68e36b46..d25ce19b278f 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -66,6 +66,7 @@ #include "nsBidiUtils.h" #include "nsLayoutUtils.h" #include "nsTextFrame.h" +#include "nsCSSRendering.h" #ifdef DEBUG #undef NOISY_HORIZONTAL_ALIGN @@ -2624,6 +2625,8 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea) combinedAreaResult.height = mFinalLineHeight; } + PRBool isStandardsMode = + mPresContext->CompatibilityMode() != eCompatibility_NavQuirks; for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { nsPoint origin = nsPoint(pfd->mBounds.x, pfd->mBounds.y); nsIFrame* frame = pfd->mFrame; @@ -2652,6 +2655,15 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea) // x and y which were computed above. nsRect r; if (pfd->mSpan) { + if (isStandardsMode) { + // Combine the text decoration area for inline elements of standards + // mode + PRUint8 decorations = frame->GetStyleTextReset()->mTextDecoration; + if (decorations) { + nsLineLayout::CombineTextDecorations(mPresContext, decorations, + frame, pfd->mSpan->mFrame->mCombinedArea); + } + } // Compute a new combined area for the child span before // aggregating it into our combined area. RelativePositionFrames(pfd->mSpan, r); @@ -2695,3 +2707,60 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea) } aCombinedArea = combinedAreaResult; } + +void +nsLineLayout::CombineTextDecorations(nsPresContext* aPresContext, + PRUint8 aDecorations, + nsIFrame* aFrame, + nsRect& aCombinedArea, + nscoord aAscentOverride, + float aUnderlineSizeRatio) +{ + if (!(aDecorations & (NS_STYLE_TEXT_DECORATION_UNDERLINE | + NS_STYLE_TEXT_DECORATION_OVERLINE | + NS_STYLE_TEXT_DECORATION_LINE_THROUGH))) + return; + + nsCOMPtr fm; + nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm)); + if (aAscentOverride == 0) + fm->GetMaxAscent(aAscentOverride); + gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(aAscentOverride); + nsRect decorationArea; + if (aDecorations & (NS_STYLE_TEXT_DECORATION_UNDERLINE | + NS_STYLE_TEXT_DECORATION_OVERLINE)) { + nscoord offsetCoord, sizeCoord; + fm->GetUnderline(offsetCoord, sizeCoord); + gfxSize size(aPresContext->AppUnitsToGfxUnits(aCombinedArea.width), + aPresContext->AppUnitsToGfxUnits(sizeCoord)); + if (aDecorations & NS_STYLE_TEXT_DECORATION_OVERLINE) { + decorationArea = + nsCSSRendering::GetTextDecorationRect(aPresContext, size, ascent, + ascent, NS_STYLE_TEXT_DECORATION_OVERLINE, + NS_STYLE_BORDER_STYLE_SOLID); + aCombinedArea.UnionRect(aCombinedArea, decorationArea); + } + if (aDecorations & NS_STYLE_TEXT_DECORATION_UNDERLINE) { + aUnderlineSizeRatio = PR_MAX(aUnderlineSizeRatio, 1.0); + size.height *= aUnderlineSizeRatio; + gfxFloat offset = aPresContext->AppUnitsToGfxUnits(offsetCoord); + decorationArea = + nsCSSRendering::GetTextDecorationRect(aPresContext, size, ascent, + offset, NS_STYLE_TEXT_DECORATION_UNDERLINE, + NS_STYLE_BORDER_STYLE_SOLID); + aCombinedArea.UnionRect(aCombinedArea, decorationArea); + } + } + if (aDecorations & NS_STYLE_TEXT_DECORATION_LINE_THROUGH) { + nscoord offsetCoord, sizeCoord; + fm->GetStrikeout(offsetCoord, sizeCoord); + gfxSize size(aPresContext->AppUnitsToGfxUnits(aCombinedArea.width), + aPresContext->AppUnitsToGfxUnits(sizeCoord)); + gfxFloat offset = aPresContext->AppUnitsToGfxUnits(offsetCoord); + decorationArea = + nsCSSRendering::GetTextDecorationRect(aPresContext, size, ascent, + offset, NS_STYLE_TEXT_DECORATION_LINE_THROUGH, + NS_STYLE_BORDER_STYLE_SOLID); + aCombinedArea.UnionRect(aCombinedArea, decorationArea); + } +} diff --git a/layout/generic/nsLineLayout.h b/layout/generic/nsLineLayout.h index 7170564a522a..63026941fa61 100644 --- a/layout/generic/nsLineLayout.h +++ b/layout/generic/nsLineLayout.h @@ -132,6 +132,12 @@ public: */ void RelativePositionFrames(nsRect& aCombinedArea); + static void CombineTextDecorations(nsPresContext* aPresContext, + PRUint8 aDecorations, + nsIFrame* aFrame, + nsRect& aCombinedArea, + nscoord aAscentOverride = 0, + float aUnderlineSizeRatio = 1.0f); //---------------------------------------- // Supporting methods and data for flags diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h index 8c90d8211d8a..8b590efec8dd 100644 --- a/layout/generic/nsTextFrame.h +++ b/layout/generic/nsTextFrame.h @@ -359,6 +359,38 @@ protected: PRBool isRTLChars, PRBool isOddLevel, PRBool isBidiSystem); + + void UnionTextDecorationOverflow(nsPresContext* aPresContext, + const gfxTextRun::Metrics& aTextMetrics, + nsRect* aOverflowRect); + + struct TextDecorations { + PRUint8 mDecorations; + nscolor mOverColor; + nscolor mUnderColor; + nscolor mStrikeColor; + + TextDecorations() : + mDecorations(0), mOverColor(NS_RGB(0, 0, 0)), + mUnderColor(NS_RGB(0, 0, 0)), mStrikeColor(NS_RGB(0, 0, 0)) + { } + + PRBool HasDecorationlines() { + return !!(mDecorations & (NS_STYLE_TEXT_DECORATION_UNDERLINE | + NS_STYLE_TEXT_DECORATION_OVERLINE | + NS_STYLE_TEXT_DECORATION_LINE_THROUGH)); + } + PRBool HasUnderline() { + return !!(mDecorations & NS_STYLE_TEXT_DECORATION_UNDERLINE); + } + PRBool HasOverline() { + return !!(mDecorations & NS_STYLE_TEXT_DECORATION_OVERLINE); + } + PRBool HasStrikeout() { + return !!(mDecorations & NS_STYLE_TEXT_DECORATION_LINE_THROUGH); + } + }; + TextDecorations GetTextDecorations(nsPresContext* aPresContext); }; #endif diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index a31911ff9e29..d7466b45280f 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -3546,39 +3546,35 @@ FillClippedRect(gfxContext* aCtx, nsPresContext* aPresContext, aCtx->Fill(); } -void -nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect, - const gfxPoint& aFramePt, - const gfxPoint& aTextBaselinePt, - nsTextPaintStyle& aTextPaintStyle, - PropertyProvider& aProvider) +nsTextFrame::TextDecorations +nsTextFrame::GetTextDecorations(nsPresContext* aPresContext) { + TextDecorations decorations; + // Quirks mode text decoration are rendered by children; see bug 1777 // In non-quirks mode, nsHTMLContainer::Paint and nsBlockFrame::Paint // does the painting of text decorations. - if (eCompatibility_NavQuirks != aTextPaintStyle.PresContext()->CompatibilityMode()) - return; + if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode()) + return decorations; PRBool useOverride = PR_FALSE; nscolor overrideColor; - PRUint8 decorations = NS_STYLE_TEXT_DECORATION_NONE; // A mask of all possible decorations. PRUint8 decorMask = NS_STYLE_TEXT_DECORATION_UNDERLINE | NS_STYLE_TEXT_DECORATION_OVERLINE | - NS_STYLE_TEXT_DECORATION_LINE_THROUGH; - nscolor overColor, underColor, strikeColor; - nsStyleContext* context = GetStyleContext(); - PRBool hasDecorations = context->HasTextDecorations(); + NS_STYLE_TEXT_DECORATION_LINE_THROUGH; - while (hasDecorations) { + for (nsStyleContext* context = GetStyleContext(); + decorMask && context && context->HasTextDecorations(); + context = context->GetParent()) { const nsStyleTextReset* styleText = context->GetStyleTextReset(); if (!useOverride && (NS_STYLE_TEXT_DECORATION_OVERRIDE_ALL & styleText->mTextDecoration)) { // This handles the La // la la case. The link underline should be green. useOverride = PR_TRUE; - overrideColor = context->GetStyleColor()->mColor; + overrideColor = context->GetStyleColor()->mColor; } PRUint8 useDecorations = decorMask & styleText->mTextDecoration; @@ -3586,30 +3582,59 @@ nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect, nscolor color = context->GetStyleColor()->mColor; if (NS_STYLE_TEXT_DECORATION_UNDERLINE & useDecorations) { - underColor = useOverride ? overrideColor : color; + decorations.mUnderColor = useOverride ? overrideColor : color; decorMask &= ~NS_STYLE_TEXT_DECORATION_UNDERLINE; - decorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE; + decorations.mDecorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE; } if (NS_STYLE_TEXT_DECORATION_OVERLINE & useDecorations) { - overColor = useOverride ? overrideColor : color; + decorations.mOverColor = useOverride ? overrideColor : color; decorMask &= ~NS_STYLE_TEXT_DECORATION_OVERLINE; - decorations |= NS_STYLE_TEXT_DECORATION_OVERLINE; + decorations.mDecorations |= NS_STYLE_TEXT_DECORATION_OVERLINE; } if (NS_STYLE_TEXT_DECORATION_LINE_THROUGH & useDecorations) { - strikeColor = useOverride ? overrideColor : color; + decorations.mStrikeColor = useOverride ? overrideColor : color; decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_THROUGH; - decorations |= NS_STYLE_TEXT_DECORATION_LINE_THROUGH; + decorations.mDecorations |= NS_STYLE_TEXT_DECORATION_LINE_THROUGH; } } - if (0 == decorMask) - break; - context = context->GetParent(); - if (!context) - break; - hasDecorations = context->HasTextDecorations(); } - if (!decorations) + return decorations; +} + +void +nsTextFrame::UnionTextDecorationOverflow( + nsPresContext* aPresContext, + const gfxTextRun::Metrics& aTextMetrics, + nsRect* aOverflowRect) +{ + NS_ASSERTION(mTextRun, "mTextRun is null"); + nsRect rect; + TextDecorations decorations = GetTextDecorations(aPresContext); + float ratio = 1.0f; + // Note that we need to add underline area when this frame has selection for + // spellchecking and IME. + if (mState & NS_FRAME_SELECTED_CONTENT) { + nsILookAndFeel* look = aPresContext->LookAndFeel(); + look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize, + ratio); + decorations.mDecorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE; + } + nsLineLayout::CombineTextDecorations(aPresContext, decorations.mDecorations, + this, *aOverflowRect, nscoord(NS_round(aTextMetrics.mAscent)), + ratio); +} + +void +nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect, + const gfxPoint& aFramePt, + const gfxPoint& aTextBaselinePt, + nsTextPaintStyle& aTextPaintStyle, + PropertyProvider& aProvider) +{ + TextDecorations decorations = + GetTextDecorations(aTextPaintStyle.PresContext()); + if (!decorations.HasDecorationlines()) return; gfxFont::Metrics fontMetrics = GetFontMetrics(aProvider.GetFontGroup()); @@ -3620,26 +3645,26 @@ nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect, gfxSize size(GetRect().width / app, 0); gfxFloat ascent = gfxFloat(mAscent) / app; - if (decorations & NS_FONT_DECORATION_OVERLINE) { + if (decorations.HasOverline()) { size.height = fontMetrics.underlineSize; nsCSSRendering::PaintDecorationLine( - aCtx, overColor, pt, size, ascent, ascent, size.height, + aCtx, decorations.mOverColor, pt, size, ascent, ascent, NS_STYLE_TEXT_DECORATION_OVERLINE, NS_STYLE_BORDER_STYLE_SOLID, mTextRun->IsRightToLeft()); } - if (decorations & NS_FONT_DECORATION_UNDERLINE) { + if (decorations.HasUnderline()) { size.height = fontMetrics.underlineSize; gfxFloat offset = fontMetrics.underlineOffset; nsCSSRendering::PaintDecorationLine( - aCtx, underColor, pt, size, ascent, offset, size.height, + aCtx, decorations.mUnderColor, pt, size, ascent, offset, NS_STYLE_TEXT_DECORATION_UNDERLINE, NS_STYLE_BORDER_STYLE_SOLID, mTextRun->IsRightToLeft()); } - if (decorations & NS_FONT_DECORATION_LINE_THROUGH) { + if (decorations.HasStrikeout()) { size.height = fontMetrics.strikeoutSize; gfxFloat offset = fontMetrics.strikeoutOffset; nsCSSRendering::PaintDecorationLine( - aCtx, strikeColor, pt, size, ascent, offset, size.height, + aCtx, decorations.mStrikeColor, pt, size, ascent, offset, NS_STYLE_TEXT_DECORATION_UNDERLINE, NS_STYLE_BORDER_STYLE_SOLID, mTextRun->IsRightToLeft()); } @@ -3667,7 +3692,7 @@ static void DrawIMEUnderline(gfxContext* aContext, PRInt32 aIndex, gfxFloat width = PR_MAX(0, aWidth - 2.0 * aSize); gfxPoint pt(aPt.x + 1.0, aPt.y); nsCSSRendering::PaintDecorationLine( - aContext, color, pt, gfxSize(width, actualSize), aAscent, aOffset, aSize, + aContext, color, pt, gfxSize(width, actualSize), aAscent, aOffset, NS_STYLE_TEXT_DECORATION_UNDERLINE, style, aIsRTL); } @@ -3685,7 +3710,7 @@ static void DrawSelectionDecorations(gfxContext* aContext, SelectionType aType, case nsISelectionController::SELECTION_SPELLCHECK: { nsCSSRendering::PaintDecorationLine( aContext, NS_RGB(255,0,0), - aPt, size, aAscent, aFontMetrics.underlineOffset, size.height, + aPt, size, aAscent, aFontMetrics.underlineOffset, NS_STYLE_TEXT_DECORATION_UNDERLINE, NS_STYLE_BORDER_STYLE_DOTTED, aIsRTL); break; @@ -4313,6 +4338,7 @@ nsTextFrame::SetSelected(nsPresContext* aPresContext, found = PR_TRUE; } + nsFrameState oldState = mState; if ( aSelected ) AddStateBits(NS_FRAME_SELECTED_CONTENT); else @@ -4325,6 +4351,20 @@ nsTextFrame::SetSelected(nsPresContext* aPresContext, } } if (found) { + // If the selection state is changed, we need to reflow to recompute + // the overflow area for underline of spellchecking or IME. However, if + // the non-selected text already has underline, we don't need to reflow. + // And also when the IME underline is thicker than normal underline. + nsILookAndFeel* look = aPresContext->LookAndFeel(); + float ratio; + look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize, + ratio); + if (oldState != mState && + (ratio > 1.0 || !GetTextDecorations(aPresContext).HasUnderline())) { + PresContext()->PresShell()->FrameNeedsReflow(this, + nsIPresShell::eStyleChange, + NS_FRAME_IS_DIRTY); + } // Selection might change anything. Invalidate the overflow area. Invalidate(GetOverflowRect(), PR_FALSE); } @@ -5447,6 +5487,9 @@ nsTextFrame::Reflow(nsPresContext* aPresContext, aMetrics.mOverflowArea.UnionRect(boundingBox, nsRect(0, 0, aMetrics.width, aMetrics.height)); + UnionTextDecorationOverflow(aPresContext, textMetrics, + &aMetrics.mOverflowArea); + ///////////////////////////////////////////////////////////////////// // Clean up, update state ///////////////////////////////////////////////////////////////////// @@ -5649,6 +5692,9 @@ nsTextFrame::RecomputeOverflowRect() ConvertGfxRectOutward(textMetrics.mBoundingBox + gfxPoint(0, textMetrics.mAscent)); boundingBox.UnionRect(boundingBox, nsRect(nsPoint(0,0), GetSize())); + + UnionTextDecorationOverflow(PresContext(), textMetrics, &boundingBox); + return boundingBox; } diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 77435e18d7e2..7852156a3b0f 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1512,18 +1512,10 @@ nsStyleTextReset::~nsStyleTextReset(void) { } nsChangeHint nsStyleTextReset::CalcDifference(const nsStyleTextReset& aOther) const { - if (mVerticalAlign == aOther.mVerticalAlign - && mUnicodeBidi == aOther.mUnicodeBidi) { - if (mTextDecoration != aOther.mTextDecoration) { - // Reflow for blink changes, repaint for others - return - (mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) == - (aOther.mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) ? - NS_STYLE_HINT_VISUAL : NS_STYLE_HINT_REFLOW; - } - + if (mVerticalAlign == aOther.mVerticalAlign && + mUnicodeBidi == aOther.mUnicodeBidi && + mTextDecoration == aOther.mTextDecoration) return NS_STYLE_HINT_NONE; - } return NS_STYLE_HINT_REFLOW; } diff --git a/layout/xul/base/src/nsTextBoxFrame.cpp b/layout/xul/base/src/nsTextBoxFrame.cpp index 79523933767e..26bbd345133d 100644 --- a/layout/xul/base/src/nsTextBoxFrame.cpp +++ b/layout/xul/base/src/nsTextBoxFrame.cpp @@ -465,7 +465,6 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, nsCSSRendering::PaintDecorationLine(ctx, overColor, pt, gfxSize(width, sizePixel), ascentPixel, ascentPixel, - sizePixel, NS_STYLE_TEXT_DECORATION_OVERLINE, NS_STYLE_BORDER_STYLE_SOLID, isRTL); @@ -474,7 +473,6 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, nsCSSRendering::PaintDecorationLine(ctx, underColor, pt, gfxSize(width, sizePixel), ascentPixel, offsetPixel, - sizePixel, NS_STYLE_TEXT_DECORATION_UNDERLINE, NS_STYLE_BORDER_STYLE_SOLID, isRTL); @@ -487,7 +485,6 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, nsCSSRendering::PaintDecorationLine(ctx, underColor, pt, gfxSize(width, sizePixel), ascentPixel, offsetPixel, - sizePixel, NS_STYLE_TEXT_DECORATION_LINE_THROUGH, NS_STYLE_BORDER_STYLE_SOLID, isRTL);