Skip to content

Commit 3f862cc

Browse files
committed
Bug 1791226 - Don't paint line-clamped lines. r=layout-reviewers,dshin
This matches the proposal in w3c/csswg-drafts#10816, and creates much better behavior. Differential Revision: https://phabricator.services.mozilla.com/D157578
1 parent ebd7d31 commit 3f862cc

File tree

6 files changed

+86
-52
lines changed

6 files changed

+86
-52
lines changed

layout/generic/TextOverflow.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,15 +810,15 @@ bool TextOverflow::HasClippedTextOverflow(nsIFrame* aBlockFrame) {
810810
/* static */
811811
bool TextOverflow::HasBlockEllipsis(nsIFrame* aBlockFrame) {
812812
nsBlockFrame* f = do_QueryFrame(aBlockFrame);
813-
return f && f->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
813+
return f && f->HasLineClampEllipsis();
814814
}
815815

816816
static bool BlockCanHaveLineClampEllipsis(nsBlockFrame* aBlockFrame,
817817
bool aBeforeReflow) {
818818
if (aBeforeReflow) {
819819
return aBlockFrame->IsInLineClampContext();
820820
}
821-
return aBlockFrame->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
821+
return aBlockFrame->HasLineClampEllipsis();
822822
}
823823

824824
/* static */

layout/generic/nsBlockFrame.cpp

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,18 +1170,17 @@ static LogicalSize CalculateContainingBlockSizeForAbsolutes(
11701170
}
11711171

11721172
/**
1173-
* Returns aFrame if it is a non-BFC block frame, and null otherwise.
1173+
* Returns aFrame if it is an in-flow, non-BFC block frame, and null otherwise.
11741174
*
11751175
* This is used to determine whether to recurse into aFrame when applying
11761176
* -webkit-line-clamp.
11771177
*/
11781178
static const nsBlockFrame* GetAsLineClampDescendant(const nsIFrame* aFrame) {
1179-
if (const nsBlockFrame* block = do_QueryFrame(aFrame)) {
1180-
if (!block->HasAnyStateBits(NS_BLOCK_BFC)) {
1181-
return block;
1182-
}
1179+
const nsBlockFrame* block = do_QueryFrame(aFrame);
1180+
if (!block || block->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW | NS_BLOCK_BFC)) {
1181+
return nullptr;
11831182
}
1184-
return nullptr;
1183+
return block;
11851184
}
11861185

11871186
static nsBlockFrame* GetAsLineClampDescendant(nsIFrame* aFrame) {
@@ -1343,28 +1342,30 @@ class MOZ_RAII LineClampLineIterator {
13431342
};
13441343

13451344
static bool ClearLineClampEllipsis(nsBlockFrame* aFrame) {
1346-
if (!aFrame->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS)) {
1345+
if (aFrame->HasLineClampEllipsis()) {
1346+
MOZ_ASSERT(!aFrame->HasLineClampEllipsisDescendant());
1347+
aFrame->SetHasLineClampEllipsis(false);
1348+
for (auto& line : aFrame->Lines()) {
1349+
if (line.HasLineClampEllipsis()) {
1350+
line.ClearHasLineClampEllipsis();
1351+
break;
1352+
}
1353+
}
1354+
return true;
1355+
}
1356+
1357+
if (aFrame->HasLineClampEllipsisDescendant()) {
1358+
aFrame->SetHasLineClampEllipsisDescendant(false);
13471359
for (nsIFrame* f : aFrame->PrincipalChildList()) {
13481360
if (nsBlockFrame* child = GetAsLineClampDescendant(f)) {
13491361
if (ClearLineClampEllipsis(child)) {
13501362
return true;
13511363
}
13521364
}
13531365
}
1354-
return false;
1355-
}
1356-
1357-
aFrame->RemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
1358-
1359-
for (auto& line : aFrame->Lines()) {
1360-
if (line.HasLineClampEllipsis()) {
1361-
line.ClearHasLineClampEllipsis();
1362-
return true;
1363-
}
13641366
}
13651367

1366-
// We didn't find a line with the ellipsis; it must have been deleted already.
1367-
return true;
1368+
return false;
13681369
}
13691370

13701371
void nsBlockFrame::ClearLineClampEllipsis() { ::ClearLineClampEllipsis(this); }
@@ -1869,6 +1870,12 @@ nsReflowStatus nsBlockFrame::TrialReflow(nsPresContext* aPresContext,
18691870
// overflow line lists being cleared out between reflow passes.
18701871
DrainOverflowLines();
18711872

1873+
// Clear any existing -webkit-line-clamp ellipsis if we're reflowing the
1874+
// line-clamp root.
1875+
if (IsLineClampRoot(this)) {
1876+
ClearLineClampEllipsis();
1877+
}
1878+
18721879
bool blockStartMarginRoot, blockEndMarginRoot;
18731880
IsMarginRoot(&blockStartMarginRoot, &blockEndMarginRoot);
18741881

@@ -2018,11 +2025,6 @@ nsReflowStatus nsBlockFrame::TrialReflow(nsPresContext* aPresContext,
20182025
// block-start padding.
20192026
}
20202027

2021-
// Clear any existing -webkit-line-clamp ellipsis.
2022-
if (aReflowInput.mStyleDisplay->mWebkitLineClamp) {
2023-
ClearLineClampEllipsis();
2024-
}
2025-
20262028
CheckFloats(state);
20272029

20282030
// Compute our final size (for this trial layout)
@@ -2052,8 +2054,8 @@ static nsLineBox* FindLineClampTarget(nsBlockFrame*& aFrame,
20522054
nsBlockFrame* aStopAtFrame,
20532055
StyleLineClamp aLineNumber) {
20542056
MOZ_ASSERT(aLineNumber > 0);
2055-
MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS),
2056-
"Should have been removed earlier in nsBlockReflow::Reflow");
2057+
MOZ_ASSERT(!aFrame->HasLineClampEllipsis(),
2058+
"Should have been removed earlier");
20572059

20582060
nsLineBox* target = nullptr;
20592061
nsBlockFrame* targetFrame = nullptr;
@@ -2114,11 +2116,16 @@ nscoord nsBlockFrame::ApplyLineClamp(nscoord aContentBlockEndEdge) {
21142116

21152117
// Mark the line as having an ellipsis so that TextOverflow will render it.
21162118
line->SetHasLineClampEllipsis();
2117-
target->AddStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
2119+
target->SetHasLineClampEllipsis(true);
21182120

21192121
// Translate the b-end edge of the line up to aFrame's space.
21202122
nscoord edge = line->BEnd();
21212123
for (nsIFrame* f = target; f; f = f->GetParent()) {
2124+
MOZ_ASSERT(f->IsBlockFrameOrSubclass(),
2125+
"GetAsLineClampDescendant guarantees this");
2126+
if (f != target) {
2127+
static_cast(f)->SetHasLineClampEllipsisDescendant(true);
2128+
}
21222129
if (f == this) {
21232130
break;
21242131
}
@@ -7637,7 +7644,7 @@ static void DisplayLine(nsDisplayListBuilder* aBuilder,
76377644
const bool aLineInLine, const nsDisplayListSet& aLists,
76387645
nsBlockFrame* aFrame, TextOverflow* aTextOverflow,
76397646
uint32_t aLineNumberForTextOverflow, int32_t aDepth,
7640-
int32_t& aDrawnLines) {
7647+
int32_t& aDrawnLines, bool& aFoundLineClamp) {
76417648
#ifdef DEBUG
76427649
if (nsBlockFrame::gLamePaintMetrics) {
76437650
aDrawnLines++;
@@ -7670,6 +7677,14 @@ static void DisplayLine(nsDisplayListBuilder* aBuilder,
76707677
kid = kid->GetNextSibling();
76717678
}
76727679

7680+
if (aFrame->HasLineClampEllipsisDescendant() && !aLineInLine) {
7681+
if (nsBlockFrame* f = GetAsLineClampDescendant(aLine->mFirstChild)) {
7682+
if (f->HasLineClampEllipsis() || f->HasLineClampEllipsisDescendant()) {
7683+
aFoundLineClamp = true;
7684+
}
7685+
}
7686+
}
7687+
76737688
if (aTextOverflow && aLineInLine) {
76747689
aTextOverflow->ProcessLine(collection, aLine.get(),
76757690
aLineNumberForTextOverflow);
@@ -7774,12 +7789,14 @@ void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
77747789
// runs of text as a whole, which requires we iterate through all lines
77757790
// to find our backplate size.
77767791
nsLineBox* cursor =
7777-
(hasDescendantPlaceHolders || textOverflow.isSome() || backplateColor)
7792+
(hasDescendantPlaceHolders || textOverflow.isSome() || backplateColor ||
7793+
HasLineClampEllipsis() || HasLineClampEllipsisDescendant())
77787794
? nullptr
77797795
: GetFirstLineContaining(aBuilder->GetDirtyRect().y);
77807796
LineIterator line_end = LinesEnd();
77817797

77827798
TextOverflow* textOverflowPtr = textOverflow.ptrOr(nullptr);
7799+
bool foundClamp = false;
77837800

77847801
if (cursor) {
77857802
for (LineIterator line = mLines.begin(cursor); line != line_end; ++line) {
@@ -7794,7 +7811,8 @@ void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
77947811

77957812
if (ShouldDescendIntoLine(lineArea)) {
77967813
DisplayLine(aBuilder, line, line->IsInline(), aLists, this, nullptr,
7797-
0, depth, drawnLines);
7814+
0, depth, drawnLines, foundClamp);
7815+
MOZ_ASSERT(!foundClamp);
77987816
}
77997817
}
78007818
}
@@ -7825,7 +7843,7 @@ void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
78257843

78267844
if ((lineInLine && textOverflowPtr) || ShouldDescendIntoLine(lineArea)) {
78277845
DisplayLine(aBuilder, line, lineInLine, aLists, this, textOverflowPtr,
7828-
lineCount, depth, drawnLines);
7846+
lineCount, depth, drawnLines, foundClamp);
78297847
}
78307848

78317849
if (!lineInLine && !curBackplateArea.IsEmpty()) {
@@ -7856,6 +7874,10 @@ void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
78567874
}
78577875
}
78587876
}
7877+
foundClamp = foundClamp || line->HasLineClampEllipsis();
7878+
if (foundClamp) {
7879+
break;
7880+
}
78597881
lineCount++;
78607882
}
78617883

layout/generic/nsBlockFrame.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,28 @@ class nsBlockFrame : public nsContainerFrame {
681681
* This function is O(1).
682682
*/
683683
bool MaybeHasFloats() const;
684+
/**
685+
* This indicates that exactly one line in this block has the
686+
* LineClampEllipsis flag set, and that such a line must be found
687+
* and have that flag cleared when reflowing this element's nearest legacy box
688+
* container.
689+
*/
690+
bool HasLineClampEllipsis() const {
691+
return HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
692+
}
693+
/**
694+
* This indicates that we have a descendant in our block formatting context
695+
* that has such a line.
696+
*/
697+
bool HasLineClampEllipsisDescendant() const {
698+
return HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS_DESCENDANT);
699+
}
700+
void SetHasLineClampEllipsis(bool aValue) {
701+
AddOrRemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS, aValue);
702+
}
703+
void SetHasLineClampEllipsisDescendant(bool aValue) {
704+
AddOrRemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS_DESCENDANT, aValue);
705+
}
684706

685707
protected:
686708
nsBlockFrame* GetLineClampRoot() const;

layout/generic/nsFrameStateBits.h

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -562,18 +562,14 @@ FRAME_STATE_BIT(Block, 28, NS_BLOCK_HAS_MARKER)
562562
// continuation chain or none of them.
563563
FRAME_STATE_BIT(Block, 29, NS_BLOCK_NEEDS_BIDI_RESOLUTION)
564564

565-
// bits 30 and 31 free.
566-
567-
// NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS indicates that exactly one line in this
568-
// block has the LineClampEllipsis flag set, and that such a line must be found
569-
// and have that flag cleared when reflowing this element's nearest legacy box
570-
// container.
571-
FRAME_STATE_BIT(Block, 60, NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS)
565+
// See nsBlockFrame.h for docs
566+
FRAME_STATE_BIT(Block, 30, NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS)
567+
FRAME_STATE_BIT(Block, 31, NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS_DESCENDANT)
572568

573569
// This block has had a child marked dirty, so before we reflow we need
574570
// to look through the lines to find any such children and mark
575571
// appropriate lines dirty.
576-
FRAME_STATE_BIT(Block, 61, NS_BLOCK_LOOK_FOR_DIRTY_FRAMES)
572+
FRAME_STATE_BIT(Block, 60, NS_BLOCK_LOOK_FOR_DIRTY_FRAMES)
577573

578574
// Are our cached intrinsic inline sizes for font size inflation? i.e., what was
579575
// the current state of GetPresContext()->mInflationDisabledForShrinkWrap at the
@@ -583,13 +579,13 @@ FRAME_STATE_BIT(Block, 61, NS_BLOCK_LOOK_FOR_DIRTY_FRAMES)
583579
// to track this because it's the only thing that caches intrinsic inline sizes
584580
// that lives inside of things (form controls) that do intrinsic sizing with
585581
// font inflation enabled.
586-
FRAME_STATE_BIT(Block, 62, NS_BLOCK_INTRINSICS_INFLATED)
582+
FRAME_STATE_BIT(Block, 61, NS_BLOCK_INTRINSICS_INFLATED)
587583

588584
// NS_BLOCK_HAS_FIRST_LETTER_CHILD means that there is an inflow first-letter
589585
// frame among the block's descendants. If there is a floating first-letter
590586
// frame, or the block has first-letter style but has no first letter, this
591587
// bit is not set. This bit is set on the first continuation only.
592-
FRAME_STATE_BIT(Block, 63, NS_BLOCK_HAS_FIRST_LETTER_CHILD)
588+
FRAME_STATE_BIT(Block, 62, NS_BLOCK_HAS_FIRST_LETTER_CHILD)
593589

594590
// == Frame state bits that apply to image frames =============================
595591

testing/web-platform/tests/css/css-overflow/line-clamp/reference/webkit-line-clamp-040-ref.html

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,4 @@
1111
style>
1212
<div class="clamp">Line 1
1313
Line 2…
14-
Line 3
15-
Line 4
16-
Line 5div>
14+
div>

testing/web-platform/tests/css/css-overflow/line-clamp/webkit-line-clamp-050.html

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,11 @@
1616
border: medium solid green;
1717
padding: 15px;
1818
}
19-
span {
20-
/* TODO: Remove once we don't paint clamped lines */
21-
color: transparent;
22-
}
2319
style>
2420
<div class="clamp">
2521
Line1
26-
<div>Line2<br><span>Line3span>div>
27-
<span>Line4span>
22+
<div>Line2<br>Line3div>
23+
Line4
2824
<div>Line5<br>Line6div>
2925
Line7
3026
div>

0 commit comments

Comments
 (0)