Bug 727125 - Lazily compute LineBaselineOffset when needed so it is present after a dynamic change of 'text-decoration'. r=dbaron

This fixes the positioning of underlines set on a block or its ancestor
when drawn on children of a block that have a vertical-align !=
baseline.

The lazy computation is done all at once for all children of a block to
avoid O(N^2) searches for the line containing a frame.
This commit is contained in:
Susanna Bowen 2014-06-16 11:58:25 -07:00
parent 9e632a1ea1
commit 49c99c035b
11 changed files with 247 additions and 20 deletions

View File

@ -731,6 +731,16 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
printf("\n"); printf("\n");
#endif #endif
if (mCurrentSpan == mRootSpan) {
pfd->mFrame->Properties().Remove(nsIFrame::LineBaselineOffset());
} else {
#ifdef DEBUG
bool hasLineOffset;
pfd->mFrame->Properties().Get(nsIFrame::LineBaselineOffset(), &hasLineOffset);
NS_ASSERTION(!hasLineOffset, "LineBaselineOffset was set but was not expected");
#endif
}
mTextJustificationNumSpaces = 0; mTextJustificationNumSpaces = 0;
mTextJustificationNumLetters = 0; mTextJustificationNumLetters = 0;
@ -1452,22 +1462,6 @@ nsLineLayout::BlockDirAlignLine()
} }
PlaceStartEndFrames(psd, -mBStartEdge, lineBSize); PlaceStartEndFrames(psd, -mBStartEdge, lineBSize);
// If the frame being reflowed has text decorations, we simulate the
// propagation of those decorations to a line-level element by storing the
// offset in a frame property on any child frames that are aligned in the
// block direction somewhere other than the baseline. This property is then
// used by nsTextFrame::GetTextDecorations when the same conditions are met.
if (rootPFD.mFrame->StyleContext()->HasTextDecorationLines()) {
for (const PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
const nsIFrame *const f = pfd->mFrame;
if (f->VerticalAlignEnum() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
const nscoord offset = baselineBCoord - pfd->mBounds.BStart(lineWM);
f->Properties().Set(nsIFrame::LineBaselineOffset(),
NS_INT32_TO_PTR(offset));
}
}
}
// Fill in returned line-box and max-element-width data // Fill in returned line-box and max-element-width data
mLineBox->SetBounds(lineWM, mLineBox->SetBounds(lineWM,
psd->mIStart, mBStartEdge, psd->mIStart, mBStartEdge,

View File

@ -4620,6 +4620,40 @@ PaintSelectionBackground(gfxContext* aCtx, nsPresContext* aPresContext,
} }
} }
// Attempt to get the LineBaselineOffset property of aChildFrame
// If not set, calculate this value for all child frames of aBlockFrame
static nscoord
LazyGetLineBaselineOffset(nsIFrame* aChildFrame, nsBlockFrame* aBlockFrame)
{
bool offsetFound;
nscoord offset = NS_PTR_TO_INT32(
aChildFrame->Properties().Get(nsIFrame::LineBaselineOffset(), &offsetFound)
);
if (!offsetFound) {
for (nsBlockFrame::line_iterator line = aBlockFrame->begin_lines(),
line_end = aBlockFrame->end_lines();
line != line_end; line++) {
if (line->IsInline()) {
int32_t n = line->GetChildCount();
nscoord lineBaseline = line->BStart() + line->GetAscent();
for (nsIFrame* lineFrame = line->mFirstChild;
n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
offset = lineBaseline - lineFrame->GetNormalPosition().y;
lineFrame->Properties().Set(nsIFrame::LineBaselineOffset(),
NS_INT32_TO_PTR(offset));
}
}
}
return NS_PTR_TO_INT32(
aChildFrame->Properties().Get(nsIFrame::LineBaselineOffset(), &offsetFound)
);
} else {
return offset;
}
}
void void
nsTextFrame::GetTextDecorations( nsTextFrame::GetTextDecorations(
nsPresContext* aPresContext, nsPresContext* aPresContext,
@ -4663,7 +4697,8 @@ nsTextFrame::GetTextDecorations(
nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color); nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
} }
const bool firstBlock = !nearestBlockFound && nsLayoutUtils::GetAsBlock(f); nsBlockFrame* fBlock = nsLayoutUtils::GetAsBlock(f);
const bool firstBlock = !nearestBlockFound && fBlock;
// Not updating positions once we hit a parent block is equivalent to // Not updating positions once we hit a parent block is equivalent to
// the CSS 2.1 spec that blocks should propagate decorations down to their // the CSS 2.1 spec that blocks should propagate decorations down to their
@ -4674,13 +4709,15 @@ nsTextFrame::GetTextDecorations(
if (firstBlock) { if (firstBlock) {
// At this point, fChild can't be null since TextFrames can't be blocks // At this point, fChild can't be null since TextFrames can't be blocks
if (fChild->VerticalAlignEnum() != NS_STYLE_VERTICAL_ALIGN_BASELINE) { if (fChild->VerticalAlignEnum() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
// Since offset is the offset in the child's coordinate space, we have // Since offset is the offset in the child's coordinate space, we have
// to undo the accumulation to bring the transform out of the block's // to undo the accumulation to bring the transform out of the block's
// coordinate space // coordinate space
const nscoord lineBaselineOffset = LazyGetLineBaselineOffset(fChild,
fBlock);
baselineOffset = baselineOffset =
frameTopOffset - fChild->GetNormalPosition().y frameTopOffset - fChild->GetNormalPosition().y - lineBaselineOffset;
- NS_PTR_TO_INT32(
fChild->Properties().Get(nsIFrame::LineBaselineOffset()));
} }
} }
else if (!nearestBlockFound) { else if (!nearestBlockFound) {

View File

@ -0,0 +1,21 @@
<html>
<head>
<style>
.underline {
text-decoration: underline;
}
.align-bottom {
vertical-align: bottom;
}
.align-top {
vertical-align: top;
}
</style>
</head>
<body>
<p class="underline">
<span class="align-bottom">This</span> line has a bottom vertical align span. <br />
<span class="align-top">This</span> line has a top vertical align span.
</p>
</body>
</html>

View File

@ -0,0 +1,26 @@
<html class="reftest-wait">
<head>
<script type="text/javascript">
function addUnderline() {
var element = document.getElementById("dynamicUnderline");
element.style.textDecoration = "underline";
document.documentElement.removeAttribute("class");
}
document.addEventListener('MozReftestInvalidate', addUnderline, false);
</script>
<style>
.align-bottom {
vertical-align: bottom;
}
.align-top {
vertical-align: top;
}
</style>
</head>
<body>
<p id="dynamicUnderline">
<span class="align-bottom">This</span> line has a bottom vertical align span. <br />
<span class="align-top">This</span> line has a top vertical align span.
</p>
</body>
</html>

View File

@ -0,0 +1,21 @@
<html>
<head>
<style>
.underline {
text-decoration: underline;
}
.align-bottom {
vertical-align: bottom;
}
.align-top {
vertical-align: top;
}
</style>
</head>
<body>
<p class="underline">
<span class="align-bottom">This line has only a bottom vertical align span.</span> <br />
<span class="align-top">This line has a top vertical align span.</span>
</p>
</body>
</html>

View File

@ -0,0 +1,26 @@
<html class="reftest-wait">
<head>
<script type="text/javascript">
function addUnderline() {
var element = document.getElementById("dynamicUnderline");
element.style.textDecoration = "underline";
document.documentElement.removeAttribute("class");
}
document.addEventListener('MozReftestInvalidate', addUnderline, false);
</script>
<style>
.align-bottom {
vertical-align: bottom;
}
.align-top {
vertical-align: top;
}
</style>
</head>
<body>
<p id="dynamicUnderline">
<span class="align-bottom">This line has only a bottom vertical align span.</span> <br />
<span class="align-top">This line has a top vertical align span.</span>
</p>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<style>
.underline {
text-decoration: underline;
}
.align-bottom {
vertical-align: bottom;
}
.align-top {
vertical-align: top;
}
</style>
</head>
<body>
<p class="underline">
<span class="align-bottom">This</span> line has a bottom vertical align span. <br />
<span class="align-top">This</span> line has a top vertical align span.
</p>
</body>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<script type="text/javascript">
function addUnderline() {
var element = document.getElementById("dynamicUnderline");
element.style.textDecoration = "underline";
document.documentElement.removeAttribute("class");
}
document.addEventListener('MozReftestInvalidate', addUnderline, false);
</script>
<style>
.align-bottom {
vertical-align: bottom;
}
.align-top {
vertical-align: top;
}
</style>
</head>
<body>
<p id="dynamicUnderline">
<span class="align-bottom">This</span> line has a bottom vertical align span. <br />
<span class="align-top">This</span> line has a top vertical align span.
</p>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<style>
.underline {
text-decoration: underline;
}
.align-bottom {
vertical-align: bottom;
}
.align-top {
vertical-align: top;
}
</style>
</head>
<body>
<p class="underline">
<span class="align-bottom">This line has only a bottom vertical align span.</span> <br />
<span class="align-top">This line has a top vertical align span.</span>
</p>
</body>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<script type="text/javascript">
function addUnderline() {
var element = document.getElementById("dynamicUnderline");
element.style.textDecoration = "underline";
document.documentElement.removeAttribute("class");
}
document.addEventListener('MozReftestInvalidate', addUnderline, false);
</script>
<style>
.align-bottom {
vertical-align: bottom;
}
.align-top {
vertical-align: top;
}
</style>
</head>
<body>
<p id="dynamicUnderline">
<span class="align-bottom">This line has only a bottom vertical align span.</span> <br />
<span class="align-top">This line has a top vertical align span.</span>
</p>
</body>
</html>

View File

@ -4,6 +4,10 @@ skip-if(B2G) == complex-decoration-style-standards.html complex-decoration-style
== decoration-color-standards.html decoration-color-standards-ref.html == decoration-color-standards.html decoration-color-standards-ref.html
== decoration-style-quirks.html decoration-style-quirks-ref.html == decoration-style-quirks.html decoration-style-quirks-ref.html
== decoration-style-standards.html decoration-style-standards-ref.html == 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-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-solid-quirks.html line-through-style-block-quirks-ref.html
!= line-through-style-block-dotted-quirks.html line-through-style-block-quirks-ref.html != line-through-style-block-dotted-quirks.html line-through-style-block-quirks-ref.html
!= line-through-style-block-dashed-quirks.html line-through-style-block-quirks-ref.html != line-through-style-block-dashed-quirks.html line-through-style-block-quirks-ref.html