Bug 312156 - Implement CSS3 text-overflow. r=roc

This commit is contained in:
Mats Palmgren
2011-06-22 20:11:48 +02:00
parent c3d153f2f0
commit c12d6a33ea
16 changed files with 1292 additions and 126 deletions

View File

@@ -3208,39 +3208,6 @@ EnsureDifferentColors(nscolor colorA, nscolor colorB)
//-----------------------------------------------------------------------------
static nscolor
DarkenColor(nscolor aColor)
{
PRUint16 hue, sat, value;
PRUint8 alpha;
// convert the RBG to HSV so we can get the lightness (which is the v)
NS_RGB2HSV(aColor, hue, sat, value, alpha);
// The goal here is to send white to black while letting colored
// stuff stay colored... So we adopt the following approach.
// Something with sat = 0 should end up with value = 0. Something
// with a high sat can end up with a high value and it's ok.... At
// the same time, we don't want to make things lighter. Do
// something simple, since it seems to work.
if (value > sat) {
value = sat;
// convert this color back into the RGB color space.
NS_HSV2RGB(aColor, hue, sat, value, alpha);
}
return aColor;
}
// Check whether we should darken text colors. We need to do this if
// background images and colors are being suppressed, because that means
// light text will not be visible against the (presumed light-colored) background.
static PRBool
ShouldDarkenColors(nsPresContext* aPresContext)
{
return !aPresContext->GetBackgroundColorDraw() &&
!aPresContext->GetBackgroundImageDraw();
}
nsTextPaintStyle::nsTextPaintStyle(nsTextFrame* aFrame)
: mFrame(aFrame),
mPresContext(aFrame->PresContext()),
@@ -3279,11 +3246,7 @@ nsTextPaintStyle::EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackCo
nscolor
nsTextPaintStyle::GetTextColor()
{
nscolor color = mFrame->GetVisitedDependentColor(eCSSProperty_color);
if (ShouldDarkenColors(mPresContext)) {
color = DarkenColor(color);
}
return color;
return nsLayoutUtils::GetTextColor(mFrame);
}
PRBool
@@ -4169,10 +4132,10 @@ nsTextFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
ClearTextRun(nsnull);
}
class nsDisplayText : public nsDisplayItem {
class nsDisplayText : public nsCharClipDisplayItem {
public:
nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame) :
nsDisplayItem(aBuilder, aFrame),
nsCharClipDisplayItem(aBuilder, aFrame),
mDisableSubpixelAA(PR_FALSE) {
MOZ_COUNT_CTOR(nsDisplayText);
}
@@ -4218,7 +4181,9 @@ nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
gfxContextAutoDisableSubpixelAntialiasing disable(aCtx->ThebesContext(),
mDisableSubpixelAA);
f->PaintText(aCtx, ToReferenceFrame(), extraVisible);
NS_ASSERTION(mLeftEdge >= 0, "illegal left edge");
NS_ASSERTION(mRightEdge >= 0, "illegal right edge");
f->PaintText(aCtx, ToReferenceFrame(), extraVisible, *this);
}
NS_IMETHODIMP
@@ -4434,12 +4399,14 @@ nsTextFrame::UnionTextDecorationOverflow(nsPresContext* aPresContext,
}
void
nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
const gfxPoint& aFramePt,
const gfxPoint& aTextBaselinePt,
nsTextPaintStyle& aTextPaintStyle,
PropertyProvider& aProvider,
const nscolor* aOverrideColor)
nsTextFrame::PaintTextDecorations(
gfxContext* aCtx, const gfxRect& aDirtyRect,
const gfxPoint& aFramePt,
const gfxPoint& aTextBaselinePt,
nsTextPaintStyle& aTextPaintStyle,
PropertyProvider& aProvider,
const nsCharClipDisplayItem::ClipEdges& aClipEdges,
const nscolor* aOverrideColor)
{
TextDecorations decorations =
GetTextDecorations(aTextPaintStyle.PresContext());
@@ -4457,8 +4424,11 @@ nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
gfxFloat app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
// XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
gfxPoint pt(aFramePt.x / app, (aTextBaselinePt.y - mAscent) / app);
gfxSize size(GetRect().width / app, 0);
nscoord x = aFramePt.x;
nscoord width = GetRect().width;
aClipEdges.Intersect(&x, &width);
gfxPoint pt(x / app, (aTextBaselinePt.y - mAscent) / app);
gfxSize size(width / app, 0);
gfxFloat ascent = gfxFloat(mAscent) / app;
nscolor lineColor;
@@ -4811,7 +4781,9 @@ nsTextFrame::PaintOneShadow(PRUint32 aOffset, PRUint32 aLength,
nsCSSShadowItem* aShadowDetails,
PropertyProvider* aProvider, const nsRect& aDirtyRect,
const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
gfxContext* aCtx, const nscolor& aForegroundColor)
gfxContext* aCtx, const nscolor& aForegroundColor,
const nsCharClipDisplayItem::ClipEdges& aClipEdges,
nscoord aLeftSideOffset)
{
gfxPoint shadowOffset(aShadowDetails->mXOffset, aShadowDetails->mYOffset);
nscoord blurRadius = NS_MAX(aShadowDetails->mRadius, 0);
@@ -4827,7 +4799,7 @@ nsTextFrame::PaintOneShadow(PRUint32 aOffset, PRUint32 aLength,
// The origin of mBoundingBox is the text baseline left, so we must translate it by
// that much in order to make the origin the top-left corner of the text bounding box.
gfxRect shadowGfxRect = shadowMetrics.mBoundingBox +
gfxPoint(aFramePt.x, aTextBaselinePt.y) + shadowOffset;
gfxPoint(aFramePt.x + aLeftSideOffset, aTextBaselinePt.y) + shadowOffset;
nsRect shadowRect(shadowGfxRect.X(), shadowGfxRect.Y(),
shadowGfxRect.Width(), shadowGfxRect.Height());
@@ -4864,7 +4836,7 @@ nsTextFrame::PaintOneShadow(PRUint32 aOffset, PRUint32 aLength,
nsTextPaintStyle textPaintStyle(this);
PaintTextDecorations(shadowContext, dirtyGfxRect, aFramePt + shadowOffset,
aTextBaselinePt + shadowOffset,
textPaintStyle, *aProvider, &shadowColor);
textPaintStyle, *aProvider, aClipEdges, &shadowColor);
contextBoxBlur.DoPaint();
aCtx->Restore();
@@ -5042,7 +5014,8 @@ PRBool
nsTextFrame::PaintTextWithSelection(gfxContext* aCtx,
const gfxPoint& aFramePt,
const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
PropertyProvider& aProvider, nsTextPaintStyle& aTextPaintStyle)
PropertyProvider& aProvider, nsTextPaintStyle& aTextPaintStyle,
const nsCharClipDisplayItem::ClipEdges& aClipEdges)
{
SelectionDetails* details = GetSelectionDetails();
if (!details)
@@ -5052,7 +5025,7 @@ nsTextFrame::PaintTextWithSelection(gfxContext* aCtx,
PaintTextWithSelectionColors(aCtx, aFramePt, aTextBaselinePt, aDirtyRect,
aProvider, aTextPaintStyle, details, &allTypes);
PaintTextDecorations(aCtx, aDirtyRect, aFramePt, aTextBaselinePt,
aTextPaintStyle, aProvider);
aTextPaintStyle, aProvider, aClipEdges);
PRInt32 i;
// Iterate through just the selection types that paint decorations and
// paint decorations for any that actually occur in this frame. Paint
@@ -5136,9 +5109,115 @@ nsTextFrame::GetSnappedBaselineY(gfxContext* aContext, gfxFloat aY)
return aContext->DeviceToUser(putativeRect.TopLeft()).y*appUnitsPerDevUnit;
}
bool
nsTextFrame::MeasureCharClippedText(gfxContext* aCtx,
nscoord aLeftEdge, nscoord aRightEdge,
nscoord* aSnappedLeftEdge,
nscoord* aSnappedRightEdge)
{
// Don't pass in aRenderingContext here, because we need a *reference*
// context and aRenderingContext might have some transform in it
// XXX get the block and line passed to us somehow! This is slow!
gfxSkipCharsIterator iter = EnsureTextRun();
if (!mTextRun)
return false;
PropertyProvider provider(this, iter);
// Trim trailing whitespace
provider.InitializeForDisplay(PR_TRUE);
PRUint32 startOffset = provider.GetStart().GetSkippedOffset();
PRUint32 maxLength = ComputeTransformedLength(provider);
return MeasureCharClippedText(aCtx, provider, aLeftEdge, aRightEdge,
&startOffset, &maxLength,
aSnappedLeftEdge, aSnappedRightEdge);
}
static PRUint32 GetClusterLength(gfxTextRun* aTextRun,
PRUint32 aStartOffset,
PRUint32 aMaxLength,
bool aIsRTL)
{
PRUint32 clusterLength = aIsRTL ? 0 : 1;
while (clusterLength < aMaxLength) {
if (aTextRun->IsClusterStart(aStartOffset + clusterLength)) {
if (aIsRTL) {
++clusterLength;
}
break;
}
++clusterLength;
}
return clusterLength;
}
bool
nsTextFrame::MeasureCharClippedText(gfxContext* aCtx,
PropertyProvider& aProvider,
nscoord aLeftEdge, nscoord aRightEdge,
PRUint32* aStartOffset,
PRUint32* aMaxLength,
nscoord* aSnappedLeftEdge,
nscoord* aSnappedRightEdge)
{
*aSnappedLeftEdge = 0;
*aSnappedRightEdge = 0;
if (aLeftEdge <= 0 && aRightEdge <= 0) {
return true;
}
PRUint32 offset = *aStartOffset;
PRUint32 maxLength = *aMaxLength;
const nscoord frameWidth = GetSize().width;
const PRBool rtl = mTextRun->IsRightToLeft();
gfxFloat advanceWidth = 0;
const nscoord startEdge = rtl ? aRightEdge : aLeftEdge;
if (startEdge > 0) {
const gfxFloat maxAdvance = gfxFloat(startEdge);
while (maxLength > 0) {
PRUint32 clusterLength =
GetClusterLength(mTextRun, offset, maxLength, rtl);
advanceWidth +=
mTextRun->GetAdvanceWidth(offset, clusterLength, &aProvider);
maxLength -= clusterLength;
offset += clusterLength;
if (advanceWidth >= maxAdvance) {
break;
}
}
nscoord* snappedStartEdge = rtl ? aSnappedRightEdge : aSnappedLeftEdge;
*snappedStartEdge = NSToCoordFloor(advanceWidth);
*aStartOffset = offset;
}
const nscoord endEdge = rtl ? aLeftEdge : aRightEdge;
if (endEdge > 0) {
const gfxFloat maxAdvance = gfxFloat(frameWidth - endEdge);
while (maxLength > 0) {
PRUint32 clusterLength =
GetClusterLength(mTextRun, offset, maxLength, rtl);
gfxFloat nextAdvance = advanceWidth +
mTextRun->GetAdvanceWidth(offset, clusterLength, &aProvider);
if (nextAdvance > maxAdvance) {
break;
}
// This cluster fits, include it.
advanceWidth = nextAdvance;
maxLength -= clusterLength;
offset += clusterLength;
}
maxLength = offset - *aStartOffset;
nscoord* snappedEndEdge = rtl ? aSnappedLeftEdge : aSnappedRightEdge;
*snappedEndEdge = NSToCoordFloor(gfxFloat(frameWidth) - advanceWidth);
}
*aMaxLength = maxLength;
return maxLength != 0;
}
void
nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect)
const nsRect& aDirtyRect,
const nsCharClipDisplayItem& aItem)
{
// Don't pass in aRenderingContext here, because we need a *reference*
// context and aRenderingContext might have some transform in it
@@ -5147,23 +5226,28 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
if (!mTextRun)
return;
nsTextPaintStyle textPaintStyle(this);
PropertyProvider provider(this, iter);
// Trim trailing whitespace
provider.InitializeForDisplay(PR_TRUE);
gfxContext* ctx = aRenderingContext->ThebesContext();
const PRBool rtl = mTextRun->IsRightToLeft();
const nscoord frameWidth = GetSize().width;
gfxPoint framePt(aPt.x, aPt.y);
gfxPoint textBaselinePt(
mTextRun->IsRightToLeft() ? gfxFloat(aPt.x + GetSize().width) : framePt.x,
GetSnappedBaselineY(ctx, aPt.y));
gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
aDirtyRect.width, aDirtyRect.height);
gfxFloat advanceWidth;
gfxRGBA foregroundColor = gfxRGBA(textPaintStyle.GetTextColor());
gfxPoint textBaselinePt(rtl ? gfxFloat(aPt.x + frameWidth) : framePt.x,
GetSnappedBaselineY(ctx, aPt.y));
PRUint32 startOffset = provider.GetStart().GetSkippedOffset();
PRUint32 maxLength = ComputeTransformedLength(provider);
nscoord snappedLeftEdge, snappedRightEdge;
if (!MeasureCharClippedText(ctx, provider, aItem.mLeftEdge, aItem.mRightEdge,
&startOffset, &maxLength, &snappedLeftEdge, &snappedRightEdge)) {
return;
}
textBaselinePt.x += rtl ? -snappedRightEdge : snappedLeftEdge;
nsCharClipDisplayItem::ClipEdges clipEdges(aItem, snappedLeftEdge,
snappedRightEdge);
nsTextPaintStyle textPaintStyle(this);
nscolor foregroundColor = textPaintStyle.GetTextColor();
// Paint the text shadow before doing any foreground stuff
const nsStyleText* textStyle = GetStyleText();
@@ -5171,29 +5255,29 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
// Text shadow happens with the last value being painted at the back,
// ie. it is painted first.
for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
PaintOneShadow(provider.GetStart().GetSkippedOffset(),
ComputeTransformedLength(provider),
PaintOneShadow(startOffset, maxLength,
textStyle->mTextShadow->ShadowAt(i - 1), &provider,
aDirtyRect, framePt, textBaselinePt, ctx,
textPaintStyle.GetTextColor());
foregroundColor, clipEdges, snappedLeftEdge);
}
}
gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
aDirtyRect.width, aDirtyRect.height);
// Fork off to the (slower) paint-with-selection path if necessary.
if (nsLayoutUtils::GetNonGeneratedAncestor(this)->GetStateBits() & NS_FRAME_SELECTED_CONTENT) {
if (PaintTextWithSelection(ctx, framePt, textBaselinePt,
dirtyRect, provider, textPaintStyle))
dirtyRect, provider, textPaintStyle, clipEdges))
return;
}
ctx->SetColor(foregroundColor);
ctx->SetColor(gfxRGBA(foregroundColor));
DrawText(ctx, textBaselinePt, provider.GetStart().GetSkippedOffset(),
ComputeTransformedLength(provider), &dirtyRect,
&provider, advanceWidth,
(GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
gfxFloat advanceWidth;
DrawText(ctx, textBaselinePt, startOffset, maxLength, &dirtyRect, &provider,
advanceWidth, (GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
PaintTextDecorations(ctx, dirtyRect, framePt, textBaselinePt,
textPaintStyle, provider);
textPaintStyle, provider, clipEdges);
}
void