Bug 727125 - Update overflow areas for text frames on text-decoration change to ensure text-decoration on hover is visible. r=dbaron

This commit is contained in:
Susanna Bowen 2014-06-17 10:32:00 -04:00
parent 5161839fdc
commit 4451470f2b
8 changed files with 89 additions and 28 deletions

View File

@ -513,6 +513,22 @@ RestyleManager::StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
return;
}
void
RestyleManager::AddSubtreeToOverflowTracker(nsIFrame* aFrame)
{
mOverflowChangedTracker.AddFrame(
aFrame,
OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED);
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* child = childFrames.get();
AddSubtreeToOverflowTracker(child);
}
}
}
NS_DECLARE_FRAME_PROPERTY(ChangeListProperty, nullptr)
/**
@ -716,6 +732,7 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
StyleChangeReflow(frame, hint);
didReflowThisFrame = true;
}
if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
nsChangeHint_ChildrenOnlyTransform)) {
@ -733,7 +750,11 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
"nsChangeHint_UpdateOverflow should be passed too");
if (!didReflowThisFrame &&
(hint & (nsChangeHint_UpdateOverflow |
nsChangeHint_UpdatePostTransformOverflow))) {
nsChangeHint_UpdatePostTransformOverflow |
nsChangeHint_UpdateSubtreeOverflow))) {
if (hint & nsChangeHint_UpdateSubtreeOverflow) {
AddSubtreeToOverflowTracker(frame);
}
OverflowChangedTracker::ChangeKind changeKind;
if (hint & nsChangeHint_ChildrenOnlyTransform) {
// The overflow areas of the child frames need to be updated:
@ -769,7 +790,8 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
// If we have both nsChangeHint_UpdateOverflow and
// nsChangeHint_UpdatePostTransformOverflow, CHILDREN_AND_PARENT_CHANGED
// is selected as it is stronger.
if (hint & nsChangeHint_UpdateOverflow) {
if (hint & (nsChangeHint_UpdateOverflow |
nsChangeHint_UpdateSubtreeOverflow)) {
changeKind = OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED;
} else {
changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;

View File

@ -245,6 +245,9 @@ private:
void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
// Recursively add all the given frame and all children to the tracker.
void AddSubtreeToOverflowTracker(nsIFrame* aFrame);
// Returns true if this function managed to successfully move a frame, and
// false if it could not process the position change, and a reflow should
// be performed instead.

View File

@ -72,23 +72,28 @@ enum nsChangeHint {
nsChangeHint_ReconstructFrame = 0x400,
/**
* The frame's overflow area has changed, either through a change in its
* transform or a change in its position. Does not update any descendant
* The frame's overflow area has changed. Does not update any descendant
* frames.
*/
nsChangeHint_UpdateOverflow = 0x800,
/**
* The overflow area of the frame and all of its descendants has changed. This
* can happen through a text-decoration change.
*/
nsChangeHint_UpdateSubtreeOverflow = 0x1000,
/**
* The frame's overflow area has changed, through a change in its transform.
* Does not update any descendant frames.
*/
nsChangeHint_UpdatePostTransformOverflow = 0x1000,
nsChangeHint_UpdatePostTransformOverflow = 0x2000,
/**
* The children-only transform of an SVG frame changed, requiring the
* overflow rects of the frame's immediate children to be updated.
*/
nsChangeHint_ChildrenOnlyTransform = 0x2000,
nsChangeHint_ChildrenOnlyTransform = 0x4000,
/**
* The frame's offsets have changed, while its dimensions might have
@ -100,7 +105,7 @@ enum nsChangeHint {
* nsChangeHint_UpdateOverflow in order to get the overflow areas of
* the ancestors updated as well.
*/
nsChangeHint_RecomputePosition = 0x4000,
nsChangeHint_RecomputePosition = 0x8000,
/**
* Behaves like ReconstructFrame, but only if the frame has descendants
@ -108,7 +113,7 @@ enum nsChangeHint {
* has changed whether the frame is a container for fixed-pos or abs-pos
* elements, but reframing is otherwise not needed.
*/
nsChangeHint_AddOrRemoveTransform = 0x8000,
nsChangeHint_AddOrRemoveTransform = 0x10000,
/**
* This change hint has *no* change handling behavior. However, it
@ -116,13 +121,13 @@ enum nsChangeHint {
* changes, and it's inherited by a child, that might require a reflow
* due to the border-width change on the child.
*/
nsChangeHint_BorderStyleNoneChange = 0x10000,
nsChangeHint_BorderStyleNoneChange = 0x20000,
/**
* SVG textPath needs to be recomputed because the path has changed.
* This means that the glyph positions of the text need to be recomputed.
*/
nsChangeHint_UpdateTextPath = 0x20000
nsChangeHint_UpdateTextPath = 0x40000
// IMPORTANT NOTE: When adding new hints, consider whether you need to
// add them to NS_HintsNotHandledForDescendantsIn() below.

View File

@ -4803,7 +4803,7 @@ GetInflationForTextDecorations(nsIFrame* aFrame, nscoord aInflationMinFontSize)
void
nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aBlockReflowState,
nsIFrame* aBlock,
PropertyProvider& aProvider,
nsRect* aVisualOverflowRect,
bool aIncludeTextDecorations)
@ -4816,8 +4816,8 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
if (IsFloatingFirstLetterChild()) {
// The underline/overline drawable area must be contained in the overflow
// rect when this is in floating first letter frame at *both* modes.
nsIFrame* firstLetterFrame = aBlockReflowState.frame;
uint8_t decorationStyle = firstLetterFrame->StyleContext()->
// In this case, aBlock is the ::first-letter frame.
uint8_t decorationStyle = aBlock->StyleContext()->
StyleTextReset()->GetDecorationStyle();
// If the style is none, let's include decoration line rect as solid style
// since changing the style from none to solid/dotted/dashed doesn't cause
@ -4861,7 +4861,7 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
GetTextDecorations(aPresContext, eResolvedColors, textDecs);
if (textDecs.HasDecorationLines()) {
nscoord inflationMinFontSize =
nsLayoutUtils::InflationMinFontSizeFor(aBlockReflowState.frame);
nsLayoutUtils::InflationMinFontSizeFor(aBlock);
const nscoord width = GetSize().width;
const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(),
@ -8046,7 +8046,7 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
// When we have text decorations, we don't need to compute their overflow now
// because we're guaranteed to do it later
// (see nsLineLayout::RelativePositionFrames)
UnionAdditionalOverflow(presContext, *aLineLayout.LineContainerRS(),
UnionAdditionalOverflow(presContext, aLineLayout.LineContainerRS()->frame,
provider, &aMetrics.VisualOverflow(), false);
/////////////////////////////////////////////////////////////////////
@ -8288,7 +8288,7 @@ nsTextFrame::RecomputeOverflow(const nsHTMLReflowState& aBlockReflowState)
&provider);
nsRect &vis = result.VisualOverflow();
vis.UnionRect(vis, RoundOut(textMetrics.mBoundingBox) + nsPoint(0, mAscent));
UnionAdditionalOverflow(PresContext(), aBlockReflowState, provider,
UnionAdditionalOverflow(PresContext(), aBlockReflowState.frame, provider,
&vis, true);
return result;
}
@ -8579,3 +8579,36 @@ nsTextFrame::HasAnyNoncollapsedCharacters()
int32_t skippedOffsetEnd = iter.ConvertOriginalToSkipped(offsetEnd);
return skippedOffset != skippedOffsetEnd;
}
bool
nsTextFrame::UpdateOverflow()
{
nsRect rect(nsPoint(0, 0), GetSize());
nsOverflowAreas overflowAreas(rect, rect);
if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
return false;
}
gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
if (!mTextRun) {
return false;
}
PropertyProvider provider(this, iter, nsTextFrame::eInflated);
provider.InitializeForDisplay(true);
nsIFrame*decorationsBlock;
if (IsFloatingFirstLetterChild()) {
decorationsBlock = GetParent();
} else {
for (nsIFrame* f = this; f; f = f->GetParent()) {
nsBlockFrame* fBlock = nsLayoutUtils::GetAsBlock(f);
if (fBlock) {
decorationsBlock = fBlock;
break;
}
}
}
UnionAdditionalOverflow(PresContext(), decorationsBlock, provider,
&overflowAreas.VisualOverflow(), true);
return FinishAndStoreOverflow(overflowAreas, GetSize());
}

View File

@ -519,6 +519,8 @@ public:
bool IsFloatingFirstLetterChild() const;
virtual bool UpdateOverflow() MOZ_OVERRIDE;
protected:
virtual ~nsTextFrame();
@ -552,7 +554,7 @@ protected:
SelectionDetails* GetSelectionDetails();
void UnionAdditionalOverflow(nsPresContext* aPresContext,
const nsHTMLReflowState& aBlockReflowState,
nsIFrame* aBlock,
PropertyProvider& aProvider,
nsRect* aVisualOverflowRect,
bool aIncludeTextDecorations);

View File

@ -6,7 +6,7 @@ skip-if(B2G) == complex-decoration-style-standards.html complex-decoration-style
== decoration-style-standards.html decoration-style-standards-ref.html
fuzzy-if(B2G,255,1) == dynamic-underline-vertical-align-quirks-1.html dynamic-underline-vertical-align-quirks-1-ref.html
fuzzy-if(B2G,255,1) == dynamic-underline-vertical-align-standards-1.html dynamic-underline-vertical-align-standards-1-ref.html
fails == dynamic-underline-vertical-align-quirks-2.html dynamic-underline-vertical-align-quirks-2-ref.html
fuzzy-if(B2G,255,1) == dynamic-underline-vertical-align-quirks-2.html dynamic-underline-vertical-align-quirks-2-ref.html
fuzzy-if(B2G,255,1) == dynamic-underline-vertical-align-standards-2.html dynamic-underline-vertical-align-standards-2-ref.html
== line-through-style-block-solid-quirks.html line-through-style-block-quirks-ref.html
!= line-through-style-block-dotted-quirks.html line-through-style-block-quirks-ref.html

View File

@ -2992,17 +2992,11 @@ nsChangeHint nsStyleTextReset::CalcDifference(const nsStyleTextReset& aOther) co
uint8_t otherLineStyle = aOther.GetDecorationStyle();
if (mTextDecorationLine != aOther.mTextDecorationLine ||
lineStyle != otherLineStyle) {
// Reflow for decoration line style changes only to or from double or
// wave because that may cause overflow area changes
if (lineStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE ||
lineStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY ||
otherLineStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE ||
otherLineStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) {
return NS_STYLE_HINT_REFLOW;
}
// Repaint for other style decoration lines because they must be in
// default overflow rect
return NS_STYLE_HINT_VISUAL;
nsChangeHint hint = NS_STYLE_HINT_VISUAL;
NS_UpdateHint(hint, nsChangeHint_UpdateSubtreeOverflow);
return hint;
}
// Repaint for decoration color changes

View File

@ -1480,7 +1480,9 @@ struct nsStyleTextReset {
nsChangeHint CalcDifference(const nsStyleTextReset& aOther) const;
static nsChangeHint MaxDifference() {
return NS_STYLE_HINT_REFLOW;
return nsChangeHint(
NS_STYLE_HINT_REFLOW |
nsChangeHint_UpdateSubtreeOverflow);
}
static nsChangeHint MaxDifferenceNeverInherited() {
// CalcDifference never returns nsChangeHint_NeedReflow or