Skip to content

Commit

Permalink
[IFC] Maximum intrinsic width content caching should support "line-br…
Browse files Browse the repository at this point in the history
…eak: after-white-space"

https://bugs.webkit.org/show_bug.cgi?id=260568

Reviewed by Antti Koivisto.

"line-break: after-white-space" trims trailing whitespace in intrinsic width mode, while
preserves them in final layout.
This patch restores such trimmed whitespace content so that we can cache the layout result for subsequent layouts.

* Source/WebCore/layout/formattingContexts/inline/AbstractLineBuilder.h:
* Source/WebCore/layout/formattingContexts/inline/InlineLine.cpp:
(WebCore::Layout::Line::handleTrailingTrimmableContent):
(WebCore::Layout::Line::restoreTrimmedTrailingWhitespace):
* Source/WebCore/layout/formattingContexts/inline/InlineLine.h:
* Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.cpp:
(WebCore::Layout::LineBuilder::layoutInlineContent):
* Source/WebCore/layout/formattingContexts/inline/IntrinsicWidthHandler.cpp:
(WebCore::Layout::IntrinsicWidthHandler::computedIntrinsicSizes):
(WebCore::Layout::IntrinsicWidthHandler::computedIntrinsicWidthForConstraint):
* Source/WebCore/layout/formattingContexts/inline/TextOnlySimpleLineBuilder.cpp:
(WebCore::Layout::TextOnlySimpleLineBuilder::layoutInlineContent):
(WebCore::Layout::TextOnlySimpleLineBuilder::initialize):
(WebCore::Layout::TextOnlySimpleLineBuilder::handleLineEnding):
(WebCore::Layout::TextOnlySimpleLineBuilder::hasIntrinsicWidthSpecificStyle): Deleted.
* Source/WebCore/layout/formattingContexts/inline/TextOnlySimpleLineBuilder.h:

Canonical link: https://commits.webkit.org/267245@main
  • Loading branch information
alanbaradlay committed Aug 24, 2023
1 parent 55efa3e commit 8f04643
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ layer at (0,0) size 800x600
text run at (1,1) width 27: "foo "
RenderTableRow {TR} at (0,27) size 29x23
RenderTableCell {TD} at (2,27) size 25x23 [r=1 c=0 rs=1 cs=1]
RenderText {#text} at (1,1) size 26x20
text run at (1,1) width 26: "bar "
RenderText {#text} at (1,1) size 23x20
text run at (1,1) width 23: "bar"
selection start: position 1 of child 0 {#text} of child 1 {TD} of child 0 {TR} of child 1 {TBODY} of child 1 {TABLE} of child 1 {DIV} of body
selection end: position 2 of child 1 {TABLE} of child 1 {DIV} of body
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ layer at (0,0) size 800x600
text run at (1,1) width 27: "foo "
RenderTableRow {TR} at (0,26) size 29x22
RenderTableCell {TD} at (2,26) size 25x22 [r=1 c=0 rs=1 cs=1]
RenderText {#text} at (1,1) size 26x20
text run at (1,1) width 26: "bar "
RenderText {#text} at (1,1) size 23x20
text run at (1,1) width 23: "bar"
selection start: position 1 of child 0 {#text} of child 1 {TD} of child 0 {TR} of child 1 {TBODY} of child 1 {TABLE} of child 1 {DIV} of body
selection end: position 2 of child 1 {TABLE} of child 1 {DIV} of body
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ struct LineLayoutResult {
IsFirstLast isFirstLast { };
// Misc
size_t nonSpanningInlineLevelBoxCount { 0 };
InlineLayoutUnit trimmedTrailingWhitespaceWidth { 0.f }; // only used for line-break: after-white-space currently
std::optional<InlineLayoutUnit> hintForNextLineTopToAvoidIntrusiveFloat { }; // This is only used for cases when intrusive floats prevent any content placement at current vertical position.
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,33 @@ bool InlineFormattingContext::createDisplayContentForLineFromCachedContent(const
{
if (!m_maximumIntrinsicWidthResultForSingleLine)
return false;
if (m_maximumIntrinsicWidthResultForSingleLine->constraint > constraints.horizontal().logicalWidth) {
auto horizontalAvailableSpace = constraints.horizontal().logicalWidth;
if (m_maximumIntrinsicWidthResultForSingleLine->constraint > horizontalAvailableSpace) {
m_maximumIntrinsicWidthResultForSingleLine = { };
return false;
}
if (!inlineLayoutState.parentBlockLayoutState().floatingState().isEmpty()) {
m_maximumIntrinsicWidthResultForSingleLine = { };
return false;
}

auto& lineBreakingResult = m_maximumIntrinsicWidthResultForSingleLine->result;
auto restoreTrimmedTrailingWhitespaceIfApplicable = [&] {
if (root().style().lineBreak() != LineBreak::AfterWhiteSpace || !lineBreakingResult.trimmedTrailingWhitespaceWidth)
return;
if (ceiledLayoutUnit(lineBreakingResult.contentGeometry.logicalWidth) + LayoutUnit::epsilon() <= horizontalAvailableSpace)
return;
if (!Line::restoreTrimmedTrailingWhitespace(lineBreakingResult.trimmedTrailingWhitespaceWidth, lineBreakingResult.inlineContent)) {
ASSERT_NOT_REACHED();
m_maximumIntrinsicWidthResultForSingleLine = { };
return;
}
lineBreakingResult.contentGeometry.logicalWidth += lineBreakingResult.trimmedTrailingWhitespaceWidth;
lineBreakingResult.contentGeometry.logicalRightIncludingNegativeMargin += lineBreakingResult.trimmedTrailingWhitespaceWidth;
lineBreakingResult.trimmedTrailingWhitespaceWidth = { };
};
restoreTrimmedTrailingWhitespaceIfApplicable();

lineBreakingResult.lineGeometry.logicalTopLeft = { constraints.horizontal().logicalLeft, constraints.logicalTop() };
lineBreakingResult.lineGeometry.logicalWidth = constraints.horizontal().logicalWidth;
lineBreakingResult.contentGeometry.logicalLeft = InlineFormattingGeometry::horizontalAlignmentOffset(root().style(), lineBreakingResult.contentGeometry.logicalWidth, lineBreakingResult.lineGeometry.logicalWidth, lineBreakingResult.hangingContent.logicalWidth, lineBreakingResult.inlineContent, true);
Expand Down
32 changes: 27 additions & 5 deletions Source/WebCore/layout/formattingContexts/inline/InlineLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,19 @@ void Line::applyRunExpansion(InlineLayoutUnit horizontalAvailableSpace)
m_contentLogicalWidth += accumulatedExpansion;
}

void Line::handleTrailingTrimmableContent(TrailingContentAction trailingTrimmableContentAction)
InlineLayoutUnit Line::handleTrailingTrimmableContent(TrailingContentAction trailingTrimmableContentAction)
{
if (m_trimmableTrailingContent.isEmpty() || m_runs.isEmpty())
return;
return { };

if (trailingTrimmableContentAction == TrailingContentAction::Preserve)
return m_trimmableTrailingContent.reset();
if (trailingTrimmableContentAction == TrailingContentAction::Preserve) {
m_trimmableTrailingContent.reset();
return { };
}

m_contentLogicalWidth -= m_trimmableTrailingContent.remove();
auto trimmedWidth = m_trimmableTrailingContent.remove();
m_contentLogicalWidth -= trimmedWidth;
return trimmedWidth;
}

void Line::handleOverflowingNonBreakingSpace(TrailingContentAction trailingContentAction, InlineLayoutUnit overflowingWidth)
Expand Down Expand Up @@ -721,6 +725,24 @@ bool Line::lineHasVisuallyNonEmptyContent() const
return false;
}

bool Line::restoreTrimmedTrailingWhitespace(InlineLayoutUnit trimmedTrailingWhitespaceWidth, RunList& runs)
{
auto& lastRun = runs.last();
if (!lastRun.isText()) {
ASSERT_NOT_REACHED();
return false;
}
auto& layoutBox = downcast<InlineTextBox>(lastRun.layoutBox());
if (lastRun.m_textContent->start + lastRun.m_textContent->length == layoutBox.content().length()) {
ASSERT_NOT_REACHED();
return false;
}
lastRun.m_logicalWidth += trimmedTrailingWhitespaceWidth;
// This must be collapsed whitespace.
lastRun.m_textContent->length += 1;
return true;
}

const InlineFormattingContext& Line::formattingContext() const
{
return m_inlineFormattingContext;
Expand Down
4 changes: 3 additions & 1 deletion Source/WebCore/layout/formattingContexts/inline/InlineLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class Line {
void addTrailingHyphen(InlineLayoutUnit hyphenLogicalWidth);

enum class TrailingContentAction : uint8_t { Remove, Preserve };
void handleTrailingTrimmableContent(TrailingContentAction);
InlineLayoutUnit handleTrailingTrimmableContent(TrailingContentAction);
void handleTrailingHangingContent(std::optional<IntrinsicWidthMode>, InlineLayoutUnit horizontalAvailableSpace, bool isLastFormattedLine);
void handleOverflowingNonBreakingSpace(TrailingContentAction, InlineLayoutUnit overflowingWidth);
void resetBidiLevelForTrailingWhitespace(UBiDiLevel rootBidiLevel);
Expand Down Expand Up @@ -197,6 +197,8 @@ class Line {
};
Result close();

static bool restoreTrimmedTrailingWhitespace(InlineLayoutUnit trimmedTrailingWhitespaceWidth, RunList&);

private:
InlineLayoutUnit lastRunLogicalRight() const { return m_runs.isEmpty() ? 0.0f : m_runs.last().logicalRight(); }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ LineLayoutResult LineBuilder::layoutInlineContent(const LineInput& lineInput, co
, { WTFMove(visualOrderList), inlineBaseDirection }
, { isFirstFormattedLine() ? LineLayoutResult::IsFirstLast::FirstFormattedLine::WithinIFC : LineLayoutResult::IsFirstLast::FirstFormattedLine::No, isLastLine }
, result.nonSpanningInlineLevelBoxCount
, { }
, lineContent.range.isEmpty() ? std::make_optional(m_lineLogicalRect.top() + m_candidateInlineContentEnclosingHeight) : std::nullopt
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ IntrinsicWidthConstraints IntrinsicWidthHandler::computedIntrinsicSizes()
if (TextOnlySimpleLineBuilder::isEligibleForSimplifiedTextOnlyInlineLayout(root(), inlineFormattingState)) {
auto simplifiedLineBuilder = TextOnlySimpleLineBuilder { formattingContext(), { }, inlineFormattingState.inlineItems() };
auto minimumWidth = isEligibleForNonLineBuilderProcess(rootStyle) ? ceiledLayoutUnit(simplifiedMinimumWidth()) : computedIntrinsicValue(IntrinsicWidthMode::Minimum, simplifiedLineBuilder);
return { minimumWidth, computedIntrinsicValue(IntrinsicWidthMode::Maximum, simplifiedLineBuilder, TextOnlySimpleLineBuilder::hasIntrinsicWidthSpecificStyle(rootStyle) ? MayCacheLayoutResult::No : MayCacheLayoutResult::Yes) };
return { minimumWidth, computedIntrinsicValue(IntrinsicWidthMode::Maximum, simplifiedLineBuilder, MayCacheLayoutResult::Yes) };
}

auto floatingState = FloatingState { root() };
Expand Down Expand Up @@ -112,12 +112,18 @@ InlineLayoutUnit IntrinsicWidthHandler::computedIntrinsicWidthForConstraint(Intr

layoutRange.start = InlineFormattingGeometry::leadingInlineItemPositionForNextLine(lineLayoutResult.inlineItemRange.end, previousLineEnd, layoutRange.end);
if (layoutRange.isEmpty()) {
auto shouldCacheLineBreakingResultForSubsequentLayout = !lineIndex && mayCacheLayoutResult == MayCacheLayoutResult::Yes;
if (shouldCacheLineBreakingResultForSubsequentLayout)
auto cacheLineBreakingResultForSubsequentLayoutIfApplicable = [&] {
m_maximumIntrinsicWidthResultForSingleLine = { };
if (mayCacheLayoutResult == MayCacheLayoutResult::No)
return;
m_maximumIntrinsicWidthResultForSingleLine = LineBreakingResult { ceiledLayoutUnit(maximumContentWidth), WTFMove(lineLayoutResult) };
};
cacheLineBreakingResultForSubsequentLayoutIfApplicable();
break;
}

// Support single line only.
mayCacheLayoutResult = MayCacheLayoutResult::No;
previousLineEnd = layoutRange.start;
previousLine = PreviousLine { lineIndex++, lineLayoutResult.contentGeometry.trailingOverflowingContentWidth, { }, { }, WTFMove(lineLayoutResult.floatContent.suspendedFloats) };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ LineLayoutResult TextOnlySimpleLineBuilder::layoutInlineContent(const LineInput&
, { !result.isHangingTrailingContentWhitespace, result.hangingTrailingContentWidth }
, { }
, { isFirstFormattedLine() ? LineLayoutResult::IsFirstLast::FirstFormattedLine::WithinIFC : LineLayoutResult::IsFirstLast::FirstFormattedLine::No, isLastLine }
, { }
, m_trimmedTrailingWhitespaceWidth
};
}

Expand All @@ -139,6 +141,7 @@ void TextOnlySimpleLineBuilder::initialize(const InlineItemRange& layoutRange, c
m_previousLine = previousLine;
m_lineLogicalRect = initialLogicalRect;
m_wrapOpportunityList = { };
m_trimmedTrailingWhitespaceWidth = { };
}

InlineItemPosition TextOnlySimpleLineBuilder::placeInlineTextContent(const InlineItemRange& layoutRange)
Expand Down Expand Up @@ -374,7 +377,7 @@ void TextOnlySimpleLineBuilder::handleLineEnding(InlineItemPosition placedConten
auto shouldPreserveTrailingWhitespace = [&] {
return root().style().lineBreak() == LineBreak::AfterWhiteSpace && intrinsicWidthMode() != IntrinsicWidthMode::Minimum && (!isLastLine || horizontalAvailableSpace < m_line.contentLogicalWidth());
};
m_line.handleTrailingTrimmableContent(shouldPreserveTrailingWhitespace() ? Line::TrailingContentAction::Preserve : Line::TrailingContentAction::Remove);
m_trimmedTrailingWhitespaceWidth = m_line.handleTrailingTrimmableContent(shouldPreserveTrailingWhitespace() ? Line::TrailingContentAction::Preserve : Line::TrailingContentAction::Remove);
if (formattingContext().formattingQuirks().trailingNonBreakingSpaceNeedsAdjustment(isInIntrinsicWidthMode(), horizontalAvailableSpace < m_line.contentLogicalWidth()))
m_line.handleOverflowingNonBreakingSpace(shouldPreserveTrailingWhitespace() ? Line::TrailingContentAction::Preserve : Line::TrailingContentAction::Remove, m_line.contentLogicalWidth() - horizontalAvailableSpace);

Expand Down Expand Up @@ -468,11 +471,6 @@ bool TextOnlySimpleLineBuilder::isEligibleForSimplifiedTextOnlyInlineLayout(cons
return true;
}

bool TextOnlySimpleLineBuilder::hasIntrinsicWidthSpecificStyle(const RenderStyle& rootStyle)
{
return rootStyle.lineBreak() == LineBreak::AfterWhiteSpace;
}

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ class TextOnlySimpleLineBuilder : public AbstractLineBuilder {
LineLayoutResult layoutInlineContent(const LineInput&, const std::optional<PreviousLine>&) final;

static bool isEligibleForSimplifiedTextOnlyInlineLayout(const ElementBox& root, const InlineFormattingState&, const FloatingState* = nullptr);
static bool hasIntrinsicWidthSpecificStyle(const RenderStyle& rootStyle);

private:
InlineItemPosition placeInlineTextContent(const InlineItemRange&);
Expand Down Expand Up @@ -75,6 +74,7 @@ class TextOnlySimpleLineBuilder : public AbstractLineBuilder {
const InlineItems& m_inlineItems;
Vector<const InlineTextItem*> m_wrapOpportunityList;
bool m_isWrappingAllowed { false };
InlineLayoutUnit m_trimmedTrailingWhitespaceWidth { 0.f };

std::optional<InlineTextItem> m_partialLeadingTextItem;
};
Expand Down

0 comments on commit 8f04643

Please sign in to comment.