Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (254804 => 254805)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp 2020-01-19 15:19:00 UTC (rev 254804)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp 2020-01-19 16:02:01 UTC (rev 254805)
@@ -64,129 +64,9 @@
m_width = 0;
}
-struct LineBuilder::ContinuousContent {
-public:
- ContinuousContent(const InlineItemRun&, bool textIsAlignJustify);
-
- bool isEligible(const InlineItemRun&) const;
- void append(const InlineItemRun&);
- LineBuilder::Run close();
-
- static bool canInlineItemRunBeExpanded(const InlineItemRun& run) { return run.isText() && !run.isCollapsed() && !run.isCollapsedToZeroAdvanceWidth(); }
-
-private:
- const InlineItemRun& m_initialInlineRun;
- const bool m_collectExpansionOpportunities { false };
- unsigned m_expandedLength { 0 };
- InlineLayoutUnit m_expandedWidth { 0 };
- bool m_trailingRunCanBeExpanded { true };
- bool m_hasTrailingExpansionOpportunity { false };
- unsigned m_expansionOpportunityCount { 0 };
-};
-
-LineBuilder::ContinuousContent::ContinuousContent(const InlineItemRun& initialInlineRun, bool textIsAlignJustify)
- : m_initialInlineRun(initialInlineRun)
- , m_collectExpansionOpportunities(textIsAlignJustify && !isWhitespacePreserved(m_initialInlineRun.style())) // Do not collect expansion data on preserved whitespace content (we should not mutate the spacing between runs in such cases).
-{
- // We should not create a ContinuousContent object when even the inital run can not be expanded.
- ASSERT(canInlineItemRunBeExpanded(initialInlineRun));
-}
-
-bool LineBuilder::ContinuousContent::isEligible(const InlineItemRun& inlineItemRun) const
-{
- if (!m_trailingRunCanBeExpanded)
- return false;
- // Only non-collapsed text runs with the same layout box can be added as continuous content.
- return inlineItemRun.isText() && !inlineItemRun.isCollapsedToZeroAdvanceWidth() && &m_initialInlineRun.layoutBox() == &inlineItemRun.layoutBox();
-}
-
-void LineBuilder::ContinuousContent::append(const InlineItemRun& inlineItemRun)
-{
- // Merged content needs to be continuous.
- ASSERT(isEligible(inlineItemRun));
- m_trailingRunCanBeExpanded = canInlineItemRunBeExpanded(inlineItemRun);
-
- ASSERT(inlineItemRun.isText());
- m_expandedLength += inlineItemRun.textContext()->length();
- m_expandedWidth += inlineItemRun.logicalWidth();
-
- if (m_collectExpansionOpportunities) {
- m_hasTrailingExpansionOpportunity = inlineItemRun.hasExpansionOpportunity();
- if (m_hasTrailingExpansionOpportunity)
- ++m_expansionOpportunityCount;
- }
-}
-
-LineBuilder::Run LineBuilder::ContinuousContent::close()
-{
- if (!m_expandedLength)
- return { m_initialInlineRun };
- // Expand the text content and set the expansion opportunities.
- ASSERT(m_initialInlineRun.isText());
- auto textContext = *m_initialInlineRun.textContext();
- auto length = textContext.length() + m_expandedLength;
- textContext.expand(length);
-
- if (m_collectExpansionOpportunities) {
- // FIXME: This is a very simple expansion merge. We should eventually switch over to FontCascade::expansionOpportunityCount.
- ExpansionBehavior expansionBehavior = m_hasTrailingExpansionOpportunity ? (ForbidLeadingExpansion | AllowTrailingExpansion) : (AllowLeadingExpansion | AllowTrailingExpansion);
- if (m_initialInlineRun.hasExpansionOpportunity())
- ++m_expansionOpportunityCount;
- textContext.setExpansion({ expansionBehavior, { } });
- }
- return { m_initialInlineRun, Display::InlineRect { 0_lu, m_initialInlineRun.logicalLeft(), m_initialInlineRun.logicalWidth() + m_expandedWidth, 0_lu }, textContext, m_expansionOpportunityCount };
-}
-
-LineBuilder::Run::Run(const InlineItemRun& inlineItemRun)
- : m_layoutBox(&inlineItemRun.layoutBox())
- , m_type(inlineItemRun.type())
- , m_logicalRect({ 0_lu, inlineItemRun.logicalLeft(), inlineItemRun.logicalWidth(), 0_lu })
- , m_textContext(inlineItemRun.textContext())
- , m_isCollapsedToVisuallyEmpty(inlineItemRun.isCollapsedToZeroAdvanceWidth())
-{
- if (inlineItemRun.hasExpansionOpportunity()) {
- m_expansionOpportunityCount = 1;
- ASSERT(m_textContext);
- m_textContext->setExpansion({ DefaultExpansion, { } });
- }
-}
-
-LineBuilder::Run::Run(const InlineItemRun& inlineItemRun, const Display::InlineRect& logicalRect, const Display::Run::TextContext& textContext, unsigned expansionOpportunityCount)
- : m_layoutBox(&inlineItemRun.layoutBox())
- , m_type(inlineItemRun.type())
- , m_logicalRect(logicalRect)
- , m_textContext(textContext)
- , m_expansionOpportunityCount(expansionOpportunityCount)
- , m_isCollapsedToVisuallyEmpty(inlineItemRun.isCollapsedToZeroAdvanceWidth())
-{
-}
-
-void LineBuilder::Run::adjustExpansionBehavior(ExpansionBehavior expansionBehavior)
-{
- ASSERT(isText());
- ASSERT(hasExpansionOpportunity());
- m_textContext->setExpansion({ expansionBehavior, m_textContext->expansion()->horizontalExpansion });
-}
-
-inline Optional<ExpansionBehavior> LineBuilder::Run::expansionBehavior() const
-{
- ASSERT(isText());
- if (auto expansionContext = m_textContext->expansion())
- return expansionContext->behavior;
- return { };
-}
-
-void LineBuilder::Run::setComputedHorizontalExpansion(InlineLayoutUnit logicalExpansion)
-{
- ASSERT(isText());
- ASSERT(hasExpansionOpportunity());
- m_logicalRect.expandHorizontally(logicalExpansion);
- m_textContext->setExpansion({ m_textContext->expansion()->behavior, logicalExpansion });
-}
-
LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, Optional<TextAlignMode> horizontalAlignment, IntrinsicSizing intrinsicSizing)
: m_inlineFormattingContext(inlineFormattingContext)
- , m_trimmableTrailingContent(m_inlineItemRuns)
+ , m_trimmableTrailingContent(m_runs)
, m_horizontalAlignment(horizontalAlignment)
, m_isIntrinsicSizing(intrinsicSizing == IntrinsicSizing::Yes)
, m_shouldIgnoreTrailingLetterSpacing(RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled())
@@ -216,19 +96,11 @@
m_lineLogicalWidth = constraints.availableLogicalWidth;
m_hasIntrusiveFloat = constraints.lineIsConstrainedByFloat;
- m_inlineItemRuns.clear();
+ m_runs.clear();
m_trimmableTrailingContent.reset();
m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = { };
}
-static inline bool shouldPreserveLeadingContent(const InlineTextItem& inlineTextItem)
-{
- if (!inlineTextItem.isWhitespace())
- return true;
- auto whitespace = inlineTextItem.style().whiteSpace();
- return whitespace == WhiteSpace::Pre || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::BreakSpaces;
-}
-
LineBuilder::RunList LineBuilder::close(IsLastLineWithInlineContent isLastLineWithInlineContent)
{
// 1. Remove trimmable trailing content.
@@ -238,28 +110,8 @@
visuallyCollapsePreWrapOverflowContent();
auto hangingContent = collectHangingContent(isLastLineWithInlineContent);
- auto mergedInlineItemRuns = [&] {
- RunList runList;
- unsigned runIndex = 0;
- while (runIndex < m_inlineItemRuns.size()) {
- // Merge eligible runs.
- auto& inlineItemRun = m_inlineItemRuns[runIndex];
- if (!ContinuousContent::canInlineItemRunBeExpanded(inlineItemRun)) {
- runList.append({ inlineItemRun });
- ++runIndex;
- continue;
- }
- auto mergedRuns = ContinuousContent { inlineItemRun, isTextAlignJustify() };
- for (runIndex = runIndex + 1; runIndex < m_inlineItemRuns.size() && mergedRuns.isEligible(m_inlineItemRuns[runIndex]); ++runIndex)
- mergedRuns.append(m_inlineItemRuns[runIndex]);
- runList.append(mergedRuns.close());
- }
- return runList;
- };
-
- auto runList = mergedInlineItemRuns();
if (!m_isIntrinsicSizing) {
- for (auto& run : runList) {
+ for (auto& run : m_runs) {
adjustBaselineAndLineHeight(run);
run.setLogicalHeight(runContentHeight(run));
}
@@ -268,77 +120,21 @@
m_lineBox.setLogicalHeight(0_lu);
}
// Remove descent when all content is baseline aligned but none of them have descent.
- if (formattingContext().quirks().lineDescentNeedsCollapsing(runList)) {
+ if (formattingContext().quirks().lineDescentNeedsCollapsing(m_runs)) {
m_lineBox.shrinkVertically(m_lineBox.baseline().descent());
m_lineBox.resetDescent();
}
- alignContentVertically(runList);
- alignHorizontally(runList, hangingContent, isLastLineWithInlineContent);
+ alignContentVertically();
+ alignHorizontally(hangingContent, isLastLineWithInlineContent);
}
- return runList;
+ return WTFMove(m_runs);
}
-size_t LineBuilder::revert(const InlineItem& revertTo)
+void LineBuilder::alignContentVertically()
{
- if (m_inlineItemRuns.last() == revertTo) {
- // Since the LineBreaker does not know what has been pushed on the current line
- // in some cases revert() is called with the last item on the line.
- return { };
- }
- // 1. Remove and shrink the trailing content.
- // 2. Rebuild trimmable trailing whitespace content.
- ASSERT(!m_inlineItemRuns.isEmpty());
- auto revertedWidth = InlineLayoutUnit { };
- auto originalSize = m_inlineItemRuns.size();
- int64_t index = static_cast<int64_t>(originalSize - 1);
- while (index >= 0 && m_inlineItemRuns[index] != revertTo)
- revertedWidth += m_inlineItemRuns[index--].logicalWidth();
- m_lineBox.shrinkHorizontally(revertedWidth);
- m_inlineItemRuns.shrink(index + 1);
- // Should never need to clear the line.
- ASSERT(!m_inlineItemRuns.isEmpty());
-
- // It's easier just to rebuild trailing trimmable content.
- m_trimmableTrailingContent.reset();
- m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = isVisuallyEmpty();
- // Find the first trimmable run.
- Optional<size_t> firstTrimmableRunIndex;
- for (auto index = m_inlineItemRuns.size(); index--;) {
- auto& inlineItemRun = m_inlineItemRuns[index];
- if (inlineItemRun.isContainerStart() || inlineItemRun.isContainerEnd())
- continue;
- auto hasTrailingTrimmableContent = inlineItemRun.isTrimmableWhitespace() || inlineItemRun.hasTrailingLetterSpacing();
- if (!hasTrailingTrimmableContent)
- break;
- if (inlineItemRun.isTrimmableWhitespace()) {
- firstTrimmableRunIndex = index;
- continue;
- }
- if (inlineItemRun.hasTrailingLetterSpacing()) {
- // While trailing letter spacing is considered trimmable, it is supposed to be last one in the list.
- firstTrimmableRunIndex = index;
- break;
- }
- }
- // Forward-append runs to m_trimmableTrailingContent.
- if (firstTrimmableRunIndex) {
- for (auto index = *firstTrimmableRunIndex; index < m_inlineItemRuns.size(); ++index) {
- auto& inlineItemRun = m_inlineItemRuns[index];
- if (inlineItemRun.isContainerStart() || inlineItemRun.isContainerEnd())
- continue;
- ASSERT(inlineItemRun.isText());
- m_trimmableTrailingContent.append(index);
- }
- }
- // Consider alternative solutions if the (edge case)revert gets overly complicated.
- return originalSize - m_inlineItemRuns.size();
-}
-
-void LineBuilder::alignContentVertically(RunList& runList)
-{
ASSERT(!m_isIntrinsicSizing);
auto scrollableOverflowRect = m_lineBox.logicalRect();
- for (auto& run : runList) {
+ for (auto& run : m_runs) {
InlineLayoutUnit logicalTop = 0;
auto& layoutBox = run.layoutBox();
auto verticalAlign = layoutBox.style().verticalAlign();
@@ -393,16 +189,15 @@
m_lineBox.setScrollableOverflow(scrollableOverflowRect);
}
-void LineBuilder::justifyRuns(RunList& runList, InlineLayoutUnit availableWidth) const
+void LineBuilder::justifyRuns(InlineLayoutUnit availableWidth)
{
- ASSERT(!runList.isEmpty());
ASSERT(availableWidth > 0);
// Collect the expansion opportunity numbers and find the last run with content.
auto expansionOpportunityCount = 0;
Run* lastRunWithContent = nullptr;
- for (auto& run : runList) {
+ for (auto& run : m_runs) {
expansionOpportunityCount += run.expansionOpportunityCount();
- if ((run.isText() && !run.isCollapsedToVisuallyEmpty()) || run.isBox())
+ if (run.isText() || run.isBox())
lastRunWithContent = &run;
}
// Need to fix up the last run's trailing expansion.
@@ -417,7 +212,7 @@
// Distribute the extra space.
auto expansionToDistribute = availableWidth / expansionOpportunityCount;
InlineLayoutUnit accumulatedExpansion = 0;
- for (auto& run : runList) {
+ for (auto& run : m_runs) {
// Expand and moves runs by the accumulated expansion.
if (!run.hasExpansionOpportunity()) {
run.moveHorizontally(accumulatedExpansion);
@@ -431,17 +226,17 @@
}
}
-void LineBuilder::alignHorizontally(RunList& runList, const HangingContent& hangingContent, IsLastLineWithInlineContent lastLine)
+void LineBuilder::alignHorizontally(const HangingContent& hangingContent, IsLastLineWithInlineContent lastLine)
{
ASSERT(!m_isIntrinsicSizing);
auto availableWidth = this->availableWidth() + hangingContent.width();
- if (runList.isEmpty() || availableWidth <= 0)
+ if (m_runs.isEmpty() || availableWidth <= 0)
return;
if (isTextAlignJustify()) {
// Do not justify align the last line.
if (lastLine == IsLastLineWithInlineContent::No)
- justifyRuns(runList, availableWidth);
+ justifyRuns(availableWidth);
return;
}
@@ -474,18 +269,18 @@
// e.g. <div style="text-align: center; width: 100px;">centered text</div> : the line box will also be centered
// as opposed to start at 0px all the way to [centered text] run's right edge.
m_lineBox.moveHorizontally(*adjustment);
- for (auto& run : runList)
+ for (auto& run : m_runs)
run.moveHorizontally(*adjustment);
}
void LineBuilder::removeTrailingTrimmableContent()
{
- if (m_trimmableTrailingContent.isEmpty() || m_inlineItemRuns.isEmpty())
+ if (m_trimmableTrailingContent.isEmpty() || m_runs.isEmpty())
return;
// Complex line layout quirk: keep the trailing whitespace around when it is followed by a line break, unless the content overflows the line.
if (RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled()) {
- if (m_inlineItemRuns.last().isLineBreak() && availableWidth() >= 0 && !isTextAlignRight()) {
+ if (m_runs.last().isLineBreak() && availableWidth() >= 0 && !isTextAlignRight()) {
m_trimmableTrailingContent.reset();
return;
}
@@ -493,21 +288,21 @@
m_lineBox.shrinkHorizontally(m_trimmableTrailingContent.remove());
// If we removed the first visible run on the line, we need to re-check the visibility status.
- if (!m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent)
- return;
- // Just because the line was visually empty before the removed content, it does not necessarily mean it is still visually empty.
- // <span> </span><span style="padding-left: 10px"></span> <- non-empty
- auto lineIsVisuallyEmpty = [&] {
- for (auto& run : m_inlineItemRuns) {
- if (isVisuallyNonEmpty(run))
- return false;
- }
- return true;
- };
- // We could only go from visually non empty -> to visually empty. Trimmed runs should never make the line visible.
- if (lineIsVisuallyEmpty())
- m_lineBox.setIsConsideredEmpty();
- m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = { };
+ if (m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent) {
+ // Just because the line was visually empty before the removed content, it does not necessarily mean it is still visually empty.
+ // <span> </span><span style="padding-left: 10px"></span> <- non-empty
+ auto lineIsVisuallyEmpty = [&] {
+ for (auto& run : m_runs) {
+ if (isVisuallyNonEmpty(run))
+ return false;
+ }
+ return true;
+ };
+ // We could only go from visually non empty -> to visually empty. Trimmed runs should never make the line visible.
+ if (lineIsVisuallyEmpty())
+ m_lineBox.setIsConsideredEmpty();
+ m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = { };
+ }
}
void LineBuilder::visuallyCollapsePreWrapOverflowContent()
@@ -522,23 +317,27 @@
// Let's just find the trailing pre-wrap whitespace content for now (e.g check if there are multiple trailing runs with
// different set of white-space values and decide if the in-between pre-wrap content should be collapsed as well.)
InlineLayoutUnit trimmedContentWidth = 0;
- for (auto& inlineItemRun : WTF::makeReversedRange(m_inlineItemRuns)) {
- if (inlineItemRun.style().whiteSpace() != WhiteSpace::PreWrap) {
+ for (auto& run : WTF::makeReversedRange(m_runs)) {
+ if (run.style().whiteSpace() != WhiteSpace::PreWrap) {
// We are only interested in pre-wrap trailing content.
break;
}
- auto preWrapVisuallyCollapsibleInlineItem = inlineItemRun.isContainerStart() || inlineItemRun.isContainerEnd() || inlineItemRun.isWhitespace();
+ auto preWrapVisuallyCollapsibleInlineItem = run.isContainerStart() || run.isContainerEnd() || run.hasTrailingWhitespace();
if (!preWrapVisuallyCollapsibleInlineItem)
break;
- auto runLogicalWidth = inlineItemRun.logicalWidth();
- // Never partially collapse inline container start/end items.
- auto isPartialCollapsingAllowed = inlineItemRun.isText();
- // FIXME: We should always collapse the run at a glyph boundary as the spec indicates: "collapse the character advance widths of any that would otherwise overflow"
- auto runLogicalWidthAfterCollapsing = isPartialCollapsingAllowed ? std::max<InlineLayoutUnit>(0, runLogicalWidth - overflowWidth) : 0;
- auto trimmed = runLogicalWidth - runLogicalWidthAfterCollapsing;
- trimmedContentWidth += trimmed;
- overflowWidth -= trimmed;
- inlineItemRun.adjustLogicalWidth(runLogicalWidthAfterCollapsing);
+ ASSERT(!run.hasCollapsibleTrailingWhitespace());
+ InlineLayoutUnit trimmableWidth = { };
+ if (run.isText()) {
+ // FIXME: We should always collapse the run at a glyph boundary as the spec indicates: "collapse the character advance widths of any that would otherwise overflow"
+ // and the trimmed width should be capped at std::min(run.trailingWhitespaceWidth(), overflowWidth) for texgt runs. Both FF and Chrome agree.
+ trimmableWidth = run.trailingWhitespaceWidth();
+ run.visuallyCollapseTrailingWhitespace();
+ } else {
+ trimmableWidth = run.logicalWidth();
+ run.shrinkHorizontally(trimmableWidth);
+ }
+ trimmedContentWidth += trimmableWidth;
+ overflowWidth -= trimmableWidth;
if (overflowWidth <= 0)
break;
}
@@ -552,20 +351,20 @@
ASSERT(m_trimmableTrailingContent.isEmpty());
if (isLastLineWithInlineContent == IsLastLineWithInlineContent::Yes)
hangingContent.setIsConditional();
- for (auto& inlineItemRun : WTF::makeReversedRange(m_inlineItemRuns)) {
- if (inlineItemRun.isContainerStart() || inlineItemRun.isContainerEnd())
+ for (auto& run : WTF::makeReversedRange(m_runs)) {
+ if (run.isContainerStart() || run.isContainerEnd())
continue;
- if (inlineItemRun.isLineBreak()) {
+ if (run.isLineBreak()) {
hangingContent.setIsConditional();
continue;
}
- if (!inlineItemRun.isText() || !inlineItemRun.isWhitespace() || inlineItemRun.isCollapsible())
+ if (!run.hasTrailingWhitespace())
break;
// Check if we have a preserved or hung whitespace.
- if (inlineItemRun.style().whiteSpace() != WhiteSpace::PreWrap)
+ if (run.style().whiteSpace() != WhiteSpace::PreWrap)
break;
// This is either a normal or conditionally hanging trailing whitespace.
- hangingContent.expand(inlineItemRun.logicalWidth());
+ hangingContent.expand(run.trailingWhitespaceWidth());
}
return hangingContent;
}
@@ -603,14 +402,13 @@
ASSERT_NOT_REACHED();
// Check if this freshly appended content makes the line visually non-empty.
- ASSERT(!m_inlineItemRuns.isEmpty());
- if (m_lineBox.isConsideredEmpty() && isVisuallyNonEmpty(m_inlineItemRuns.last()))
+ if (m_lineBox.isConsideredEmpty() && !m_runs.isEmpty() && isVisuallyNonEmpty(m_runs.last()))
m_lineBox.setIsConsideredNonEmpty();
}
void LineBuilder::appendNonBreakableSpace(const InlineItem& inlineItem, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth)
{
- m_inlineItemRuns.append({ inlineItem, logicalLeft, logicalWidth });
+ m_runs.append({ inlineItem, logicalLeft, logicalWidth });
m_lineBox.expandHorizontally(logicalWidth);
}
@@ -626,7 +424,7 @@
auto removeTrailingLetterSpacing = [&] {
if (!m_trimmableTrailingContent.isTrailingRunPartiallyTrimmable())
return;
- m_lineBox.shrinkHorizontally(m_trimmableTrailingContent.removeTrailingRun());
+ m_lineBox.shrinkHorizontally(m_trimmableTrailingContent.removePartiallyTrimmableContent());
};
// Prevent trailing letter-spacing from spilling out of the inline container.
// https://drafts.csswg.org/css-text-3/#letter-spacing-property See example 21.
@@ -634,14 +432,15 @@
appendNonBreakableSpace(inlineItem, contentLogicalRight(), logicalWidth);
}
-void LineBuilder::appendTextContent(const InlineTextItem& inlineItem, InlineLayoutUnit logicalWidth)
+void LineBuilder::appendTextContent(const InlineTextItem& inlineTextItem, InlineLayoutUnit logicalWidth)
{
- auto isCollapsible = inlineItem.isCollapsible();
auto willCollapseCompletely = [&] {
- if (!isCollapsible)
+ if (!inlineTextItem.isCollapsible())
return false;
+ if (inlineTextItem.isEmptyContent())
+ return true;
// Check if the last item is collapsed as well.
- for (auto& run : WTF::makeReversedRange(m_inlineItemRuns)) {
+ for (auto& run : WTF::makeReversedRange(m_runs)) {
if (run.isBox())
return false;
// https://drafts.csswg.org/css-text-3/#white-space-phase-1
@@ -650,31 +449,40 @@
// : "<span> </span> " <- the trailing whitespace collapses completely.
// Not that when the inline container has preserve whitespace style, "<span style="white-space: pre"> </span> " <- this whitespace stays around.
if (run.isText())
- return run.isCollapsible();
+ return run.hasCollapsibleTrailingWhitespace();
ASSERT(run.isContainerStart() || run.isContainerEnd());
}
// Leading whitespace.
- return !shouldPreserveLeadingContent(inlineItem);
+ return !isWhitespacePreserved(inlineTextItem.style());
};
- auto collapsesToZeroAdvanceWidth = willCollapseCompletely();
- logicalWidth = collapsesToZeroAdvanceWidth ? 0 : logicalWidth;
- auto collapsedRun = isCollapsible && inlineItem.length() > 1;
- auto contentLength = collapsedRun ? 1 : inlineItem.length();
- m_inlineItemRuns.append({ inlineItem, contentLogicalWidth(), logicalWidth, collapsedRun, collapsesToZeroAdvanceWidth, Display::Run::TextContext { inlineItem.start(), contentLength, inlineItem.layoutBox().textContext()->content } });
+ if (willCollapseCompletely())
+ return;
+
+ auto inlineTextItemNeedsNewRun = true;
+ if (!m_runs.isEmpty()) {
+ auto& lastRun = m_runs.last();
+ inlineTextItemNeedsNewRun = lastRun.hasCollapsedTrailingWhitespace() || !lastRun.isText() || &lastRun.layoutBox() != &inlineTextItem.layoutBox();
+ if (!inlineTextItemNeedsNewRun)
+ lastRun.expand(inlineTextItem, logicalWidth);
+ }
+ if (inlineTextItemNeedsNewRun)
+ m_runs.append({ inlineTextItem, contentLogicalWidth(), logicalWidth });
+
m_lineBox.expandHorizontally(logicalWidth);
- if (isCollapsible && !TextUtil::shouldPreserveTrailingWhitespace(inlineItem.style())) {
- // If we ever collapse this content, we need to know if the line visibility state needs to be recomputed.
+ // Set the trailing trimmable content.
+ if (inlineTextItem.isWhitespace() && !TextUtil::shouldPreserveTrailingWhitespace(inlineTextItem.style())) {
+ m_trimmableTrailingContent.addFullyTrimmableContent(m_runs.size() - 1, logicalWidth);
+ // If we ever trim this content, we need to know if the line visibility state needs to be recomputed.
if (m_trimmableTrailingContent.isEmpty())
m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = isVisuallyEmpty();
- m_trimmableTrailingContent.append(m_inlineItemRuns.size() - 1);
- } else {
- // Existing trailing collapsible content can only be expanded if the current run is fully collapsible.
- m_trimmableTrailingContent.reset();
- if (!m_shouldIgnoreTrailingLetterSpacing && !inlineItem.isWhitespace() && inlineItem.style().letterSpacing() > 0)
- m_trimmableTrailingContent.append(m_inlineItemRuns.size() - 1);
+ return;
}
+ // Any non-whitespace, no-trimmable content resets the existing trimmable.
+ m_trimmableTrailingContent.reset();
+ if (!m_shouldIgnoreTrailingLetterSpacing && !inlineTextItem.isWhitespace() && inlineTextItem.style().letterSpacing() > 0)
+ m_trimmableTrailingContent.addPartiallyTrimmableContent(m_runs.size() - 1, logicalWidth);
}
void LineBuilder::appendNonReplacedInlineBox(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)
@@ -682,7 +490,7 @@
auto& layoutBox = inlineItem.layoutBox();
auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
auto horizontalMargin = boxGeometry.horizontalMargin();
- m_inlineItemRuns.append({ inlineItem, contentLogicalWidth() + horizontalMargin.start, logicalWidth });
+ m_runs.append({ inlineItem, contentLogicalWidth() + horizontalMargin.start, logicalWidth });
m_lineBox.expandHorizontally(logicalWidth + horizontalMargin.start + horizontalMargin.end);
m_trimmableTrailingContent.reset();
}
@@ -697,11 +505,10 @@
void LineBuilder::appendLineBreak(const InlineItem& inlineItem)
{
if (inlineItem.isHardLineBreak())
- return m_inlineItemRuns.append({ inlineItem, contentLogicalWidth(), 0_lu });
+ return m_runs.append({ inlineItem, contentLogicalWidth(), 0_lu });
// Soft line breaks (preserved new line characters) require inline text boxes for compatibility reasons.
ASSERT(inlineItem.isSoftLineBreak());
- auto& softLineBreakItem = downcast<InlineSoftLineBreakItem>(inlineItem);
- m_inlineItemRuns.append({ softLineBreakItem, contentLogicalWidth(), 0_lu, false, false, Display::Run::TextContext { softLineBreakItem.position(), 1, softLineBreakItem.layoutBox().textContext()->content } });
+ m_runs.append({ downcast<InlineSoftLineBreakItem>(inlineItem), contentLogicalWidth() });
}
void LineBuilder::adjustBaselineAndLineHeight(const Run& run)
@@ -810,11 +617,14 @@
return boxGeometry.marginBoxHeight();
}
-bool LineBuilder::isVisuallyNonEmpty(const InlineItemRun& run) const
+bool LineBuilder::isVisuallyNonEmpty(const Run& run) const
{
if (run.isText())
- return !run.hasEmptyTextContent();
+ return true;
+ if (run.isLineBreak())
+ return true;
+
// Note that this does not check whether the inline container has content. It simply checks if the container itself is considered non-empty.
if (run.isContainerStart() || run.isContainerEnd()) {
if (!run.logicalWidth())
@@ -826,9 +636,6 @@
return boxGeometry.borderRight() || (boxGeometry.paddingRight() && boxGeometry.paddingRight().value());
}
- if (run.isLineBreak())
- return true;
-
if (run.isBox()) {
if (!run.layoutBox().establishesFormattingContext())
return true;
@@ -866,130 +673,127 @@
return m_inlineFormattingContext;
}
-LineBuilder::TrimmableTrailingContent::TrimmableTrailingContent(InlineItemRunList& inlineItemRunList)
- : m_inlineitemRunList(inlineItemRunList)
+LineBuilder::TrimmableTrailingContent::TrimmableTrailingContent(RunList& runs)
+ : m_runs(runs)
{
}
-void LineBuilder::TrimmableTrailingContent::append(size_t runIndex)
+void LineBuilder::TrimmableTrailingContent::addFullyTrimmableContent(size_t runIndex, InlineLayoutUnit trimmableWidth)
{
- auto& trimmableRun = m_inlineitemRunList[runIndex];
- InlineLayoutUnit trimmableWidth = 0;
- auto isFullyTrimmable = trimmableRun.isTrimmableWhitespace();
- if (isFullyTrimmable)
- trimmableWidth = trimmableRun.logicalWidth();
- else {
- ASSERT(trimmableRun.hasTrailingLetterSpacing());
- trimmableWidth = trimmableRun.trailingLetterSpacing();
- }
- m_width += trimmableWidth;
- m_lastRunIsFullyTrimmable = isFullyTrimmable;
+ // Any subsequent trimmable whitespace should collapse to zero advanced width and ignored at ::appendTextContent().
+ ASSERT(!m_hasFullyTrimmableContent);
+ m_fullyTrimmableWidth = trimmableWidth;
+ // Note that just becasue the trimmable width is 0 (font-size: 0px), it does not mean we don't have a trimmable trailing content.
+ m_hasFullyTrimmableContent = true;
m_firstRunIndex = m_firstRunIndex.valueOr(runIndex);
}
+void LineBuilder::TrimmableTrailingContent::addPartiallyTrimmableContent(size_t runIndex, InlineLayoutUnit trimmableWidth)
+{
+ // Do not add trimmable letter spacing after a fully trimmable whitesapce.
+ ASSERT(!m_firstRunIndex);
+ ASSERT(!m_hasFullyTrimmableContent);
+ ASSERT(!m_partiallyTrimmableWidth);
+ ASSERT(trimmableWidth);
+ m_partiallyTrimmableWidth = trimmableWidth;
+ m_firstRunIndex = runIndex;
+}
+
InlineLayoutUnit LineBuilder::TrimmableTrailingContent::remove()
{
+ // Remove trimmable trailing content and move all the subsequent trailing runs.
+ // <span> </span><span></span>
+ // [trailing whitespace][container end][container start][container end]
+ // Trim the whitespace run and move the trailing inline container runs to the logical left.
ASSERT(!isEmpty());
-#if ASSERT_ENABLED
- auto hasSeenNonWhitespaceTextContent = false;
-#endif
- // Remove trimmable trailing content and move all the other trailing runs.
- // <span> </span><span></span> ->
- // [whitespace][container end][container start][container end]
- // Remove the whitespace run and move the trailing inline container runs to the left.
- InlineLayoutUnit accumulatedTrimmedWidth = 0;
- for (auto index = *m_firstRunIndex; index < m_inlineitemRunList.size(); ++index) {
- auto& run = m_inlineitemRunList[index];
- run.moveHorizontally(-accumulatedTrimmedWidth);
- if (!run.isText()) {
- ASSERT(run.isContainerStart() || run.isContainerEnd() || run.isLineBreak());
- continue;
- }
- if (run.isWhitespace()) {
- accumulatedTrimmedWidth += run.logicalWidth();
- run.setCollapsesToZeroAdvanceWidth();
- } else {
- ASSERT(!hasSeenNonWhitespaceTextContent);
-#if ASSERT_ENABLED
- hasSeenNonWhitespaceTextContent = true;
-#endif
- // Must be a letter spacing trimming.
- ASSERT(run.hasTrailingLetterSpacing());
- accumulatedTrimmedWidth += run.trailingLetterSpacing();
- run.removeTrailingLetterSpacing();
- }
+ auto& trimmableRun = m_runs[*m_firstRunIndex];
+ ASSERT(trimmableRun.isText());
+
+ if (m_hasFullyTrimmableContent)
+ trimmableRun.removeTrailingWhitespace();
+ if (m_partiallyTrimmableWidth)
+ trimmableRun.removeTrailingLetterSpacing();
+
+ auto trimmableWidth = width();
+ for (auto index = *m_firstRunIndex + 1; index < m_runs.size(); ++index) {
+ auto& run = m_runs[index];
+ ASSERT(run.isContainerStart() || run.isContainerEnd() || run.isLineBreak());
+ run.moveHorizontally(-trimmableWidth);
}
- ASSERT(accumulatedTrimmedWidth == width());
reset();
- return accumulatedTrimmedWidth;
+ return trimmableWidth;
}
-InlineLayoutUnit LineBuilder::TrimmableTrailingContent::removeTrailingRun()
+InlineLayoutUnit LineBuilder::TrimmableTrailingContent::removePartiallyTrimmableContent()
{
- ASSERT(!isEmpty());
- // Find the last trimmable run (it is not necessarily the last run e.g [container start][whitespace][container end])
- for (auto index = m_inlineitemRunList.size(); index-- && *m_firstRunIndex >= index;) {
- auto& run = m_inlineitemRunList[index];
- if (!run.isText()) {
- ASSERT(run.isContainerStart() || run.isContainerEnd());
- continue;
- }
- InlineLayoutUnit trimmedWidth = 0;
- if (run.isWhitespace()) {
- trimmedWidth = run.logicalWidth();
- run.setCollapsesToZeroAdvanceWidth();
- } else {
- ASSERT(run.hasTrailingLetterSpacing());
- trimmedWidth = run.trailingLetterSpacing();
- run.removeTrailingLetterSpacing();
- }
- m_width -= trimmedWidth;
- // We managed to remove the last trimmable run.
- if (index == *m_firstRunIndex) {
- ASSERT(!m_width);
- m_firstRunIndex = { };
- }
- return trimmedWidth;
- }
- ASSERT_NOT_REACHED();
- return 0_lu;
+ // Partially trimmable content is always gated by a fully trimmable content.
+ // We can't just trim spacing in the middle.
+ ASSERT(!m_fullyTrimmableWidth);
+ return remove();
}
-LineBuilder::InlineItemRun::InlineItemRun(const InlineItem& inlineItem, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth)
- : m_inlineItem(inlineItem)
- , m_logicalLeft(logicalLeft)
- , m_logicalWidth(logicalWidth)
+LineBuilder::Run::Run(const InlineItem& inlineItem, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth)
+ : m_type(inlineItem.type())
+ , m_layoutBox(&inlineItem.layoutBox())
+ , m_logicalRect({ 0, logicalLeft, logicalWidth, 0 })
{
}
-LineBuilder::InlineItemRun::InlineItemRun(const InlineItem& inlineItem, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth, bool isCollapsed, bool isCollapsedToZeroAdvanceWidth, Display::Run::TextContext&& textContext)
- : m_inlineItem(inlineItem)
- , m_logicalLeft(logicalLeft)
- , m_logicalWidth(logicalWidth)
- , m_textContext(WTFMove(textContext))
- , m_isCollapsed(isCollapsed)
- , m_collapsedToZeroAdvanceWidth(isCollapsedToZeroAdvanceWidth)
+LineBuilder::Run::Run(const InlineSoftLineBreakItem& softLineBreakItem, InlineLayoutUnit logicalLeft)
+ : m_type(softLineBreakItem.type())
+ , m_layoutBox(&softLineBreakItem.layoutBox())
+ , m_logicalRect({ 0, logicalLeft, 0, 0 })
+ , m_textContext({ softLineBreakItem.position(), 1, softLineBreakItem.layoutBox().textContext()->content })
{
}
-bool LineBuilder::InlineItemRun::isTrimmableWhitespace() const
+LineBuilder::Run::Run(const InlineTextItem& inlineTextItem, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth)
+ : m_type(InlineItem::Type::Text)
+ , m_layoutBox(&inlineTextItem.layoutBox())
+ , m_logicalRect({ 0, logicalLeft, logicalWidth, 0 })
+ , m_trailingWhitespaceType(trailingWhitespaceType(inlineTextItem))
+ , m_textContext({ inlineTextItem.start(), m_trailingWhitespaceType == TrailingWhitespace::Collapsed ? 1 : inlineTextItem.length(), inlineTextItem.layoutBox().textContext()->content })
{
- // Return true if the "end-of-line spaces" can be removed.
- // See https://www.w3.org/TR/css-text-3/#white-space-property matrix.
- if (!isWhitespace())
- return false;
- return !TextUtil::shouldPreserveTrailingWhitespace(style());
+ if (m_trailingWhitespaceType != TrailingWhitespace::None) {
+ m_trailingWhitespaceWidth = logicalWidth;
+ m_textContext->setExpansion({ ExpansionBehavior(DefaultExpansion), { } });
+ if (!isWhitespacePreserved(inlineTextItem.style()))
+ m_expansionOpportunityCount = 1;
+ }
}
-bool LineBuilder::InlineItemRun::hasTrailingLetterSpacing() const
+void LineBuilder::Run::expand(const InlineTextItem& inlineTextItem, InlineLayoutUnit logicalWidth)
{
+ // FIXME: This is a very simple expansion merge. We should eventually switch over to FontCascade::expansionOpportunityCount.
+ ASSERT(!hasCollapsedTrailingWhitespace());
+ ASSERT(isText() && inlineTextItem.isText());
+ ASSERT(m_layoutBox == &inlineTextItem.layoutBox());
+
+ m_logicalRect.expandHorizontally(logicalWidth);
+ m_trailingWhitespaceType = trailingWhitespaceType(inlineTextItem);
+
+ if (m_trailingWhitespaceType == TrailingWhitespace::None) {
+ m_trailingWhitespaceWidth = { };
+ m_textContext->setExpansion({ ExpansionBehavior(AllowLeadingExpansion | AllowTrailingExpansion), { } });
+ m_textContext->expand(inlineTextItem.length());
+ return;
+ }
+ m_trailingWhitespaceWidth += logicalWidth;
+ if (!isWhitespacePreserved(inlineTextItem.style()))
+ ++m_expansionOpportunityCount;
+ m_textContext->setExpansion({ ExpansionBehavior(DefaultExpansion), { } });
+ m_textContext->expand(m_trailingWhitespaceType == TrailingWhitespace::Collapsed ? 1 : inlineTextItem.length());
+}
+
+bool LineBuilder::Run::hasTrailingLetterSpacing() const
+{
// Complex line layout does not keep track of trailing letter spacing.
if (RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled())
return false;
- return !isWhitespace() && style().letterSpacing() > 0;
+ return !hasTrailingWhitespace() && style().letterSpacing() > 0;
}
-InlineLayoutUnit LineBuilder::InlineItemRun::trailingLetterSpacing() const
+InlineLayoutUnit LineBuilder::Run::trailingLetterSpacing() const
{
if (!hasTrailingLetterSpacing())
return 0_lu;
@@ -996,26 +800,60 @@
return InlineLayoutUnit { style().letterSpacing() };
}
-void LineBuilder::InlineItemRun::setCollapsesToZeroAdvanceWidth()
+void LineBuilder::Run::removeTrailingLetterSpacing()
{
- m_collapsedToZeroAdvanceWidth = true;
- m_logicalWidth = 0_lu;
+ ASSERT(hasTrailingLetterSpacing());
+ shrinkHorizontally(trailingLetterSpacing());
+ ASSERT(logicalWidth() > 0 || (!logicalWidth() && style().letterSpacing() >= intMaxForLayoutUnit));
}
-void LineBuilder::InlineItemRun::removeTrailingLetterSpacing()
+void LineBuilder::Run::removeTrailingWhitespace()
{
- ASSERT(hasTrailingLetterSpacing());
- m_logicalWidth -= trailingLetterSpacing();
- ASSERT(m_logicalWidth > 0 || (!m_logicalWidth && style().letterSpacing() >= intMaxForLayoutUnit));
+ // According to https://www.w3.org/TR/css-text-3/#white-space-property matrix
+ // Trimmable whitespace is always collapsable so the length of the trailing trimmable whitespace is always 1 (or non-existent).
+ ASSERT(m_textContext->length());
+ m_textContext->expand(-1);
+ visuallyCollapseTrailingWhitespace();
}
-bool LineBuilder::InlineItemRun::hasEmptyTextContent() const
+void LineBuilder::Run::visuallyCollapseTrailingWhitespace()
{
+ // This is just a visual adjustment, the text length should remain the same.
+ shrinkHorizontally(m_trailingWhitespaceWidth);
+ m_trailingWhitespaceWidth = { };
+ m_trailingWhitespaceType = TrailingWhitespace::None;
+
+ if (!isWhitespacePreserved(style())) {
+ ASSERT(m_expansionOpportunityCount);
+ m_expansionOpportunityCount--;
+ }
+ m_textContext->setExpansion({ ExpansionBehavior(AllowLeadingExpansion | AllowTrailingExpansion), { } });
+}
+
+void LineBuilder::Run::adjustExpansionBehavior(ExpansionBehavior expansionBehavior)
+{
ASSERT(isText());
- return isCollapsedToZeroAdvanceWidth() || downcast<InlineTextItem>(m_inlineItem).isEmptyContent();
+ ASSERT(hasExpansionOpportunity());
+ m_textContext->setExpansion({ expansionBehavior, m_textContext->expansion()->horizontalExpansion });
}
+inline Optional<ExpansionBehavior> LineBuilder::Run::expansionBehavior() const
+{
+ ASSERT(isText());
+ if (auto expansionContext = m_textContext->expansion())
+ return expansionContext->behavior;
+ return { };
}
+
+void LineBuilder::Run::setComputedHorizontalExpansion(InlineLayoutUnit logicalExpansion)
+{
+ ASSERT(isText());
+ ASSERT(hasExpansionOpportunity());
+ m_logicalRect.expandHorizontally(logicalExpansion);
+ m_textContext->setExpansion({ m_textContext->expansion()->behavior, logicalExpansion });
}
+}
+}
+
#endif
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h (254804 => 254805)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h 2020-01-19 15:19:00 UTC (rev 254804)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h 2020-01-19 16:02:01 UTC (rev 254805)
@@ -37,9 +37,9 @@
struct HangingContent;
class InlineFormattingContext;
+class InlineSoftLineBreakItem;
class LineBuilder {
- class InlineItemRun;
struct ContinuousContent;
public:
@@ -74,11 +74,6 @@
void setHasIntrusiveFloat() { m_hasIntrusiveFloat = true; }
struct Run {
- Run(const InlineItemRun&);
- Run(const InlineItemRun&, const Display::InlineRect&, const Display::Run::TextContext&, unsigned expansionOpportunityCount);
- Run(Run&&) = default;
- Run& operator=(Run&& other) = default;
-
bool isText() const { return m_type == InlineItem::Type::Text; }
bool isBox() const { return m_type == InlineItem::Type::Box; }
bool isLineBreak() const { return m_type == InlineItem::Type::HardLineBreak || m_type == InlineItem::Type::SoftLineBreak; }
@@ -89,13 +84,25 @@
const RenderStyle& style() const { return m_layoutBox->style(); }
const Display::InlineRect& logicalRect() const { return m_logicalRect; }
const Optional<Display::Run::TextContext>& textContext() const { return m_textContext; }
- bool isCollapsedToVisuallyEmpty() const { return m_isCollapsedToVisuallyEmpty; }
+ Run(Run&&) = default;
+ Run& operator=(Run&& other) = default;
+
private:
friend class LineBuilder;
+ Run(const InlineTextItem&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth);
+ Run(const InlineSoftLineBreakItem&, InlineLayoutUnit logicalLeft);
+ Run(const InlineItem&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth);
+
+ void expand(const InlineTextItem&, InlineLayoutUnit logicalWidth);
+
+ InlineLayoutUnit logicalWidth() const { return m_logicalRect.width(); }
+
+ void moveHorizontally(InlineLayoutUnit offset) { m_logicalRect.moveHorizontally(offset); }
+ void shrinkHorizontally(InlineLayoutUnit width) { m_logicalRect.expandHorizontally(-width); }
+
void adjustLogicalTop(InlineLayoutUnit logicalTop) { m_logicalRect.setTop(logicalTop); }
- void moveHorizontally(InlineLayoutUnit offset) { m_logicalRect.moveHorizontally(offset); }
void moveVertically(InlineLayoutUnit offset) { m_logicalRect.moveVertically(offset); }
void setLogicalHeight(InlineLayoutUnit logicalHeight) { m_logicalRect.setHeight(logicalHeight); }
@@ -105,17 +112,35 @@
void setComputedHorizontalExpansion(InlineLayoutUnit logicalExpansion);
void adjustExpansionBehavior(ExpansionBehavior);
+ enum class TrailingWhitespace {
+ None,
+ NotCollapsible,
+ Collapsible,
+ Collapsed
+ };
+ bool hasTrailingWhitespace() const { return m_trailingWhitespaceType != TrailingWhitespace::None; }
+ bool hasCollapsibleTrailingWhitespace() const { return m_trailingWhitespaceType == TrailingWhitespace::Collapsible || hasCollapsedTrailingWhitespace(); }
+ bool hasCollapsedTrailingWhitespace() const { return m_trailingWhitespaceType == TrailingWhitespace::Collapsed; }
+ InlineLayoutUnit trailingWhitespaceWidth() const { return m_trailingWhitespaceWidth; }
+ TrailingWhitespace trailingWhitespaceType(const InlineTextItem&) const;
+ void removeTrailingWhitespace();
+ void visuallyCollapseTrailingWhitespace();
+
+ bool hasTrailingLetterSpacing() const;
+ InlineLayoutUnit trailingLetterSpacing() const;
+ void removeTrailingLetterSpacing();
+
+ InlineItem::Type m_type { InlineItem::Type::Text };
const Box* m_layoutBox { nullptr };
- InlineItem::Type m_type;
Display::InlineRect m_logicalRect;
+ TrailingWhitespace m_trailingWhitespaceType { TrailingWhitespace::None };
+ InlineLayoutUnit m_trailingWhitespaceWidth { 0 };
Optional<Display::Run::TextContext> m_textContext;
unsigned m_expansionOpportunityCount { 0 };
- bool m_isCollapsedToVisuallyEmpty { false };
};
- using RunList = Vector<Run, 50>;
+ using RunList = Vector<Run, 10>;
enum class IsLastLineWithInlineContent { No, Yes };
RunList close(IsLastLineWithInlineContent = IsLastLineWithInlineContent::No);
- size_t revert(const InlineItem& revertTo);
static Display::LineBox::Baseline halfLeadingMetrics(const FontMetrics&, InlineLayoutUnit lineLogicalHeight);
@@ -144,8 +169,8 @@
void removeTrailingTrimmableContent();
void visuallyCollapsePreWrapOverflowContent();
HangingContent collectHangingContent(IsLastLineWithInlineContent);
- void alignHorizontally(RunList&, const HangingContent&, IsLastLineWithInlineContent);
- void alignContentVertically(RunList&);
+ void alignHorizontally(const HangingContent&, IsLastLineWithInlineContent);
+ void alignContentVertically();
void adjustBaselineAndLineHeight(const Run&);
InlineLayoutUnit runContentHeight(const Run&) const;
@@ -152,88 +177,38 @@
bool isTextAlignJustify() const { return m_horizontalAlignment == TextAlignMode::Justify; };
bool isTextAlignRight() const { return m_horizontalAlignment == TextAlignMode::Right || m_horizontalAlignment == TextAlignMode::WebKitRight || m_horizontalAlignment == TextAlignMode::End; }
- void justifyRuns(RunList&, InlineLayoutUnit availableWidth) const;
+ void justifyRuns(InlineLayoutUnit availableWidth);
- bool isVisuallyNonEmpty(const InlineItemRun&) const;
+ bool isVisuallyNonEmpty(const Run&) const;
LayoutState& layoutState() const;
const InlineFormattingContext& formattingContext() const;
- class InlineItemRun {
- public:
- InlineItemRun(const InlineItem&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth, bool isCollapsed, bool isCollapsedToZeroAdvanceWidth, Display::Run::TextContext&&);
- InlineItemRun(const InlineItem&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth);
-
- const Box& layoutBox() const { return m_inlineItem.layoutBox(); }
- const RenderStyle& style() const { return layoutBox().style(); }
- InlineLayoutUnit logicalLeft() const { return m_logicalLeft; }
- InlineLayoutUnit logicalWidth() const { return m_logicalWidth; }
- const Optional<Display::Run::TextContext>& textContext() const { return m_textContext; }
-
- bool isText() const { return m_inlineItem.isText(); }
- bool isBox() const { return m_inlineItem.isBox(); }
- bool isContainerStart() const { return m_inlineItem.isContainerStart(); }
- bool isContainerEnd() const { return m_inlineItem.isContainerEnd(); }
- bool isLineBreak() const { return m_inlineItem.isLineBreak(); }
- InlineItem::Type type() const { return m_inlineItem.type(); }
-
- bool isCollapsed() const { return m_isCollapsed; }
-
- void moveHorizontally(InlineLayoutUnit offset) { m_logicalLeft += offset; }
- void adjustLogicalWidth(InlineLayoutUnit adjustedWidth) { m_logicalWidth = adjustedWidth; }
-
- bool isTrimmableWhitespace() const;
- bool hasTrailingLetterSpacing() const;
-
- InlineLayoutUnit trailingLetterSpacing() const;
- void removeTrailingLetterSpacing();
-
- void setCollapsesToZeroAdvanceWidth();
- bool isCollapsedToZeroAdvanceWidth() const { return m_collapsedToZeroAdvanceWidth; }
-
- bool isCollapsible() const { return is<InlineTextItem>(m_inlineItem) && downcast<InlineTextItem>(m_inlineItem).isCollapsible(); }
- bool isWhitespace() const { return is<InlineTextItem>(m_inlineItem) && downcast<InlineTextItem>(m_inlineItem).isWhitespace(); }
- bool hasEmptyTextContent() const;
-
- bool hasExpansionOpportunity() const { return isWhitespace() && !isCollapsedToZeroAdvanceWidth(); }
-
- bool operator==(const InlineItem& other) const { return &other == &m_inlineItem; }
- bool operator!=(const InlineItem& other) const { return !(*this == other); }
-
- private:
- const InlineItem& m_inlineItem;
- InlineLayoutUnit m_logicalLeft { 0 };
- InlineLayoutUnit m_logicalWidth { 0 };
- const Optional<Display::Run::TextContext> m_textContext;
- bool m_isCollapsed { false };
- bool m_collapsedToZeroAdvanceWidth { false };
- };
-
- using InlineItemRunList = Vector<InlineItemRun, 50>;
-
struct TrimmableTrailingContent {
- TrimmableTrailingContent(InlineItemRunList&);
+ TrimmableTrailingContent(RunList&);
- void append(size_t runIndex);
+ void addFullyTrimmableContent(size_t runIndex, InlineLayoutUnit trimmableWidth);
+ void addPartiallyTrimmableContent(size_t runIndex, InlineLayoutUnit trimmableWidth);
InlineLayoutUnit remove();
- InlineLayoutUnit removeTrailingRun();
- void reset();
+ InlineLayoutUnit removePartiallyTrimmableContent();
- InlineLayoutUnit width() const { return m_width; }
- Optional<size_t> firstRunIndex() { return m_firstRunIndex; }
+ InlineLayoutUnit width() const { return m_fullyTrimmableWidth + m_partiallyTrimmableWidth; }
bool isEmpty() const { return !m_firstRunIndex.hasValue(); }
- bool isTrailingRunFullyTrimmable() const { return m_lastRunIsFullyTrimmable; }
- bool isTrailingRunPartiallyTrimmable() const { return !isEmpty() && !isTrailingRunFullyTrimmable(); }
+ bool isTrailingRunFullyTrimmable() const { return m_hasFullyTrimmableContent; }
+ bool isTrailingRunPartiallyTrimmable() const { return m_partiallyTrimmableWidth; }
+ void reset();
+
private:
- InlineItemRunList& m_inlineitemRunList;
+ RunList& m_runs;
Optional<size_t> m_firstRunIndex;
- InlineLayoutUnit m_width { 0 };
- bool m_lastRunIsFullyTrimmable { false };
+ bool m_hasFullyTrimmableContent { false };
+ InlineLayoutUnit m_fullyTrimmableWidth { 0 };
+ InlineLayoutUnit m_partiallyTrimmableWidth { 0 };
};
const InlineFormattingContext& m_inlineFormattingContext;
- InlineItemRunList m_inlineItemRuns;
+ RunList m_runs;
TrimmableTrailingContent m_trimmableTrailingContent;
Optional<Display::LineBox::Baseline> m_initialStrut;
InlineLayoutUnit m_lineLogicalWidth { 0 };
@@ -247,11 +222,23 @@
inline void LineBuilder::TrimmableTrailingContent::reset()
{
+ m_hasFullyTrimmableContent = false;
m_firstRunIndex = { };
- m_width = 0_lu;
- m_lastRunIsFullyTrimmable = false;
+ m_fullyTrimmableWidth = { };
+ m_partiallyTrimmableWidth = { };
}
+inline LineBuilder::Run::TrailingWhitespace LineBuilder::Run::trailingWhitespaceType(const InlineTextItem& inlineTextItem) const
+{
+ if (!inlineTextItem.isWhitespace())
+ return TrailingWhitespace::None;
+ if (!inlineTextItem.isCollapsible())
+ return TrailingWhitespace::NotCollapsible;
+ if (inlineTextItem.length() == 1)
+ return TrailingWhitespace::Collapsible;
+ return TrailingWhitespace::Collapsed;
}
+
}
+}
#endif