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

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

View File

@ -632,10 +632,30 @@ nsresult nsCaret::PrimeTimer()
return NS_OK;
}
void nsCaret::InvalidateTextOverflowBlock()
{
// If the nearest block has a potential 'text-overflow' marker then
// invalidate it.
if (mLastContent) {
nsIFrame* caretFrame = mLastContent->GetPrimaryFrame();
if (caretFrame) {
nsIFrame* block = nsLayoutUtils::GetAsBlock(caretFrame) ? caretFrame :
nsLayoutUtils::FindNearestBlockAncestor(caretFrame);
if (block) {
const nsStyleTextReset* style = block->GetStyleTextReset();
if (style->mTextOverflow.mType != NS_STYLE_TEXT_OVERFLOW_CLIP) {
block->InvalidateOverflowRect();
}
}
}
}
}
//-----------------------------------------------------------------------------
void nsCaret::StartBlinking()
{
InvalidateTextOverflowBlock();
if (mReadOnly) {
// Make sure the one draw command we use for a readonly caret isn't
// done until the selection is set
@ -659,6 +679,8 @@ void nsCaret::StartBlinking()
//-----------------------------------------------------------------------------
void nsCaret::StopBlinking()
{
InvalidateTextOverflowBlock();
if (mDrawn) // erase the caret if necessary
DrawCaret(PR_TRUE);

View File

@ -204,6 +204,10 @@ protected:
void StartBlinking();
void StopBlinking();
// If the nearest block has a potential 'text-overflow' marker then
// invalidate it.
void InvalidateTextOverflowBlock();
PRBool DrawAtPositionWithHint(nsIDOMNode* aNode,
PRInt32 aOffset,

View File

@ -66,6 +66,7 @@ enum Type {
TYPE_COMBOBOX_FOCUS,
TYPE_EVENT_RECEIVER,
TYPE_FIELDSET_BORDER_BACKGROUND,
TYPE_FORCEPAINTONSCROLL,
TYPE_FRAMESET_BORDER,
TYPE_FRAMESET_BLANK,
TYPE_HEADER_FOOTER,
@ -95,6 +96,7 @@ enum Type {
TYPE_TABLE_BORDER_BACKGROUND,
TYPE_TEXT,
TYPE_TEXT_DECORATION,
TYPE_TEXT_OVERFLOW,
TYPE_TEXT_SHADOW,
TYPE_TRANSFORM,
TYPE_VIDEO,

View File

@ -2745,3 +2745,20 @@ PRBool nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayIt
other->mBounds + other->mEffectsFrame->GetOffsetTo(mEffectsFrame));
return PR_TRUE;
}
nsDisplayForcePaintOnScroll::nsDisplayForcePaintOnScroll(
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
: nsDisplayItem(aBuilder, aFrame) {
MOZ_COUNT_CTOR(nsDisplayForcePaintOnScroll);
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplayForcePaintOnScroll::~nsDisplayForcePaintOnScroll() {
MOZ_COUNT_DTOR(nsDisplayForcePaintOnScroll);
}
#endif
PRBool nsDisplayForcePaintOnScroll::IsVaryingRelativeToMovingFrame(
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
return PR_TRUE;
}

View File

@ -187,7 +187,7 @@ public:
* @return the root of the display list's frame (sub)tree, whose origin
* establishes the coordinate system for the display list
*/
nsIFrame* ReferenceFrame() { return mReferenceFrame; }
nsIFrame* ReferenceFrame() const { return mReferenceFrame; }
/**
* @return a point pt such that adding pt to a coordinate relative to aFrame
* makes it relative to ReferenceFrame(), i.e., returns
@ -195,7 +195,7 @@ public:
* the appunits of aFrame. It may be optimized to be faster than
* aFrame->GetOffsetToCrossDoc(ReferenceFrame()) (but currently isn't).
*/
nsPoint ToReferenceFrame(const nsIFrame* aFrame) {
nsPoint ToReferenceFrame(const nsIFrame* aFrame) const {
return aFrame->GetOffsetToCrossDoc(ReferenceFrame());
}
/**
@ -636,7 +636,7 @@ public:
* that wrap item lists, this could return nsnull because there is no single
* underlying frame; for leaf items it will never return nsnull.
*/
inline nsIFrame* GetUnderlyingFrame() { return mFrame; }
inline nsIFrame* GetUnderlyingFrame() const { return mFrame; }
/**
* The default bounds is the frame border rect.
* @return a rectangle relative to aBuilder->ReferenceFrame() that
@ -813,7 +813,7 @@ public:
/**
* Returns the result of aBuilder->ToReferenceFrame(GetUnderlyingFrame())
*/
const nsPoint& ToReferenceFrame() {
const nsPoint& ToReferenceFrame() const {
NS_ASSERTION(mFrame, "No frame?");
return mToReferenceFrame;
}
@ -2187,4 +2187,69 @@ private:
nsDisplayWrapList mStoredList;
};
/**
* This class adds basic support for limiting the rendering to the part inside
* the specified edges. It's a base class for the display item classes that
* does the actual work. The two members, mLeftEdge and mRightEdge, are
* relative to the edges of the frame's scrollable overflow rectangle and is
* the amount to suppress on each side.
*
* Setting none, both or only one edge is allowed.
* The values must be non-negative.
* The default value for both edges is zero, which means everything is painted.
*/
class nsCharClipDisplayItem : public nsDisplayItem {
public:
nsCharClipDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
: nsDisplayItem(aBuilder, aFrame), mLeftEdge(0), mRightEdge(0) {}
struct ClipEdges {
ClipEdges(const nsDisplayItem& aItem,
nscoord aLeftEdge, nscoord aRightEdge) {
nsRect r = aItem.GetUnderlyingFrame()->GetScrollableOverflowRect() +
aItem.ToReferenceFrame();
mX = aLeftEdge > 0 ? r.x + aLeftEdge : nscoord_MIN;
mXMost = aRightEdge > 0 ? NS_MAX(r.XMost() - aRightEdge, mX) : nscoord_MAX;
}
void Intersect(nscoord* aX, nscoord* aWidth) const {
nscoord xmost1 = *aX + *aWidth;
*aX = NS_MAX(*aX, mX);
*aWidth = NS_MAX(NS_MIN(xmost1, mXMost) - *aX, 0);
}
nscoord mX;
nscoord mXMost;
};
ClipEdges Edges() const { return ClipEdges(*this, mLeftEdge, mRightEdge); }
static nsCharClipDisplayItem* CheckCast(nsDisplayItem* aItem) {
nsDisplayItem::Type t = aItem->GetType();
return (t == nsDisplayItem::TYPE_TEXT ||
t == nsDisplayItem::TYPE_TEXT_DECORATION ||
t == nsDisplayItem::TYPE_TEXT_SHADOW)
? static_cast<nsCharClipDisplayItem*>(aItem) : nsnull;
}
nscoord mLeftEdge; // length from the left side
nscoord mRightEdge; // length from the right side
};
/**
* This is a dummy item that reports true for IsVaryingRelativeToMovingFrame.
* It forces the bounds of its frame to be repainted every time it is scrolled.
* It is transparent to events and does not paint anything.
*/
class nsDisplayForcePaintOnScroll : public nsDisplayItem
{
public:
nsDisplayForcePaintOnScroll(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayForcePaintOnScroll();
#endif
NS_DISPLAY_DECL_NAME("ForcePaintOnScroll", TYPE_FORCEPAINTONSCROLL)
virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame);
};
#endif /*NSDISPLAYLIST_H_*/

View File

@ -22,7 +22,7 @@
*
* Contributor(s):
* L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
* Mats Palmgren <mats.palmgren@bredband.net>
* Mats Palmgren <matspal@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -50,6 +50,7 @@
#include "nsIAtom.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSColorUtils.h"
#include "nsIView.h"
#include "nsPlaceholderFrame.h"
#include "nsIScrollableFrame.h"
@ -2790,6 +2791,49 @@ nsLayoutUtils::PrefWidthFromInline(nsIFrame* aFrame,
return data.prevLines;
}
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();
}
nscolor
nsLayoutUtils::GetTextColor(nsIFrame* aFrame)
{
nscolor color = aFrame->GetVisitedDependentColor(eCSSProperty_color);
if (ShouldDarkenColors(aFrame->PresContext())) {
color = DarkenColor(color);
}
return color;
}
void
nsLayoutUtils::DrawString(const nsIFrame* aFrame,
nsRenderingContext* aContext,

View File

@ -22,7 +22,7 @@
* Contributor(s):
* Boris Zbarsky <bzbarsky@mit.edu> (original author)
* L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
* Mats Palmgren <mats.palmgren@bredband.net>
* Mats Palmgren <matspal@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -931,6 +931,9 @@ public:
static nscoord MinWidthFromInline(nsIFrame* aFrame,
nsRenderingContext* aRenderingContext);
// Get a suitable foreground color for painting text for the frame.
static nscolor GetTextColor(nsIFrame* aFrame);
static void DrawString(const nsIFrame* aFrame,
nsRenderingContext* aContext,
const PRUnichar* aString,

View File

@ -109,6 +109,7 @@ CPPSRCS = \
nsSubDocumentFrame.cpp \
nsTextFrameThebes.cpp \
nsTextFrameUtils.cpp \
TextOverflow.cpp \
nsTextRunTransformations.cpp \
nsViewportFrame.cpp \
$(NULL)

View File

@ -0,0 +1,634 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is an implementation of CSS3 text-overflow.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mats Palmgren <matspal@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "TextOverflow.h"
// Please maintain alphabetical order below
#include "nsBlockFrame.h"
#include "nsCaret.h"
#include "nsContentUtils.h"
#include "nsIScrollableFrame.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include "nsRect.h"
#include "nsRenderingContext.h"
#include "nsTextFrame.h"
namespace mozilla {
namespace css {
static const PRUnichar kEllipsisChar[] = { 0x2026, 0x0 };
static const PRUnichar kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
// Return an ellipsis if the font supports it,
// otherwise use three ASCII periods as fallback.
static nsDependentString GetEllipsis(nsIFrame* aFrame)
{
// Check if the first font supports Unicode ellipsis.
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
gfxFont* firstFont = fontGroup->GetFontAt(0);
return firstFont && firstFont->HasCharacter(kEllipsisChar[0])
? nsDependentString(kEllipsisChar,
NS_ARRAY_LENGTH(kEllipsisChar) - 1)
: nsDependentString(kASCIIPeriodsChar,
NS_ARRAY_LENGTH(kASCIIPeriodsChar) - 1);
}
static nsIFrame*
GetSelfOrNearestBlock(nsIFrame* aFrame)
{
return nsLayoutUtils::GetAsBlock(aFrame) ? aFrame :
nsLayoutUtils::FindNearestBlockAncestor(aFrame);
}
// Return true if the frame is an atomic inline-level element.
// It's not supposed to be called for block frames since we never
// process block descendants for text-overflow.
static bool
IsAtomicElement(nsIFrame* aFrame, const nsIAtom* aFrameType)
{
NS_PRECONDITION(!aFrame->GetStyleDisplay()->IsBlockOutside(),
"unexpected block frame");
if (aFrame->IsFrameOfType(nsIFrame::eReplaced)) {
if (aFrameType != nsGkAtoms::textFrame &&
aFrameType != nsGkAtoms::brFrame) {
return true;
}
}
return aFrame->GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_INLINE;
}
static bool
IsFullyClipped(nsTextFrame* aFrame, nscoord aLeft, nscoord aRight,
nscoord* aSnappedLeft, nscoord* aSnappedRight)
{
*aSnappedLeft = aLeft;
*aSnappedRight = aRight;
if (aLeft <= 0 && aRight <= 0) {
return false;
}
nsRefPtr<nsRenderingContext> rc =
aFrame->PresContext()->PresShell()->GetReferenceRenderingContext();
return rc &&
!aFrame->MeasureCharClippedText(rc->ThebesContext(), aLeft, aRight,
aSnappedLeft, aSnappedRight);
}
static bool
IsHorizontalOverflowVisible(nsIFrame* aFrame)
{
NS_PRECONDITION(nsLayoutUtils::GetAsBlock(aFrame) != nsnull,
"expected a block frame");
nsIFrame* f = aFrame;
while (f && f->GetStyleContext()->GetPseudo()) {
f = f->GetParent();
}
return !f || f->GetStyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE;
}
static nsDisplayItem*
ClipMarker(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame,
nsDisplayItem* aMarker,
const nsRect& aContentArea,
nsRect* aMarkerRect)
{
nsDisplayItem* item = aMarker;
nscoord rightOverflow = aMarkerRect->XMost() - aContentArea.XMost();
if (rightOverflow > 0) {
// Marker overflows on the right side (content width < marker width).
aMarkerRect->width -= rightOverflow;
item = new (aBuilder)
nsDisplayClip(aBuilder, aFrame, aMarker, *aMarkerRect);
} else {
nscoord leftOverflow = aContentArea.x - aMarkerRect->x;
if (leftOverflow > 0) {
// Marker overflows on the left side
aMarkerRect->width -= leftOverflow;
aMarkerRect->x += leftOverflow;
item = new (aBuilder)
nsDisplayClip(aBuilder, aFrame, aMarker, *aMarkerRect);
}
}
return item;
}
static void
InflateLeft(nsRect* aRect, bool aInfinity, nscoord aDelta)
{
nscoord xmost = aRect->XMost();
if (aInfinity) {
aRect->x = nscoord_MIN;
} else {
aRect->x -= aDelta;
}
aRect->width = NS_MAX(xmost - aRect->x, 0);
}
static void
InflateRight(nsRect* aRect, bool aInfinity, nscoord aDelta)
{
if (aInfinity) {
aRect->width = nscoord_MAX;
} else {
aRect->width = NS_MAX(aRect->width + aDelta, 0);
}
}
static bool
IsFrameDescendantOfAny(nsIFrame* aChild,
const TextOverflow::FrameHashtable& aSetOfFrames,
nsIFrame* aCommonAncestor)
{
for (nsIFrame* f = aChild; f != aCommonAncestor;
f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
if (aSetOfFrames.GetEntry(f)) {
return true;
}
}
return false;
}
class nsDisplayTextOverflowMarker : public nsDisplayItem
{
public:
nsDisplayTextOverflowMarker(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
const nsRect& aRect, nscoord aAscent,
const nsString& aString)
: nsDisplayItem(aBuilder, aFrame), mRect(aRect), mString(aString),
mAscent(aAscent) {
MOZ_COUNT_CTOR(nsDisplayTextOverflowMarker);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayTextOverflowMarker() {
MOZ_COUNT_DTOR(nsDisplayTextOverflowMarker);
}
#endif
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
return mRect;
}
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx);
NS_DISPLAY_DECL_NAME("TextOverflow", TYPE_TEXT_OVERFLOW)
private:
nsRect mRect; // in reference frame coordinates
const nsString mString; // the marker text
nscoord mAscent; // baseline for the marker text in mRect
};
void
nsDisplayTextOverflowMarker::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
nsStyleContext* sc = mFrame->GetStyleContext();
nsLayoutUtils::SetFontFromStyle(aCtx, sc);
aCtx->SetColor(nsLayoutUtils::GetTextColor(mFrame));
nsPoint baselinePt = mRect.TopLeft();
baselinePt.y += mAscent;
nsLayoutUtils::DrawString(mFrame, aCtx, mString.get(), mString.Length(),
baselinePt);
}
/* static */ TextOverflow*
TextOverflow::WillProcessLines(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists,
nsIFrame* aBlockFrame)
{
if (!CanHaveTextOverflow(aBuilder, aBlockFrame)) {
return nsnull;
}
nsAutoPtr<TextOverflow> textOverflow(new TextOverflow);
textOverflow->mBuilder = aBuilder;
textOverflow->mBlock = aBlockFrame;
textOverflow->mMarkerList = aLists.PositionedDescendants();
textOverflow->mContentArea = aBlockFrame->GetContentRectRelativeToSelf();
nsIScrollableFrame* scroll =
nsLayoutUtils::GetScrollableFrameFor(aBlockFrame);
textOverflow->mCanHaveHorizontalScrollbar = false;
if (scroll) {
textOverflow->mCanHaveHorizontalScrollbar =
scroll->GetScrollbarStyles().mHorizontal != NS_STYLE_OVERFLOW_HIDDEN;
textOverflow->mContentArea.MoveBy(scroll->GetScrollPosition());
}
textOverflow->mBlockIsRTL =
aBlockFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
const nsStyleTextReset* style = aBlockFrame->GetStyleTextReset();
textOverflow->mLeft.Init(style->mTextOverflow);
textOverflow->mRight.Init(style->mTextOverflow);
// The left/right marker string is setup in ExamineLineFrames when a line
// has overflow on that side.
return textOverflow.forget();
}
void
TextOverflow::DidProcessLines()
{
nsIScrollableFrame* scroll = nsLayoutUtils::GetScrollableFrameFor(mBlock);
if (scroll) {
// Create a dummy item covering the entire area, it doesn't paint
// but reports true for IsVaryingRelativeToMovingFrame().
nsIFrame* scrollFrame = do_QueryFrame(scroll);
nsDisplayItem* marker = new (mBuilder)
nsDisplayForcePaintOnScroll(mBuilder, scrollFrame);
if (marker) {
mMarkerList->AppendNewToBottom(marker);
mBlock->PresContext()->SetHasFixedBackgroundFrame();
}
}
}
void
TextOverflow::ExamineFrameSubtree(nsIFrame* aFrame,
const nsRect& aContentArea,
const nsRect& aInsideMarkersArea,
FrameHashtable* aFramesToHide,
AlignmentEdges* aAlignmentEdges)
{
const nsIAtom* frameType = aFrame->GetType();
if (frameType == nsGkAtoms::brFrame) {
return;
}
const bool isAtomic = IsAtomicElement(aFrame, frameType);
if (aFrame->GetStyleVisibility()->IsVisible()) {
nsRect childRect = aFrame->GetScrollableOverflowRect() +
aFrame->GetOffsetTo(mBlock);
bool overflowLeft = childRect.x < aContentArea.x;
bool overflowRight = childRect.XMost() > aContentArea.XMost();
if (overflowLeft) {
mLeft.mHasOverflow = true;
}
if (overflowRight) {
mRight.mHasOverflow = true;
}
if (isAtomic && (overflowLeft || overflowRight)) {
aFramesToHide->PutEntry(aFrame);
} else if (isAtomic || frameType == nsGkAtoms::textFrame) {
AnalyzeMarkerEdges(aFrame, frameType, aInsideMarkersArea,
aFramesToHide, aAlignmentEdges);
}
}
if (isAtomic) {
return;
}
nsIFrame* child = aFrame->GetFirstChild(nsnull);
while (child) {
ExamineFrameSubtree(child, aContentArea, aInsideMarkersArea,
aFramesToHide, aAlignmentEdges);
child = child->GetNextSibling();
}
}
void
TextOverflow::AnalyzeMarkerEdges(nsIFrame* aFrame,
const nsIAtom* aFrameType,
const nsRect& aInsideMarkersArea,
FrameHashtable* aFramesToHide,
AlignmentEdges* aAlignmentEdges)
{
nsRect borderRect(aFrame->GetOffsetTo(mBlock), aFrame->GetSize());
nscoord leftOverlap =
NS_MAX(aInsideMarkersArea.x - borderRect.x, 0);
nscoord rightOverlap =
NS_MAX(borderRect.XMost() - aInsideMarkersArea.XMost(), 0);
bool insideLeftEdge = aInsideMarkersArea.x <= borderRect.XMost();
bool insideRightEdge = borderRect.x <= aInsideMarkersArea.XMost();
if ((leftOverlap > 0 && insideLeftEdge) ||
(rightOverlap > 0 && insideRightEdge)) {
if (aFrameType == nsGkAtoms::textFrame &&
aInsideMarkersArea.x < aInsideMarkersArea.XMost()) {
// a clipped text frame and there is some room between the markers
nscoord snappedLeft, snappedRight;
bool isFullyClipped =
IsFullyClipped(static_cast<nsTextFrame*>(aFrame),
leftOverlap, rightOverlap, &snappedLeft, &snappedRight);
if (!isFullyClipped) {
nsRect snappedRect = borderRect;
if (leftOverlap > 0) {
snappedRect.x += snappedLeft;
snappedRect.width -= snappedLeft;
}
if (rightOverlap > 0) {
snappedRect.width -= snappedRight;
}
aAlignmentEdges->Accumulate(snappedRect);
}
} else if (IsAtomicElement(aFrame, aFrameType)) {
aFramesToHide->PutEntry(aFrame);
}
} else if (!insideLeftEdge || !insideRightEdge) {
// frame is outside
if (IsAtomicElement(aFrame, aFrameType)) {
aFramesToHide->PutEntry(aFrame);
}
} else {
// frame is inside
aAlignmentEdges->Accumulate(borderRect);
}
}
void
TextOverflow::ExamineLineFrames(nsLineBox* aLine,
FrameHashtable* aFramesToHide,
AlignmentEdges* aAlignmentEdges)
{
// Scrolling to the end position can leave some text still overflowing due to
// pixel snapping behaviour in our scrolling code so we move the edges 1px
// outward to avoid triggering a text-overflow marker for such overflow.
nsRect contentArea = mContentArea;
const nscoord scrollAdjust = mCanHaveHorizontalScrollbar ?
mBlock->PresContext()->AppUnitsPerDevPixel() : 0;
InflateLeft(&contentArea,
mLeft.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP,
scrollAdjust);
InflateRight(&contentArea,
mRight.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP,
scrollAdjust);
nsRect lineRect = aLine->GetScrollableOverflowArea();
const bool leftOverflow = lineRect.x < contentArea.x;
const bool rightOverflow = lineRect.XMost() > contentArea.XMost();
if (!leftOverflow && !rightOverflow) {
// The line does not overflow - no need to traverse the frame tree.
return;
}
PRUint32 pass = 0;
bool guessLeft =
mLeft.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP && leftOverflow;
bool guessRight =
mRight.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP && rightOverflow;
do {
// Setup marker strings as needed.
if (guessLeft || guessRight) {
mLeft.SetupString(mBlock);
mRight.mMarkerString = mLeft.mMarkerString;
mRight.mWidth = mLeft.mWidth;
mRight.mInitialized = mLeft.mInitialized;
}
// If there is insufficient space for both markers then keep the one on the
// end side per the block's 'direction'.
nscoord rightMarkerWidth = mRight.mWidth;
nscoord leftMarkerWidth = mLeft.mWidth;
if (leftOverflow && rightOverflow &&
leftMarkerWidth + rightMarkerWidth > contentArea.width) {
if (mBlockIsRTL) {
rightMarkerWidth = 0;
} else {
leftMarkerWidth = 0;
}
}
// Calculate the area between the potential markers aligned at the
// block's edge.
nsRect insideMarkersArea = mContentArea;
InflateLeft(&insideMarkersArea, !guessLeft, -leftMarkerWidth);
InflateRight(&insideMarkersArea, !guessRight, -rightMarkerWidth);
// Analyze the frames on aLine for the overflow situation at the content
// edges and at the edges of the area between the markers.
PRInt32 n = aLine->GetChildCount();
nsIFrame* child = aLine->mFirstChild;
for (; n-- > 0; child = child->GetNextSibling()) {
ExamineFrameSubtree(child, contentArea, insideMarkersArea,
aFramesToHide, aAlignmentEdges);
}
if (guessLeft == mLeft.IsNeeded() && guessRight == mRight.IsNeeded()) {
break;
} else {
guessLeft = mLeft.IsNeeded();
guessRight = mRight.IsNeeded();
mLeft.Reset();
mRight.Reset();
aFramesToHide->Clear();
}
NS_ASSERTION(pass == 0, "2nd pass should never guess wrong");
} while (++pass != 2);
}
void
TextOverflow::ProcessLine(const nsDisplayListSet& aLists,
nsLineBox* aLine)
{
NS_ASSERTION(mLeft.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
mRight.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP,
"TextOverflow with 'clip' for both sides");
mLeft.Reset();
mRight.Reset();
FrameHashtable framesToHide;
if (!framesToHide.Init(100)) {
return;
}
AlignmentEdges alignmentEdges;
ExamineLineFrames(aLine, &framesToHide, &alignmentEdges);
bool needLeft = mLeft.IsNeeded();
bool needRight = mRight.IsNeeded();
if (!needLeft && !needRight) {
return;
}
NS_ASSERTION(mLeft.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
!needLeft, "left marker for 'clip'");
NS_ASSERTION(mRight.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
!needRight, "right marker for 'clip'");
// If there is insufficient space for both markers then keep the one on the
// end side per the block's 'direction'.
if (needLeft && needRight &&
mLeft.mWidth + mRight.mWidth > mContentArea.width) {
if (mBlockIsRTL) {
needRight = false;
} else {
needLeft = false;
}
}
nsRect insideMarkersArea = mContentArea;
if (needLeft) {
InflateLeft(&insideMarkersArea, false, -mLeft.mWidth);
}
if (needRight) {
InflateRight(&insideMarkersArea, false, -mRight.mWidth);
}
if (!mCanHaveHorizontalScrollbar && alignmentEdges.mAssigned) {
nsRect alignmentRect = nsRect(alignmentEdges.x, insideMarkersArea.y,
alignmentEdges.Width(), 1);
insideMarkersArea.IntersectRect(insideMarkersArea, alignmentRect);
}
// Clip and remove display items as needed at the final marker edges.
nsDisplayList* lists[] = { aLists.Content(), aLists.PositionedDescendants() };
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(lists); ++i) {
PruneDisplayListContents(lists[i], framesToHide, insideMarkersArea);
}
CreateMarkers(aLine, needLeft, needRight, insideMarkersArea);
}
void
TextOverflow::PruneDisplayListContents(nsDisplayList* aList,
const FrameHashtable& aFramesToHide,
const nsRect& aInsideMarkersArea)
{
nsDisplayList saved;
nsDisplayItem* item;
while ((item = aList->RemoveBottom())) {
nsIFrame* itemFrame = item->GetUnderlyingFrame();
if (itemFrame && IsFrameDescendantOfAny(itemFrame, aFramesToHide, mBlock)) {
item->~nsDisplayItem();
continue;
}
nsDisplayList* wrapper = item->GetList();
if (wrapper) {
if (!itemFrame || GetSelfOrNearestBlock(itemFrame) == mBlock) {
PruneDisplayListContents(wrapper, aFramesToHide, aInsideMarkersArea);
}
}
nsCharClipDisplayItem* charClip = itemFrame ?
nsCharClipDisplayItem::CheckCast(item) : nsnull;
if (charClip && GetSelfOrNearestBlock(itemFrame) == mBlock) {
nsRect rect = itemFrame->GetScrollableOverflowRect() +
itemFrame->GetOffsetTo(mBlock);
if (mLeft.IsNeeded() && rect.x < aInsideMarkersArea.x) {
charClip->mLeftEdge = aInsideMarkersArea.x - rect.x;
}
if (mRight.IsNeeded() && rect.XMost() > aInsideMarkersArea.XMost()) {
charClip->mRightEdge = rect.XMost() - aInsideMarkersArea.XMost();
}
}
saved.AppendToTop(item);
}
aList->AppendToTop(&saved);
}
/* static */ bool
TextOverflow::CanHaveTextOverflow(nsDisplayListBuilder* aBuilder,
nsIFrame* aBlockFrame)
{
const nsStyleTextReset* style = aBlockFrame->GetStyleTextReset();
// Nothing to do for text-overflow:clip or if 'overflow-x:visible'
// or if we're just building items for event processing.
if ((style->mTextOverflow.mType == NS_STYLE_TEXT_OVERFLOW_CLIP) ||
IsHorizontalOverflowVisible(aBlockFrame) ||
aBuilder->IsForEventDelivery()) {
return false;
}
// Inhibit the markers if a descendant content owns the caret.
nsRefPtr<nsCaret> caret = aBlockFrame->PresContext()->PresShell()->GetCaret();
PRBool visible = PR_FALSE;
if (caret && NS_SUCCEEDED(caret->GetCaretVisible(&visible)) && visible) {
nsCOMPtr<nsISelection> domSelection = caret->GetCaretDOMSelection();
if (domSelection) {
nsCOMPtr<nsIDOMNode> node;
domSelection->GetFocusNode(getter_AddRefs(node));
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
if (content && nsContentUtils::ContentIsDescendantOf(content,
aBlockFrame->GetContent())) {
return false;
}
}
}
return true;
}
void
TextOverflow::CreateMarkers(const nsLineBox* aLine,
bool aCreateLeft,
bool aCreateRight,
const nsRect& aInsideMarkersArea) const
{
if (aCreateLeft) {
nsRect markerRect = nsRect(aInsideMarkersArea.x - mLeft.mWidth,
aLine->mBounds.y,
mLeft.mWidth, aLine->mBounds.height);
markerRect += mBuilder->ToReferenceFrame(mBlock);
nsDisplayItem* marker = new (mBuilder)
nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
aLine->GetAscent(), mLeft.mMarkerString);
if (marker) {
marker = ClipMarker(mBuilder, mBlock, marker,
mContentArea + mBuilder->ToReferenceFrame(mBlock),
&markerRect);
}
mMarkerList->AppendNewToTop(marker);
}
if (aCreateRight) {
nsRect markerRect = nsRect(aInsideMarkersArea.XMost(),
aLine->mBounds.y,
mRight.mWidth, aLine->mBounds.height);
markerRect += mBuilder->ToReferenceFrame(mBlock);
nsDisplayItem* marker = new (mBuilder)
nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
aLine->GetAscent(), mRight.mMarkerString);
if (marker) {
marker = ClipMarker(mBuilder, mBlock, marker,
mContentArea + mBuilder->ToReferenceFrame(mBlock),
&markerRect);
}
mMarkerList->AppendNewToTop(marker);
}
}
void
TextOverflow::Marker::SetupString(nsIFrame* aFrame)
{
if (mInitialized) {
return;
}
nsRefPtr<nsRenderingContext> rc =
aFrame->PresContext()->PresShell()->GetReferenceRenderingContext();
nsLayoutUtils::SetFontFromStyle(rc, aFrame->GetStyleContext());
mMarkerString = mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS ?
GetEllipsis(aFrame) : mStyle->mString;
mWidth = nsLayoutUtils::GetStringWidth(aFrame, rc, mMarkerString.get(),
mMarkerString.Length());
mInitialized = true;
}
} // namespace css
} // namespace mozilla

View File

@ -0,0 +1,228 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is an implementation of CSS3 text-overflow.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mats Palmgren <matspal@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef TextOverflow_h_
#define TextOverflow_h_
#include "nsDisplayList.h"
#include "nsLineBox.h"
#include "nsStyleStruct.h"
#include "nsTHashtable.h"
namespace mozilla {
namespace css {
/**
* A class for rendering CSS3 text-overflow.
* Usage:
* 1. allocate an object using WillProcessLines
* 2. then call ProcessLine for each line you are building display lists for
* 3. finally call DidProcessLines
*/
class TextOverflow {
public:
/**
* Allocate an object for text-overflow processing.
* @return nsnull if no processing is necessary. The caller owns the object.
*/
static TextOverflow* WillProcessLines(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists,
nsIFrame* aBlockFrame);
/**
* Analyze the display lists for text overflow and what kind of item is at
* the content edges. Add display items for text-overflow markers as needed
* and remove or clip items that would overlap a marker.
*/
void ProcessLine(const nsDisplayListSet& aLists, nsLineBox* aLine);
/**
* Do final processing, currently just adds a dummy item for scroll frames
* to make IsVaryingRelativeToMovingFrame() true for the entire area.
*/
void DidProcessLines();
/**
* @return true if aBlockFrame needs analysis for text overflow.
*/
static bool CanHaveTextOverflow(nsDisplayListBuilder* aBuilder,
nsIFrame* aBlockFrame);
typedef nsTHashtable<nsPtrHashKey<nsIFrame> > FrameHashtable;
protected:
TextOverflow() {}
struct AlignmentEdges {
AlignmentEdges() : mAssigned(false) {}
void Accumulate(const nsRect& aRect) {
if (NS_LIKELY(mAssigned)) {
x = NS_MIN(x, aRect.X());
xmost = NS_MAX(xmost, aRect.XMost());
} else {
x = aRect.X();
xmost = aRect.XMost();
mAssigned = true;
}
}
nscoord Width() { return xmost - x; }
nscoord x;
nscoord xmost;
bool mAssigned;
};
/**
* Examines frames on the line to determine whether we should draw a left
* and/or right marker, and if so, which frames should be completely hidden
* and the bounds of what will be displayed between the markers.
* @param aLine the line we're processing
* @param aFramesToHide frames that should have their display items removed
* @param aAlignmentEdges the outermost edges of all text and atomic
* inline-level frames that are inside the area between the markers
*/
void ExamineLineFrames(nsLineBox* aLine,
FrameHashtable* aFramesToHide,
AlignmentEdges* aAlignmentEdges);
/**
* LineHasOverflowingText calls this to analyze edges, both the block's
* content edges and the hypothetical marker edges aligned at the block edges.
* @param aFrame the descendant frame of mBlock that we're analyzing
* @param aContentArea the block's content area
* @param aInsideMarkersArea the rectangle between the markers
* @param aFramesToHide frames that should have their display items removed
* @param aAlignmentEdges the outermost edges of all text and atomic
* inline-level frames that are inside the area between the markers
*/
void ExamineFrameSubtree(nsIFrame* aFrame,
const nsRect& aContentArea,
const nsRect& aInsideMarkersArea,
FrameHashtable* aFramesToHide,
AlignmentEdges* aAlignmentEdges);
/**
* ExamineFrameSubtree calls this to analyze a frame against the hypothetical
* marker edges (aInsideMarkersArea) for text frames and atomic inline-level
* elements. A text frame adds its extent inside aInsideMarkersArea where
* grapheme clusters are fully visible. An atomic adds its border box if
* it's fully inside aInsideMarkersArea, otherwise the frame is added to
* aFramesToHide.
* @param aFrame the descendant frame of mBlock that we're analyzing
* @param aFrameType aFrame's frame type
* @param aInsideMarkersArea the rectangle between the markers
* @param aFramesToHide frames that should have their display items removed
* @param aAlignmentEdges the outermost edges of all text and atomic
* inline-level frames that are inside the area between the markers
* inside aInsideMarkersArea
*/
void AnalyzeMarkerEdges(nsIFrame* aFrame,
const nsIAtom* aFrameType,
const nsRect& aInsideMarkersArea,
FrameHashtable* aFramesToHide,
AlignmentEdges* aAlignmentEdges);
/**
* Clip or remove items given the final marker edges. ("clip" here just means
* assigning mLeftEdge/mRightEdge for any nsCharClipDisplayItem that needs it,
* see nsDisplayList.h for a description of that item).
* @param aFramesToHide remove display items for these frames
* @param aInsideMarkersArea is the area inside the markers
*/
void PruneDisplayListContents(nsDisplayList* aList,
const FrameHashtable& aFramesToHide,
const nsRect& aInsideMarkersArea);
/**
* ProcessLine calls this to create display items for the markers and insert
* them into a display list for the block.
* @param aLine the line we're processing
* @param aCreateLeft if true, create a marker on the left side
* @param aCreateRight if true, create a marker on the right side
* @param aInsideMarkersArea is the area inside the markers
*/
void CreateMarkers(const nsLineBox* aLine,
bool aCreateLeft,
bool aCreateRight,
const nsRect& aInsideMarkersArea) const;
nsRect mContentArea;
nsDisplayListBuilder* mBuilder;
nsIFrame* mBlock;
nsDisplayList* mMarkerList;
bool mBlockIsRTL;
bool mCanHaveHorizontalScrollbar;
class Marker {
public:
void Init(const nsStyleTextOverflow& aStyle) {
mInitialized = false;
mWidth = 0;
mStyle = &aStyle;
}
/**
* Setup the marker string and calculate its size, if not done already.
*/
void SetupString(nsIFrame* aFrame);
bool IsNeeded() const {
return mHasOverflow;
}
void Reset() {
mHasOverflow = false;
}
// The intrinsic width of the marker string.
nscoord mWidth;
// The marker text.
nsString mMarkerString;
// The style for this side.
const nsStyleTextOverflow* mStyle;
// True if there is visible overflowing inline content on this side.
bool mHasOverflow;
// True if mMarkerString and mWidth have been setup from style.
bool mInitialized;
};
Marker mLeft; // the horizontal left marker
Marker mRight; // the horizontal right marker
};
} // namespace css
} // namespace mozilla
#endif /* !defined(TextOverflow_h_) */

View File

@ -89,6 +89,7 @@
#include "nsCSSRendering.h"
#include "FrameLayerBuilder.h"
#include "nsRenderingContext.h"
#include "TextOverflow.h"
#include "mozilla/Util.h" // for DebugOnly
#ifdef IBMBIDI
@ -107,6 +108,7 @@ static const PRUnichar kSquareCharacter = 0x25aa;
#define DISABLE_FLOAT_BREAKING_IN_COLUMNS
using namespace mozilla;
using namespace mozilla::css;
#ifdef DEBUG
#include "nsPrintfCString.h"
@ -6072,15 +6074,17 @@ nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection)
}
/* virtual */ void
nsBlockFrame::PaintTextDecorationLine(gfxContext* aCtx,
const nsPoint& aPt,
nsLineBox* aLine,
nscolor aColor,
PRUint8 aStyle,
gfxFloat aOffset,
gfxFloat aAscent,
gfxFloat aSize,
const PRUint8 aDecoration)
nsBlockFrame::PaintTextDecorationLine(
gfxContext* aCtx,
const nsPoint& aPt,
nsLineBox* aLine,
nscolor aColor,
PRUint8 aStyle,
gfxFloat aOffset,
gfxFloat aAscent,
gfxFloat aSize,
const nsCharClipDisplayItem::ClipEdges& aClipEdges,
const PRUint8 aDecoration)
{
NS_ASSERTION(!aLine->IsBlock(), "Why did we ask for decorations on a block?");
@ -6088,10 +6092,12 @@ nsBlockFrame::PaintTextDecorationLine(gfxContext* aCtx,
nscoord width = aLine->mBounds.width;
AdjustForTextIndent(aLine, start, width);
nscoord x = start + aPt.x;
aClipEdges.Intersect(&x, &width);
// Only paint if we have a positive width
if (width > 0) {
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(start + aPt.x),
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(x),
PresContext()->AppUnitsToGfxUnits(aLine->mBounds.y + aPt.y));
gfxSize size(PresContext()->AppUnitsToGfxUnits(width), aSize);
nsCSSRendering::PaintDecorationLine(
@ -6151,7 +6157,7 @@ static nsresult
DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
const nsRect& aDirtyRect, nsBlockFrame::line_iterator& aLine,
PRInt32 aDepth, PRInt32& aDrawnLines, const nsDisplayListSet& aLists,
nsBlockFrame* aFrame) {
nsBlockFrame* aFrame, TextOverflow* aTextOverflow) {
// If the line's combined area (which includes child frames that
// stick outside of the line's bounding box or our bounding box)
// intersects the dirty rect then paint the line.
@ -6169,24 +6175,27 @@ DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
// on all the frames on the line, but that might be expensive. So
// we approximate it by checking it on aFrame; if it's true for any
// frame in the line, it's also true for aFrame.
if (!intersect && !aBuilder->ShouldDescendIntoFrame(aFrame))
PRBool lineInline = aLine->IsInline();
PRBool lineMayHaveTextOverflow = aTextOverflow && lineInline;
if (!intersect && !aBuilder->ShouldDescendIntoFrame(aFrame) &&
!lineMayHaveTextOverflow)
return NS_OK;
nsDisplayListCollection collection;
nsresult rv;
nsDisplayList aboveTextDecorations;
PRBool lineInline = aLine->IsInline();
if (lineInline) {
// Display the text-decoration for the hypothetical anonymous inline box
// that wraps these inlines
rv = aFrame->DisplayTextDecorations(aBuilder, aLists.Content(),
rv = aFrame->DisplayTextDecorations(aBuilder, collection.Content(),
&aboveTextDecorations, aLine);
NS_ENSURE_SUCCESS(rv, rv);
}
// Block-level child backgrounds go on the blockBorderBackgrounds list ...
// Inline-level child backgrounds go on the regular child content list.
nsDisplayListSet childLists(aLists,
lineInline ? aLists.Content() : aLists.BlockBorderBackgrounds());
nsDisplayListSet childLists(collection,
lineInline ? collection.Content() : collection.BlockBorderBackgrounds());
nsIFrame* kid = aLine->mFirstChild;
PRInt32 n = aLine->GetChildCount();
while (--n >= 0) {
@ -6196,7 +6205,13 @@ DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
kid = kid->GetNextSibling();
}
aLists.Content()->AppendToTop(&aboveTextDecorations);
collection.Content()->AppendToTop(&aboveTextDecorations);
if (lineMayHaveTextOverflow) {
aTextOverflow->ProcessLine(collection, aLine.get());
}
collection.MoveTo(aLists);
return NS_OK;
}
@ -6238,6 +6253,10 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
// Prepare for text-overflow processing.
nsAutoPtr<TextOverflow> textOverflow(
TextOverflow::WillProcessLines(aBuilder, aLists, this));
// Don't use the line cursor if we might have a descendant placeholder ...
// it might skip lines that contain placeholders but don't themselves
// intersect with the dirty area.
@ -6262,7 +6281,7 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
break;
}
rv = DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
aLists, this);
aLists, this, textOverflow);
if (NS_FAILED(rv))
break;
}
@ -6277,7 +6296,7 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
++line) {
nsRect lineArea = line->GetVisualOverflowArea();
rv = DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
aLists, this);
aLists, this, textOverflow);
if (NS_FAILED(rv))
break;
if (!lineArea.IsEmpty()) {
@ -6296,6 +6315,11 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
}
}
// Finalize text-overflow processing.
if (textOverflow) {
textOverflow->DidProcessLines();
}
if (NS_SUCCEEDED(rv) && (nsnull != mBullet) && HaveOutsideBullet()) {
// Display outside bullets manually
rv = BuildDisplayListForChild(aBuilder, mBullet, aDirtyRect, aLists);

View File

@ -372,6 +372,7 @@ protected:
gfxFloat aOffset,
gfxFloat aAscent,
gfxFloat aSize,
const nsCharClipDisplayItem::ClipEdges& aClipEdges,
const PRUint8 aDecoration);
virtual void AdjustForTextIndent(const nsLineBox* aLine,

View File

@ -68,12 +68,12 @@
#include "nsDisplayList.h"
#include "nsCSSRendering.h"
class nsDisplayTextDecoration : public nsDisplayItem {
class nsDisplayTextDecoration : public nsCharClipDisplayItem {
public:
nsDisplayTextDecoration(nsDisplayListBuilder* aBuilder,
nsHTMLContainerFrame* aFrame, PRUint8 aDecoration,
nscolor aColor, PRUint8 aStyle, nsLineBox* aLine)
: nsDisplayItem(aBuilder, aFrame), mLine(aLine), mColor(aColor),
: nsCharClipDisplayItem(aBuilder, aFrame), mLine(aLine), mColor(aColor),
mDecoration(aDecoration), mStyle(aStyle) {
MOZ_COUNT_CTOR(nsDisplayTextDecoration);
}
@ -134,15 +134,15 @@ nsDisplayTextDecoration::Paint(nsDisplayListBuilder* aBuilder,
gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
mStyle, underlineOffset, ascent,
metrics.underlineSize, mDecoration);
metrics.underlineSize, Edges(), mDecoration);
} else if (mDecoration == NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) {
f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
mStyle, metrics.maxAscent, ascent,
metrics.underlineSize, mDecoration);
metrics.underlineSize, Edges(), mDecoration);
} else {
f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
mStyle, metrics.strikeoutOffset,
ascent, metrics.strikeoutSize, mDecoration);
mStyle, metrics.strikeoutOffset, ascent,
metrics.strikeoutSize, Edges(), mDecoration);
}
}
@ -152,14 +152,14 @@ nsDisplayTextDecoration::GetBounds(nsDisplayListBuilder* aBuilder)
return mFrame->GetVisualOverflowRect() + ToReferenceFrame();
}
class nsDisplayTextShadow : public nsDisplayItem {
class nsDisplayTextShadow : public nsCharClipDisplayItem {
public:
nsDisplayTextShadow(nsDisplayListBuilder* aBuilder,
nsHTMLContainerFrame* aFrame,
const PRUint8 aDecoration, PRUint8 aUnderlineStyle,
PRUint8 aOverlineStyle, PRUint8 aStrikeThroughStyle,
nsLineBox* aLine)
: nsDisplayItem(aBuilder, aFrame), mLine(aLine),
: nsCharClipDisplayItem(aBuilder, aFrame), mLine(aLine),
mDecorationFlags(aDecoration), mUnderlineStyle(aUnderlineStyle),
mOverlineStyle(aOverlineStyle), mStrikeThroughStyle(aStrikeThroughStyle) {
MOZ_COUNT_CTOR(nsDisplayTextShadow);
@ -300,22 +300,23 @@ nsDisplayTextShadow::Paint(nsDisplayListBuilder* aBuilder,
continue;
}
const nsCharClipDisplayItem::ClipEdges clipEdges = this->Edges();
if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) {
f->PaintTextDecorationLine(shadowCtx, pt, mLine, shadowColor,
mUnderlineStyle, underlineOffset, ascent,
metrics.underlineSize,
metrics.underlineSize, clipEdges,
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE);
}
if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) {
f->PaintTextDecorationLine(shadowCtx, pt, mLine, shadowColor,
mOverlineStyle, metrics.maxAscent, ascent,
metrics.underlineSize,
metrics.underlineSize, clipEdges,
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE);
}
if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
f->PaintTextDecorationLine(shadowCtx, pt, mLine, shadowColor,
mStrikeThroughStyle, metrics.strikeoutOffset,
ascent, metrics.strikeoutSize,
ascent, metrics.strikeoutSize, clipEdges,
NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH);
}
@ -440,6 +441,7 @@ nsHTMLContainerFrame::PaintTextDecorationLine(
gfxFloat aOffset,
gfxFloat aAscent,
gfxFloat aSize,
const nsCharClipDisplayItem::ClipEdges& aClipEdges,
const PRUint8 aDecoration)
{
NS_ASSERTION(!aLine, "Should not have passed a linebox to a non-block frame");
@ -450,8 +452,10 @@ nsHTMLContainerFrame::PaintTextDecorationLine(
bp.Side(side) = 0;
}
}
nscoord x = aPt.x + bp.left;
nscoord innerWidth = mRect.width - bp.left - bp.right;
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(bp.left + aPt.x),
aClipEdges.Intersect(&x, &innerWidth);
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(x),
PresContext()->AppUnitsToGfxUnits(bp.top + aPt.y));
gfxSize size(PresContext()->AppUnitsToGfxUnits(innerWidth), aSize);
nsCSSRendering::PaintDecorationLine(aCtx, aColor, pt, size, aAscent, aOffset,

View File

@ -41,6 +41,7 @@
#define nsHTMLContainerFrame_h___
#include "nsContainerFrame.h"
#include "nsDisplayList.h"
#include "gfxPoint.h"
class nsString;
@ -172,20 +173,23 @@ protected:
* i.e. negative offsets draws *below*
* the baseline.
* @param aSize the thickness of the line
* @param aClipEdges clip edges from the display item
* @param aDecoration which line will be painted i.e.,
* NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or
* NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or
* NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH.
*/
virtual void PaintTextDecorationLine(gfxContext* aCtx,
const nsPoint& aPt,
nsLineBox* aLine,
nscolor aColor,
PRUint8 aStyle,
gfxFloat aOffset,
gfxFloat aAscent,
gfxFloat aSize,
const PRUint8 aDecoration);
virtual void PaintTextDecorationLine(
gfxContext* aCtx,
const nsPoint& aPt,
nsLineBox* aLine,
nscolor aColor,
PRUint8 aStyle,
gfxFloat aOffset,
gfxFloat aAscent,
gfxFloat aSize,
const nsCharClipDisplayItem::ClipEdges& aClipEdges,
const PRUint8 aDecoration);
virtual void AdjustForTextIndent(const nsLineBox* aLine,
nscoord& start,

View File

@ -56,6 +56,7 @@
#include "gfxFont.h"
#include "gfxSkipChars.h"
#include "gfxContext.h"
#include "nsDisplayList.h"
class nsTextPaintStyle;
class PropertyProvider;
@ -272,16 +273,41 @@ public:
gfxFloat GetSnappedBaselineY(gfxContext* aContext, gfxFloat aY);
/**
* Calculate the horizontal bounds of the grapheme clusters that fit entirely
* inside the given left/right edges (which are positive lengths from the
* respective frame edge). If an input value is zero it is ignored and the
* result for that edge is zero. All out parameter values are undefined when
* the method returns false.
* @return true if at least one whole grapheme cluster fit between the edges
*/
bool MeasureCharClippedText(gfxContext* aCtx,
nscoord aLeftEdge, nscoord aRightEdge,
nscoord* aSnappedLeftEdge,
nscoord* aSnappedRightEdge);
/**
* Same as above; this method also the returns the corresponding text run
* offset and number of characters that fit. All out parameter values are
* undefined when the method returns false.
* @return true if at least one whole grapheme cluster fit between the edges
*/
bool MeasureCharClippedText(gfxContext* aCtx,
PropertyProvider& aProvider,
nscoord aLeftEdge, nscoord aRightEdge,
PRUint32* aStartOffset, PRUint32* aMaxLength,
nscoord* aSnappedLeftEdge,
nscoord* aSnappedRightEdge);
// primary frame paint method called from nsDisplayText
// The private DrawText() is what applies the text to a graphics context
void PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect);
const nsRect& aDirtyRect, const nsCharClipDisplayItem& aItem);
// helper: paint quirks-mode CSS text decorations
void PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
const gfxPoint& aFramePt,
const gfxPoint& aTextBaselinePt,
nsTextPaintStyle& aTextStyle,
PropertyProvider& aProvider,
const nsCharClipDisplayItem::ClipEdges& aClipEdges,
const nscolor* aOverrideColor = nsnull);
// helper: paint text frame when we're impacted by at least one selection.
// Return PR_FALSE if the text was not painted and we should continue with
@ -291,7 +317,8 @@ public:
const gfxPoint& aTextBaselinePt,
const gfxRect& aDirtyRect,
PropertyProvider& aProvider,
nsTextPaintStyle& aTextPaintStyle);
nsTextPaintStyle& aTextPaintStyle,
const nsCharClipDisplayItem::ClipEdges& aClipEdges);
// helper: paint text with foreground and background colors determined
// by selection(s). Also computes a mask of all selection types applying to
// our text, returned in aAllTypes.
@ -428,7 +455,9 @@ protected:
const gfxPoint& aFramePt,
const gfxPoint& aTextBaselinePt,
gfxContext* aCtx,
const nscolor& aForegroundColor);
const nscolor& aForegroundColor,
const nsCharClipDisplayItem::ClipEdges& aClipEdges,
nscoord aLeftSideOffset);
struct TextDecorations {
PRUint8 mDecorations;

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