mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Bug 312156 - Implement CSS3 text-overflow. r=roc
This commit is contained in:
parent
8506a31912
commit
e757474795
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_*/
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -109,6 +109,7 @@ CPPSRCS = \
|
||||
nsSubDocumentFrame.cpp \
|
||||
nsTextFrameThebes.cpp \
|
||||
nsTextFrameUtils.cpp \
|
||||
TextOverflow.cpp \
|
||||
nsTextRunTransformations.cpp \
|
||||
nsViewportFrame.cpp \
|
||||
$(NULL)
|
||||
|
634
layout/generic/TextOverflow.cpp
Normal file
634
layout/generic/TextOverflow.cpp
Normal 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
|
228
layout/generic/TextOverflow.h
Normal file
228
layout/generic/TextOverflow.h
Normal 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_) */
|
@ -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);
|
||||
|
@ -372,6 +372,7 @@ protected:
|
||||
gfxFloat aOffset,
|
||||
gfxFloat aAscent,
|
||||
gfxFloat aSize,
|
||||
const nsCharClipDisplayItem::ClipEdges& aClipEdges,
|
||||
const PRUint8 aDecoration);
|
||||
|
||||
virtual void AdjustForTextIndent(const nsLineBox* aLine,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user