Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
"-webkit-box-decoration-break: clone" with left and right padding cau…
…ses unexpected wrapping of inline content

https://bugs.webkit.org/show_bug.cgi?id=259188
<rdar://problem/112197978>

Reviewed by Antti Koivisto.

This bug is specific to shrink-to-fit content with "-webkit-box-decoration-break: clone" when
the content is supposed to fit the current line.

The fix is about accounting for the accumulated decoration end width when adjusting the available width for the continuous content.
We already do that for content on the current line and this patch adjusts it for the current continuous content as well.

* LayoutTests/fast/inline/shrink-to-fit-content-with-decoration-clone-expected.html: Added.
* LayoutTests/fast/inline/shrink-to-fit-content-with-decoration-clone.html: Added.
* Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.cpp:
(WebCore::Layout::LineCandidate::InlineContent::setAccumulatedClonedDecorationEnd):
(WebCore::Layout::LineCandidate::InlineContent::accumulatedClonedDecorationEnd const):
(WebCore::Layout::LineCandidate::InlineContent::reset):
(WebCore::Layout::LineBuilder::candidateContentForLine):
(WebCore::Layout::availableWidth):

Canonical link: https://commits.webkit.org/266051@main
  • Loading branch information
alanbaradlay committed Jul 14, 2023
1 parent 59c4d69 commit e05c6e3
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 3 deletions.
@@ -0,0 +1,19 @@
<style>
.shrink_to_fit {
float: left;
font-size: 10px;
background-color: green;
margin-left: 5px;
}

span {
padding-right: 20px;
}
</style>
<div class=shrink_to_fit><span>Pass if no wrap</span></div>
<div class=shrink_to_fit><span>Pass if no wrap</span></div>
<div class=shrink_to_fit><span><span>Pass if no wrap</span></span></div>
<div class=shrink_to_fit><span>Pass if no wrap</span></div>
<div class=shrink_to_fit><span>Pass if no wrap</span></div>
<div class=shrink_to_fit><span><span style="padding-right: 0px">Pass if no wrap</span></span></div>
<div class=shrink_to_fit><span>Pass <span>if no wrap</span></span></div>
@@ -0,0 +1,21 @@
<style>
.shrink_to_fit {
float: left;
font-size: 10px;
background-color: green;
margin-left: 5px;
}

.decoration_clone {
padding-right: 20px;
overflow-wrap: break-word;
-webkit-box-decoration-break: clone;
}
</style>
<div class=shrink_to_fit><span style="padding-right: 20px">Pass&nbspif&nbspno&nbspwrap</span></div>
<div class=shrink_to_fit><span class=decoration_clone>Pass&nbspif&nbspno&nbspwrap</span></div>
<div class=shrink_to_fit><span class=decoration_clone><span class=decoration_clone>Pass&nbspif&nbspno&nbspwrap</span></span></div>
<div class=shrink_to_fit><span class=decoration_clone>Pass&nbspif&nbspno&nbspwrap</span></div>
<div class=shrink_to_fit><span class=decoration_clone>Pass if no wrap</span></div>
<div class=shrink_to_fit><span class=decoration_clone><span>Pass if no wrap</span></div>
<div class=shrink_to_fit><span class=decoration_clone>Pass <span class=decoration_clone>if no wrap</span></span></div>
Expand Up @@ -215,13 +215,17 @@ struct LineCandidate {

void setHangingContentWidth(InlineLayoutUnit logicalWidth) { m_continuousContent.setHangingContentWidth(logicalWidth); }

void setAccumulatedClonedDecorationEnd(InlineLayoutUnit accumulatedWidth) { m_accumulatedClonedDecorationEnd = accumulatedWidth; }
InlineLayoutUnit accumulatedClonedDecorationEnd() const { return m_accumulatedClonedDecorationEnd; }

private:
// FIXME: Enable this when we stop feature-matching legacy line layout.
bool m_ignoreTrailingLetterSpacing { true };

InlineContentBreaker::ContinuousContent m_continuousContent;
const InlineItem* m_trailingLineBreak { nullptr };
const InlineItem* m_trailingWordBreakOpportunity { nullptr };
InlineLayoutUnit m_accumulatedClonedDecorationEnd { 0.f };
bool m_hasTrailingSoftWrapOpportunity { false };
};

Expand Down Expand Up @@ -271,6 +275,7 @@ inline void LineCandidate::InlineContent::reset()
m_continuousContent.reset();
m_trailingLineBreak = { };
m_trailingWordBreakOpportunity = { };
m_accumulatedClonedDecorationEnd = { };
}

inline void LineCandidate::reset()
Expand Down Expand Up @@ -696,6 +701,10 @@ void LineBuilder::candidateContentForLine(LineCandidate& lineCandidate, size_t c

auto firstInlineTextItemIndex = std::optional<size_t> { };
auto lastInlineTextItemIndex = std::optional<size_t> { };
#if ENABLE(CSS_BOX_DECORATION_BREAK)
HashSet<const Box*> inlineBoxListWithClonedDecorationEnd;
auto accumulatedDecorationEndWidth = InlineLayoutUnit { 0.f };
#endif
for (auto index = currentInlineItemIndex; index < softWrapOpportunityIndex; ++index) {
auto& inlineItem = m_inlineItems[index];
auto& style = isFirstFormattedLine() ? inlineItem.firstLineStyle() : inlineItem.style();
Expand All @@ -718,6 +727,15 @@ void LineBuilder::candidateContentForLine(LineCandidate& lineCandidate, size_t c
}
if (inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd()) {
auto logicalWidth = inlineItemWidth(inlineItem, currentLogicalRight);
#if ENABLE(CSS_BOX_DECORATION_BREAK)
if (style.boxDecorationBreak() == BoxDecorationBreak::Clone) {
auto& layoutBox = inlineItem.layoutBox();
if (inlineItem.isInlineBoxStart())
inlineBoxListWithClonedDecorationEnd.add(&layoutBox);
else if (inlineBoxListWithClonedDecorationEnd.contains(&layoutBox))
accumulatedDecorationEndWidth += logicalWidth;
}
#endif
lineCandidate.inlineContent.appendInlineItem(inlineItem, style, logicalWidth);
currentLogicalRight += logicalWidth;
continue;
Expand All @@ -741,6 +759,7 @@ void LineBuilder::candidateContentForLine(LineCandidate& lineCandidate, size_t c
}
ASSERT_NOT_REACHED();
}
lineCandidate.inlineContent.setAccumulatedClonedDecorationEnd(accumulatedDecorationEndWidth);

auto setLeadingAndTrailingHangingPunctuation = [&] {
auto hangingContentWidth = lineCandidate.inlineContent.continuousContent().hangingContentWidth();
Expand Down Expand Up @@ -893,10 +912,10 @@ static inline InlineLayoutUnit availableWidth(const LineCandidate::InlineContent
#endif
auto availableWidth = availableWidthForContent - line.contentLogicalRight();
auto& inlineBoxListWithClonedDecorationEnd = line.inlineBoxListWithClonedDecorationEnd();
if (inlineBoxListWithClonedDecorationEnd.isEmpty())
return std::isnan(availableWidth) ? maxInlineLayoutUnit() : availableWidth;
// We may try to commit a inline box end here which already takes up place implicitly through the cloned decoration.
// Let's not account for its logical width twice.
if (inlineBoxListWithClonedDecorationEnd.isEmpty())
return std::isnan(availableWidth) ? maxInlineLayoutUnit() : (availableWidth + candidateContent.accumulatedClonedDecorationEnd());
for (auto& run : candidateContent.continuousContent().runs()) {
if (!run.inlineItem.isInlineBoxEnd())
continue;
Expand All @@ -905,7 +924,7 @@ static inline InlineLayoutUnit availableWidth(const LineCandidate::InlineContent
continue;
availableWidth += decorationEntry->value;
}
return std::isnan(availableWidth) ? maxInlineLayoutUnit() : availableWidth;
return std::isnan(availableWidth) ? maxInlineLayoutUnit() : (availableWidth + candidateContent.accumulatedClonedDecorationEnd());
}

static std::optional<InlineLayoutUnit> eligibleOverflowWidthAsLeading(const InlineContentBreaker::ContinuousContent::RunList& candidateRuns, const InlineContentBreaker::Result& lineBreakingResult, bool isFirstFormattedLine)
Expand Down

0 comments on commit e05c6e3

Please sign in to comment.