Bug 421353 Moving the mouse over text hyperlinks which become underlined spikes cpu usage r=roc+stuart, sr=roc, a1.9=beltzner

This commit is contained in:
masayuki%d-toybox.com 2008-03-29 21:25:16 +00:00
Родитель 091ed296f1
Коммит 8210b6e92d
11 изменённых файлов: 155 добавлений и 99 удалений

Просмотреть файл

@ -138,17 +138,36 @@ nsThebesFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize)
NS_IMETHODIMP
nsThebesFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize)
{
aOffset = ROUND_TO_TWIPS(GetMetrics().underlineOffset);
aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset());
aSize = ROUND_TO_TWIPS(GetMetrics().underlineSize);
return NS_OK;
}
// GetHeight/GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
// text-decoration lines drawable area. See bug 421353.
// BE CAREFUL for rounding each values. The logic MUST be same as
// nsCSSRendering::GetTextDecorationRectInternal's.
static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics,
gfxFontGroup* aFontGroup)
{
gfxFloat offset = NS_floor(-aFontGroup->GetUnderlineOffset() + 0.5);
gfxFloat size = NS_round(aMetrics.underlineSize);
gfxFloat minDescent = NS_floor(offset + size + 0.5);
return PR_MAX(minDescent, aMetrics.maxDescent);
}
static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics)
{
return NS_floor(aMetrics.maxAscent + 0.5);
}
NS_IMETHODIMP
nsThebesFontMetrics::GetHeight(nscoord &aHeight)
{
aHeight = CEIL_TO_TWIPS(GetMetrics().maxAscent) +
CEIL_TO_TWIPS(GetMetrics().maxDescent);
aHeight = CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
return NS_OK;
}
@ -190,22 +209,22 @@ nsThebesFontMetrics::GetEmDescent(nscoord &aDescent)
NS_IMETHODIMP
nsThebesFontMetrics::GetMaxHeight(nscoord &aHeight)
{
aHeight = CEIL_TO_TWIPS(GetMetrics().maxAscent) +
CEIL_TO_TWIPS(GetMetrics().maxDescent);
aHeight = CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
return NS_OK;
}
NS_IMETHODIMP
nsThebesFontMetrics::GetMaxAscent(nscoord &aAscent)
{
aAscent = CEIL_TO_TWIPS(GetMetrics().maxAscent);
aAscent = CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics()));
return NS_OK;
}
NS_IMETHODIMP
nsThebesFontMetrics::GetMaxDescent(nscoord &aDescent)
{
aDescent = CEIL_TO_TWIPS(GetMetrics().maxDescent);
aDescent = CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
return NS_OK;
}

Просмотреть файл

@ -1469,7 +1469,7 @@ public:
// The value should be lower value of first font's metrics and the bad font's metrics.
// Otherwise, this returns from first font's metrics.
gfxFloat GetUnderlineOffset() {
if (mUnderlineOffset == 0)
if (mStyle.size != 0 && mUnderlineOffset == 0)
mUnderlineOffset = GetFontAt(0)->GetMetrics().underlineOffset;
return mUnderlineOffset;
}

Просмотреть файл

@ -470,13 +470,15 @@ gfxAtsuiFontGroup::gfxAtsuiFontGroup(const nsAString& families,
mPageLang = gfxPlatform::GetFontPrefLangFor(mStyle.langGroup.get());
for (PRUint32 i = 0; i < mFonts.Length(); ++i) {
gfxAtsuiFont* font = static_cast<gfxAtsuiFont*>(mFonts[i].get());
if (font->GetFontEntry()->FamilyEntry()->IsBadUnderlineFontFamily()) {
gfxFloat first = mFonts[0]->GetMetrics().underlineOffset;
gfxFloat bad = font->GetMetrics().underlineOffset;
mUnderlineOffset = PR_MIN(first, bad);
break;
if (!mStyle.systemFont) {
for (PRUint32 i = 0; i < mFonts.Length(); ++i) {
gfxAtsuiFont* font = static_cast<gfxAtsuiFont*>(mFonts[i].get());
if (font->GetFontEntry()->FamilyEntry()->IsBadUnderlineFontFamily()) {
gfxFloat first = mFonts[0]->GetMetrics().underlineOffset;
gfxFloat bad = font->GetMetrics().underlineOffset;
mUnderlineOffset = PR_MIN(first, bad);
break;
}
}
}
}

Просмотреть файл

@ -549,6 +549,13 @@ gfxFont::SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID, PRBool aNeed
void
gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, PRBool aIsBadUnderlineFont)
{
// Even if this font size is zero, this font is created with non-zero size.
// However, for layout and others, we should return the metrics of zero size font.
if (mStyle.size == 0) {
memset(aMetrics, 0, sizeof(gfxFont::Metrics));
return;
}
// MS (P)Gothic and MS (P)Mincho are not having suitable values in their super script offset.
// If the values are not suitable, we should use x-height instead of them.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=353632
@ -579,10 +586,6 @@ gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, PRBool aIsBadUnderlineFont)
aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset, -2.0);
// Next, we put the underline to bottom of below of the descent space.
// Note that the underline might overlap to next line when the line height is 1em.
// However, in CJK text, such case is very rare, so, we don't need to worry about such case.
// Becasue most CJK glyphs use top of the em square. Therefore, for readability, CJK text needs
// larger line gap than Western text, generally.
if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset, -aMetrics->emDescent);
} else {

Просмотреть файл

@ -747,12 +747,14 @@ gfxWindowsFontGroup::gfxWindowsFontGroup(const nsAString& aFamilies, const gfxFo
mFonts.AppendElements(mFontEntries.Length());
for (PRUint32 i = 0; i < mFontEntries.Length(); ++i) {
if (mFontEntries[i]->IsBadUnderlineFont()) {
gfxFloat first = GetFontAt(0)->GetMetrics().underlineOffset;
gfxFloat bad = GetFontAt(i)->GetMetrics().underlineOffset;
mUnderlineOffset = PR_MIN(first, bad);
break;
if (!mStyle.systemFont) {
for (PRUint32 i = 0; i < mFontEntries.Length(); ++i) {
if (mFontEntries[i]->IsBadUnderlineFont()) {
gfxFloat first = GetFontAt(0)->GetMetrics().underlineOffset;
gfxFloat bad = GetFontAt(i)->GetMetrics().underlineOffset;
mUnderlineOffset = PR_MIN(first, bad);
break;
}
}
}
}

Просмотреть файл

@ -3984,16 +3984,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// might have moved frames around!
nsRect combinedArea;
aLineLayout.RelativePositionFrames(combinedArea); // XXXldb This returned width as -15, 2001-06-12, Bugzilla
if (aState.mPresContext->CompatibilityMode() != eCompatibility_NavQuirks) {
PRUint8 decorations;
nscolor underColor, overColor, strikeColor;
GetTextDecorations(aState.mPresContext, PR_TRUE, decorations,
underColor, overColor, strikeColor);
if (decorations) {
nsLineLayout::CombineTextDecorations(aState.mPresContext, decorations,
this, combinedArea);
}
}
aLine->SetCombinedArea(combinedArea);
if (addedBullet) {
aLineLayout.RemoveBulletFrame(mBullet);

Просмотреть файл

@ -2606,8 +2606,6 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea)
combinedAreaResult.height = mFinalLineHeight;
}
PRBool isStandardsMode =
mPresContext->CompatibilityMode() != eCompatibility_NavQuirks;
for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
nsIFrame* frame = pfd->mFrame;
nsPoint origin = frame->GetPosition();
@ -2636,15 +2634,6 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea)
// <b>x</b> and <b>y</b> which were computed above.
nsRect r;
if (pfd->mSpan) {
if (isStandardsMode) {
// Combine the text decoration area for inline elements of standards
// mode
PRUint8 decorations = frame->GetStyleTextReset()->mTextDecoration;
if (decorations) {
nsLineLayout::CombineTextDecorations(mPresContext, decorations,
frame, pfd->mSpan->mFrame->mCombinedArea);
}
}
// Compute a new combined area for the child span before
// aggregating it into our combined area.
RelativePositionFrames(pfd->mSpan, r);
@ -2708,7 +2697,7 @@ nsLineLayout::CombineTextDecorations(nsPresContext* aPresContext,
gfxFontGroup* fontGroup = tfm->GetThebesFontGroup();
gfxFont* firstFont = fontGroup->GetFontAt(0);
if (!firstFont)
return; // OOM
return; // OOM
const gfxFont::Metrics& metrics = firstFont->GetMetrics();
gfxFloat ascent = aAscentOverride == 0 ? metrics.maxAscent :

Просмотреть файл

@ -379,7 +379,6 @@ protected:
PRBool isBidiSystem);
void UnionTextDecorationOverflow(nsPresContext* aPresContext,
const gfxTextRun::Metrics& aTextMetrics,
nsRect* aOverflowRect);
struct TextDecorations {
@ -409,6 +408,9 @@ protected:
}
};
TextDecorations GetTextDecorations(nsPresContext* aPresContext);
PRBool HasSelectionOverflowingDecorations(nsPresContext* aPresContext,
float* aRatio = nsnull);
};
#endif

Просмотреть файл

@ -142,10 +142,13 @@
// This bit is set on frames that trimmed trailing whitespace characters when
// calculating their width during reflow.
#define TEXT_TRIMMED_TRAILING_WHITESPACE 0x01000000
// Set this bit if the textframe has overflow area for IME/spellcheck underline.
#define TEXT_SELECTION_UNDERLINE_OVERFLOWED 0x04000000
#define TEXT_REFLOW_FLAGS \
(TEXT_FIRST_LETTER|TEXT_START_OF_LINE|TEXT_END_OF_LINE|TEXT_HYPHEN_BREAK| \
TEXT_TRIMMED_TRAILING_WHITESPACE|TEXT_HAS_NONCOLLAPSED_CHARACTERS)
TEXT_TRIMMED_TRAILING_WHITESPACE|TEXT_HAS_NONCOLLAPSED_CHARACTERS| \
TEXT_SELECTION_UNDERLINE_OVERFLOWED)
// Cache bits for IsEmpty().
// Set this bit if the textframe is known to be only collapsible whitespace.
@ -1260,8 +1263,12 @@ GetSpacingFlags(const nsStyleCoord& aStyleCoord)
}
static gfxFontGroup*
GetFontGroupForFrame(nsIFrame* aFrame)
GetFontGroupForFrame(nsIFrame* aFrame,
nsIFontMetrics** aOutFontMetrics = nsnull)
{
if (aOutFontMetrics)
*aOutFontMetrics = nsnull;
nsCOMPtr<nsIFontMetrics> metrics;
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(metrics));
@ -1269,6 +1276,10 @@ GetFontGroupForFrame(nsIFrame* aFrame)
return nsnull;
nsIFontMetrics* metricsRaw = metrics;
if (aOutFontMetrics) {
*aOutFontMetrics = metricsRaw;
NS_ADDREF(*aOutFontMetrics);
}
nsIThebesFontMetrics* fm = static_cast<nsIThebesFontMetrics*>(metricsRaw);
// XXX this is a bit bogus, we're releasing 'metrics' so the returned font-group
// might actually be torn down, although because of the way the device context
@ -1326,7 +1337,7 @@ GetHyphenTextRun(gfxTextRun* aTextRun, gfxContext* aContext, nsTextFrame* aTextF
}
static gfxFont::Metrics
GetFontMetrics(gfxFontGroup* aFontGroup)
GetFirstFontMetrics(gfxFontGroup* aFontGroup)
{
if (!aFontGroup)
return gfxFont::Metrics();
@ -1980,7 +1991,8 @@ public:
const gfxSkipCharsIterator& aStart, PRInt32 aLength,
nsIFrame* aLineContainer,
nscoord aOffsetFromBlockOriginForTabs)
: mTextRun(aTextRun), mFontGroup(nsnull), mTextStyle(aTextStyle), mFrag(aFrag),
: mTextRun(aTextRun), mFontGroup(nsnull),
mTextStyle(aTextStyle), mFrag(aFrag),
mLineContainer(aLineContainer),
mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
mTabWidths(nsnull), mLength(aLength),
@ -2054,21 +2066,31 @@ public:
const nsTextFragment* GetFragment() { return mFrag; }
gfxFontGroup* GetFontGroup() {
if (!mFontGroup) {
mFontGroup = GetFontGroupForFrame(mFrame);
}
if (!mFontGroup)
InitFontGroupAndFontMetrics();
return mFontGroup;
}
nsIFontMetrics* GetFontMetrics() {
if (!mFontMetrics)
InitFontGroupAndFontMetrics();
return mFontMetrics;
}
gfxFloat* GetTabWidths(PRUint32 aTransformedStart, PRUint32 aTransformedLength);
const gfxSkipCharsIterator& GetEndHint() { return mTempIterator; }
protected:
void SetupJustificationSpacing();
void InitFontGroupAndFontMetrics() {
mFontGroup = GetFontGroupForFrame(mFrame, getter_AddRefs(mFontMetrics));
}
gfxTextRun* mTextRun;
gfxFontGroup* mFontGroup;
nsCOMPtr<nsIFontMetrics> mFontMetrics;
const nsStyleText* mTextStyle;
const nsTextFragment* mFrag;
nsIFrame* mLineContainer;
@ -2299,7 +2321,8 @@ PropertyProvider::GetTabWidths(PRUint32 aStart, PRUint32 aLength)
// Round the space width when converting to appunits the same way
// textruns do
gfxFloat spaceWidthAppUnits =
NS_roundf(GetFontMetrics(GetFontGroupForFrame(mLineContainer)).spaceWidth*
NS_roundf(GetFirstFontMetrics(
GetFontGroupForFrame(mLineContainer)).spaceWidth *
mTextRun->GetAppUnitsPerDevUnit());
gfxFloat tabWidth = 8*spaceWidthAppUnits;
for (PRUint32 i = tabsEnd; i < aStart + aLength; ++i) {
@ -3645,26 +3668,19 @@ nsTextFrame::GetTextDecorations(nsPresContext* aPresContext)
}
void
nsTextFrame::UnionTextDecorationOverflow(
nsPresContext* aPresContext,
const gfxTextRun::Metrics& aTextMetrics,
nsRect* aOverflowRect)
nsTextFrame::UnionTextDecorationOverflow(nsPresContext* aPresContext,
nsRect* aOverflowRect)
{
NS_ASSERTION(mTextRun, "mTextRun is null");
nsRect rect;
TextDecorations decorations = GetTextDecorations(aPresContext);
float ratio = 1.0f;
// Note that we need to add underline area when this frame has selection for
// spellchecking and IME.
if (mState & NS_FRAME_SELECTED_CONTENT) {
nsILookAndFeel* look = aPresContext->LookAndFeel();
look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize,
ratio);
decorations.mDecorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE;
}
nsLineLayout::CombineTextDecorations(aPresContext, decorations.mDecorations,
this, *aOverflowRect, NSToCoordRound(aTextMetrics.mAscent),
ratio);
// When this frame is not selected, the text-decoration area must be in
// frame bounds.
float ratio;
if (!HasSelectionOverflowingDecorations(aPresContext, &ratio))
return;
nsLineLayout::CombineTextDecorations(aPresContext,
NS_STYLE_TEXT_DECORATION_UNDERLINE,
this, *aOverflowRect, mAscent, ratio);
AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
}
void
@ -4318,6 +4334,26 @@ nsTextFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint) {
return offsets;
}
PRBool
nsTextFrame::HasSelectionOverflowingDecorations(nsPresContext* aPresContext,
float* aRatio)
{
float ratio;
nsILookAndFeel* look = aPresContext->LookAndFeel();
look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize, ratio);
if (aRatio)
*aRatio = ratio;
if (ratio <= 1.0f)
return PR_FALSE;
for (SelectionDetails *sd = GetSelectionDetails(); sd; sd = sd->mNext) {
if (sd->mStart != sd->mEnd &&
sd->mType & SelectionTypesWithDecorations)
return PR_TRUE;
}
return PR_FALSE;
}
//null range means the whole thing
NS_IMETHODIMP
nsTextFrame::SetSelected(nsPresContext* aPresContext,
@ -4387,7 +4423,6 @@ nsTextFrame::SetSelected(nsPresContext* aPresContext,
found = PR_TRUE;
}
nsFrameState oldState = mState;
if ( aSelected )
AddStateBits(NS_FRAME_SELECTED_CONTENT);
else
@ -4400,16 +4435,14 @@ nsTextFrame::SetSelected(nsPresContext* aPresContext,
}
}
if (found) {
// If the selection state is changed, we need to reflow to recompute
// the overflow area for underline of spellchecking or IME. However, if
// the non-selected text already has underline, we don't need to reflow.
// And also when the IME underline is thicker than normal underline.
nsILookAndFeel* look = aPresContext->LookAndFeel();
float ratio;
look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize,
ratio);
if (oldState != mState &&
(ratio > 1.0 || !GetTextDecorations(aPresContext).HasUnderline())) {
// If the selection state is changed in this content, we need to reflow
// to recompute the overflow area for underline of spellchecking or IME if
// their underline is thicker than normal decoration line.
PRBool didHaveSelectionUnderline =
!!(mState & TEXT_SELECTION_UNDERLINE_OVERFLOWED);
PRBool willHaveSelectionUnderline =
aSelected && HasSelectionOverflowingDecorations(PresContext());
if (didHaveSelectionUnderline != willHaveSelectionUnderline) {
PresContext()->PresShell()->FrameNeedsReflow(this,
nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
@ -5533,12 +5566,21 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
textMetrics.mAscent = PR_MAX(0, -textMetrics.mBoundingBox.Y());
textMetrics.mDescent = PR_MAX(0, textMetrics.mBoundingBox.YMost());
}
// Setup metrics for caller
// Disallow negative widths
aMetrics.width = NSToCoordCeil(PR_MAX(0, textMetrics.mAdvanceWidth));
aMetrics.ascent = NSToCoordCeil(textMetrics.mAscent);
aMetrics.height = aMetrics.ascent + NSToCoordCeil(textMetrics.mDescent);
// nsIFontMetrics::GetMaxAscent and nsIFontMetrics::GetMaxDescent return
// the decoration lines drawable size when the font-size is not zero.
nscoord minAscent, minDescent;
nsIFontMetrics* fm = provider.GetFontMetrics();
fm->GetMaxAscent(minAscent);
fm->GetMaxDescent(minDescent);
aMetrics.ascent = PR_MAX(NSToCoordCeil(textMetrics.mAscent), minAscent);
nscoord descent = PR_MAX(NSToCoordCeil(textMetrics.mDescent), minDescent);
aMetrics.height = aMetrics.ascent + descent;
NS_ASSERTION(aMetrics.ascent >= 0, "Negative ascent???");
NS_ASSERTION(aMetrics.height - aMetrics.ascent >= 0, "Negative descent???");
@ -5546,12 +5588,11 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
// Handle text that runs outside its normal bounds.
nsRect boundingBox =
ConvertGfxRectOutward(textMetrics.mBoundingBox + gfxPoint(0, textMetrics.mAscent));
ConvertGfxRectOutward(textMetrics.mBoundingBox + gfxPoint(0, mAscent));
aMetrics.mOverflowArea.UnionRect(boundingBox,
nsRect(0, 0, aMetrics.width, aMetrics.height));
UnionTextDecorationOverflow(aPresContext, textMetrics,
&aMetrics.mOverflowArea);
UnionTextDecorationOverflow(aPresContext, &aMetrics.mOverflowArea);
/////////////////////////////////////////////////////////////////////
// Clean up, update state
@ -5772,11 +5813,11 @@ nsTextFrame::RecomputeOverflowRect()
&provider);
nsRect boundingBox =
ConvertGfxRectOutward(textMetrics.mBoundingBox + gfxPoint(0, textMetrics.mAscent));
ConvertGfxRectOutward(textMetrics.mBoundingBox + gfxPoint(0, mAscent));
boundingBox.UnionRect(boundingBox,
nsRect(nsPoint(0,0), GetSize()));
UnionTextDecorationOverflow(PresContext(), textMetrics, &boundingBox);
UnionTextDecorationOverflow(PresContext(), &boundingBox);
return boundingBox;
}

Просмотреть файл

@ -1 +1 @@
fails-if(MOZ_WIDGET_TOOLKIT=="windows") == z-index-1.html z-index-1-ref.html # bug 388744
== z-index-1.html z-index-1-ref.html

Просмотреть файл

@ -1517,10 +1517,18 @@ nsStyleTextReset::~nsStyleTextReset(void) { }
nsChangeHint nsStyleTextReset::CalcDifference(const nsStyleTextReset& aOther) const
{
if (mVerticalAlign == aOther.mVerticalAlign &&
mUnicodeBidi == aOther.mUnicodeBidi &&
mTextDecoration == aOther.mTextDecoration)
if (mVerticalAlign == aOther.mVerticalAlign
&& mUnicodeBidi == aOther.mUnicodeBidi) {
if (mTextDecoration != aOther.mTextDecoration) {
// Reflow for blink changes, repaint for others
return
(mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) ==
(aOther.mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) ?
NS_STYLE_HINT_VISUAL : NS_STYLE_HINT_REFLOW;
}
return NS_STYLE_HINT_NONE;
}
return NS_STYLE_HINT_REFLOW;
}