Title: [254805] trunk/Source/WebCore
Revision
254805
Author
[email protected]
Date
2020-01-19 08:02:01 -0800 (Sun, 19 Jan 2020)

Log Message

[LFC][IFC] Move away from placing individual InlineItems on the line
https://bugs.webkit.org/show_bug.cgi?id=206479
<rdar://problem/58716223>

Reviewed by Antti Koivisto.

<div> text content and  more    </div>
[ ][text][ ][content][ ][and][  ]more[   ]

Instead of placing all these ^^ inline items on the line (LineBuilder::InlineItemRun) and merge them at InlineBuilder::close(),
let's expand existing runs(LineBuilder::Run) when possible ([text content and ][more]).
This helps performance and memory consumption when the line ends up having many inline itmes (e.g. white-space: nowrap).

* layout/displaytree/DisplayRun.h:
(WebCore::Display::Run::TextContext::expand):
* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
* layout/inlineformatting/InlineFormattingContextQuirks.cpp:
(WebCore::Layout::InlineFormattingContext::Quirks::lineDescentNeedsCollapsing const):
* layout/inlineformatting/InlineLineBuilder.cpp:
(WebCore::Layout::LineBuilder::LineBuilder):
(WebCore::Layout::LineBuilder::initialize):
(WebCore::Layout::LineBuilder::close):
(WebCore::Layout::LineBuilder::alignContentVertically):
(WebCore::Layout::LineBuilder::justifyRuns):
(WebCore::Layout::LineBuilder::alignHorizontally):
(WebCore::Layout::LineBuilder::removeTrailingTrimmableContent):
(WebCore::Layout::LineBuilder::visuallyCollapsePreWrapOverflowContent):
(WebCore::Layout::LineBuilder::collectHangingContent):
(WebCore::Layout::LineBuilder::append):
(WebCore::Layout::LineBuilder::appendNonBreakableSpace):
(WebCore::Layout::LineBuilder::appendInlineContainerEnd):
(WebCore::Layout::LineBuilder::appendTextContent):
(WebCore::Layout::LineBuilder::appendNonReplacedInlineBox):
(WebCore::Layout::LineBuilder::appendLineBreak):
(WebCore::Layout::LineBuilder::isVisuallyNonEmpty const):
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::TrimmableTrailingContent):
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::addFullyTrimmableContent):
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::addPartiallyTrimmableContent):
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::remove):
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::removePartiallyTrimmableContent):
(WebCore::Layout::LineBuilder::Run::Run):
(WebCore::Layout::m_textContext):
(WebCore::Layout::LineBuilder::Run::expand):
(WebCore::Layout::LineBuilder::Run::hasTrailingLetterSpacing const):
(WebCore::Layout::LineBuilder::Run::trailingLetterSpacing const):
(WebCore::Layout::LineBuilder::Run::removeTrailingLetterSpacing):
(WebCore::Layout::LineBuilder::Run::removeTrailingWhitespace):
(WebCore::Layout::LineBuilder::Run::visuallyCollapseTrailingWhitespace):
(WebCore::Layout::LineBuilder::Run::adjustExpansionBehavior):
(WebCore::Layout::LineBuilder::Run::expansionBehavior const):
(WebCore::Layout::LineBuilder::Run::setComputedHorizontalExpansion):
(WebCore::Layout::LineBuilder::ContinuousContent::canInlineItemRunBeExpanded): Deleted.
(WebCore::Layout::LineBuilder::ContinuousContent::ContinuousContent): Deleted.
(WebCore::Layout::LineBuilder::ContinuousContent::isEligible const): Deleted.
(WebCore::Layout::LineBuilder::ContinuousContent::append): Deleted.
(WebCore::Layout::LineBuilder::ContinuousContent::close): Deleted.
(WebCore::Layout::m_isCollapsedToVisuallyEmpty): Deleted.
(WebCore::Layout::shouldPreserveLeadingContent): Deleted.
(WebCore::Layout::LineBuilder::revert): Deleted.
(WebCore::Layout::LineBuilder::justifyRuns const): Deleted.
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::append): Deleted.
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::removeTrailingRun): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::InlineItemRun): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isTrimmableWhitespace const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::hasTrailingLetterSpacing const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::trailingLetterSpacing const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::setCollapsesToZeroAdvanceWidth): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::removeTrailingLetterSpacing): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::hasEmptyTextContent const): Deleted.
* layout/inlineformatting/InlineLineBuilder.h:
(WebCore::Layout::LineBuilder::Run::logicalWidth const):
(WebCore::Layout::LineBuilder::Run::shrinkHorizontally):
(WebCore::Layout::LineBuilder::Run::adjustLogicalTop):
(WebCore::Layout::LineBuilder::Run::hasTrailingWhitespace const):
(WebCore::Layout::LineBuilder::Run::hasCollapsibleTrailingWhitespace const):
(WebCore::Layout::LineBuilder::Run::hasCollapsedTrailingWhitespace const):
(WebCore::Layout::LineBuilder::Run::trailingWhitespaceWidth const):
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::width const):
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::isTrailingRunFullyTrimmable const):
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::isTrailingRunPartiallyTrimmable const):
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::reset):
(WebCore::Layout::LineBuilder::Run::trailingWhitespaceType const):
(WebCore::Layout::LineBuilder::Run::isCollapsedToVisuallyEmpty const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::layoutBox const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::style const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::logicalLeft const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::logicalWidth const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::textContext const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isText const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isBox const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isContainerStart const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isContainerEnd const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isLineBreak const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::type const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isCollapsed const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::moveHorizontally): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::adjustLogicalWidth): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isCollapsedToZeroAdvanceWidth const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isCollapsible const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::isWhitespace const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::hasExpansionOpportunity const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::operator== const): Deleted.
(WebCore::Layout::LineBuilder::InlineItemRun::operator!= const): Deleted.
(WebCore::Layout::LineBuilder::TrimmableTrailingContent::firstRunIndex): Deleted.
* layout/inlineformatting/LineLayoutContext.cpp:
(WebCore::Layout::LineLayoutContext::layoutLine):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (254804 => 254805)


--- trunk/Source/WebCore/ChangeLog	2020-01-19 15:19:00 UTC (rev 254804)
+++ trunk/Source/WebCore/ChangeLog	2020-01-19 16:02:01 UTC (rev 254805)
@@ -1,3 +1,113 @@
+2020-01-19  Zalan Bujtas  <[email protected]>
+
+        [LFC][IFC] Move away from placing individual InlineItems on the line
+        https://bugs.webkit.org/show_bug.cgi?id=206479
+        <rdar://problem/58716223>
+
+        Reviewed by Antti Koivisto.
+
+        <div> text content and  more    </div>
+        [ ][text][ ][content][ ][and][  ]more[   ]
+
+        Instead of placing all these ^^ inline items on the line (LineBuilder::InlineItemRun) and merge them at InlineBuilder::close(),
+        let's expand existing runs(LineBuilder::Run) when possible ([text content and ][more]).
+        This helps performance and memory consumption when the line ends up having many inline itmes (e.g. white-space: nowrap).
+
+        * layout/displaytree/DisplayRun.h:
+        (WebCore::Display::Run::TextContext::expand):
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
+        * layout/inlineformatting/InlineFormattingContextQuirks.cpp:
+        (WebCore::Layout::InlineFormattingContext::Quirks::lineDescentNeedsCollapsing const):
+        * layout/inlineformatting/InlineLineBuilder.cpp:
+        (WebCore::Layout::LineBuilder::LineBuilder):
+        (WebCore::Layout::LineBuilder::initialize):
+        (WebCore::Layout::LineBuilder::close):
+        (WebCore::Layout::LineBuilder::alignContentVertically):
+        (WebCore::Layout::LineBuilder::justifyRuns):
+        (WebCore::Layout::LineBuilder::alignHorizontally):
+        (WebCore::Layout::LineBuilder::removeTrailingTrimmableContent):
+        (WebCore::Layout::LineBuilder::visuallyCollapsePreWrapOverflowContent):
+        (WebCore::Layout::LineBuilder::collectHangingContent):
+        (WebCore::Layout::LineBuilder::append):
+        (WebCore::Layout::LineBuilder::appendNonBreakableSpace):
+        (WebCore::Layout::LineBuilder::appendInlineContainerEnd):
+        (WebCore::Layout::LineBuilder::appendTextContent):
+        (WebCore::Layout::LineBuilder::appendNonReplacedInlineBox):
+        (WebCore::Layout::LineBuilder::appendLineBreak):
+        (WebCore::Layout::LineBuilder::isVisuallyNonEmpty const):
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::TrimmableTrailingContent):
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::addFullyTrimmableContent):
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::addPartiallyTrimmableContent):
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::remove):
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::removePartiallyTrimmableContent):
+        (WebCore::Layout::LineBuilder::Run::Run):
+        (WebCore::Layout::m_textContext):
+        (WebCore::Layout::LineBuilder::Run::expand):
+        (WebCore::Layout::LineBuilder::Run::hasTrailingLetterSpacing const):
+        (WebCore::Layout::LineBuilder::Run::trailingLetterSpacing const):
+        (WebCore::Layout::LineBuilder::Run::removeTrailingLetterSpacing):
+        (WebCore::Layout::LineBuilder::Run::removeTrailingWhitespace):
+        (WebCore::Layout::LineBuilder::Run::visuallyCollapseTrailingWhitespace):
+        (WebCore::Layout::LineBuilder::Run::adjustExpansionBehavior):
+        (WebCore::Layout::LineBuilder::Run::expansionBehavior const):
+        (WebCore::Layout::LineBuilder::Run::setComputedHorizontalExpansion):
+        (WebCore::Layout::LineBuilder::ContinuousContent::canInlineItemRunBeExpanded): Deleted.
+        (WebCore::Layout::LineBuilder::ContinuousContent::ContinuousContent): Deleted.
+        (WebCore::Layout::LineBuilder::ContinuousContent::isEligible const): Deleted.
+        (WebCore::Layout::LineBuilder::ContinuousContent::append): Deleted.
+        (WebCore::Layout::LineBuilder::ContinuousContent::close): Deleted.
+        (WebCore::Layout::m_isCollapsedToVisuallyEmpty): Deleted.
+        (WebCore::Layout::shouldPreserveLeadingContent): Deleted.
+        (WebCore::Layout::LineBuilder::revert): Deleted.
+        (WebCore::Layout::LineBuilder::justifyRuns const): Deleted.
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::append): Deleted.
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::removeTrailingRun): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::InlineItemRun): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isTrimmableWhitespace const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::hasTrailingLetterSpacing const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::trailingLetterSpacing const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::setCollapsesToZeroAdvanceWidth): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::removeTrailingLetterSpacing): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::hasEmptyTextContent const): Deleted.
+        * layout/inlineformatting/InlineLineBuilder.h:
+        (WebCore::Layout::LineBuilder::Run::logicalWidth const):
+        (WebCore::Layout::LineBuilder::Run::shrinkHorizontally):
+        (WebCore::Layout::LineBuilder::Run::adjustLogicalTop):
+        (WebCore::Layout::LineBuilder::Run::hasTrailingWhitespace const):
+        (WebCore::Layout::LineBuilder::Run::hasCollapsibleTrailingWhitespace const):
+        (WebCore::Layout::LineBuilder::Run::hasCollapsedTrailingWhitespace const):
+        (WebCore::Layout::LineBuilder::Run::trailingWhitespaceWidth const):
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::width const):
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::isTrailingRunFullyTrimmable const):
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::isTrailingRunPartiallyTrimmable const):
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::reset):
+        (WebCore::Layout::LineBuilder::Run::trailingWhitespaceType const):
+        (WebCore::Layout::LineBuilder::Run::isCollapsedToVisuallyEmpty const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::layoutBox const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::style const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::logicalLeft const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::logicalWidth const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::textContext const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isText const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isBox const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isContainerStart const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isContainerEnd const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isLineBreak const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::type const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isCollapsed const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::moveHorizontally): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::adjustLogicalWidth): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isCollapsedToZeroAdvanceWidth const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isCollapsible const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::isWhitespace const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::hasExpansionOpportunity const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::operator== const): Deleted.
+        (WebCore::Layout::LineBuilder::InlineItemRun::operator!= const): Deleted.
+        (WebCore::Layout::LineBuilder::TrimmableTrailingContent::firstRunIndex): Deleted.
+        * layout/inlineformatting/LineLayoutContext.cpp:
+        (WebCore::Layout::LineLayoutContext::layoutLine):
+
 2020-01-18  David Kilzer  <[email protected]>
 
         REGRESSION (r254790): Update DerivedSources-{input,output}.xcfilelist to fix build

Modified: trunk/Source/WebCore/layout/displaytree/DisplayRun.h (254804 => 254805)


--- trunk/Source/WebCore/layout/displaytree/DisplayRun.h	2020-01-19 15:19:00 UTC (rev 254804)
+++ trunk/Source/WebCore/layout/displaytree/DisplayRun.h	2020-01-19 16:02:01 UTC (rev 254805)
@@ -52,7 +52,7 @@
         StringView content() const { return StringView(m_contentString).substring(m_start, m_length); }
 
         struct ExpansionContext {
-            ExpansionBehavior behavior;
+            ExpansionBehavior behavior { DefaultExpansion };
             InlineLayoutUnit horizontalExpansion { 0 };
         };
         void setExpansion(ExpansionContext expansionContext) { m_expansionContext = expansionContext; }
@@ -60,7 +60,7 @@
 
         bool needsHyphen() const { return m_needsHyphen; }
 
-        void expand(unsigned expandedLength);
+        void expand(unsigned delta) { m_length += delta; }
         void setNeedsHyphen() { m_needsHyphen = true; }
 
     private:
@@ -133,11 +133,6 @@
 {
 }
 
-inline void Run::TextContext::expand(unsigned expandedLength)
-{
-    m_length = expandedLength;
 }
-
 }
-}
 #endif

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (254804 => 254805)


--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp	2020-01-19 15:19:00 UTC (rev 254804)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp	2020-01-19 16:02:01 UTC (rev 254805)
@@ -485,7 +485,7 @@
 
         // Add final display runs to state first.
         // Inline level containers (<span>) don't generate display runs and neither do completely collapsed runs.
-        auto initiatesInlineRun = !lineRun.isContainerStart() && !lineRun.isContainerEnd() && !lineRun.isCollapsedToVisuallyEmpty();
+        auto initiatesInlineRun = !lineRun.isContainerStart() && !lineRun.isContainerEnd();
         if (initiatesInlineRun) {
             auto computedInkOverflow = [&] {
                 // FIXME: Add support for non-text ink overflow.

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp (254804 => 254805)


--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp	2020-01-19 15:19:00 UTC (rev 254804)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp	2020-01-19 16:02:01 UTC (rev 254805)
@@ -49,11 +49,8 @@
 
         if (run.isLineBreak())
             return false;
-        if (run.isText()) {
-            if (!run.isCollapsedToVisuallyEmpty())
-                return false;
-            continue;
-        }
+        if (run.isText())
+            return false;
         if (run.isContainerStart()) {
             auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
             if (boxGeometry.horizontalBorder() || (boxGeometry.horizontalPadding() && boxGeometry.horizontalPadding().value()))

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

Modified: trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp (254804 => 254805)


--- trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp	2020-01-19 15:19:00 UTC (rev 254804)
+++ trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp	2020-01-19 16:02:01 UTC (rev 254805)
@@ -287,7 +287,7 @@
                 ASSERT(!result.committedCount);
                 ASSERT(result.isEndOfLine == LineBreaker::IsEndOfLine::Yes);
                 // An earlier line wrapping opportunity turned out to be the final breaking position.
-                committedInlineItemCount -= line.revert(*result.revertTo);
+                ASSERT_NOT_IMPLEMENTED_YET();
             }
             committedInlineItemCount += result.committedCount;
             if (result.isEndOfLine == LineBreaker::IsEndOfLine::Yes) {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to