Title: [254661] trunk/Source/WebCore
Revision
254661
Author
za...@apple.com
Date
2020-01-15 18:23:39 -0800 (Wed, 15 Jan 2020)

Log Message

[LFC][IFC] LineBreaker::shouldWrapInlineContent should take the candidate content width
https://bugs.webkit.org/show_bug.cgi?id=206305
<rdar://problem/58613977>

Reviewed by Antti Koivisto.

We already have the width information of the candidate runs. Let's not loop through the runs just to re-collect the logical width.
~3% progression on PerformanceTests/Layout/line-layout-simple.html.

* layout/inlineformatting/InlineLineBreaker.cpp:
(WebCore::Layout::LineBreaker::shouldWrapInlineContent):
(WebCore::Layout::LineBreaker::tryWrappingInlineContent const):
(WebCore::Layout::ContinuousContent::ContinuousContent):
* layout/inlineformatting/InlineLineBreaker.h:
* layout/inlineformatting/LineLayoutContext.cpp:
(WebCore::Layout::LineCandidateContent::inlineContentLogicalWidth const):
(WebCore::Layout::LineCandidateContent::append):
(WebCore::Layout::LineCandidateContent::reset):
(WebCore::Layout::LineLayoutContext::tryAddingInlineItems):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (254660 => 254661)


--- trunk/Source/WebCore/ChangeLog	2020-01-16 02:09:56 UTC (rev 254660)
+++ trunk/Source/WebCore/ChangeLog	2020-01-16 02:23:39 UTC (rev 254661)
@@ -1,3 +1,25 @@
+2020-01-15  Zalan Bujtas  <za...@apple.com>
+
+        [LFC][IFC] LineBreaker::shouldWrapInlineContent should take the candidate content width
+        https://bugs.webkit.org/show_bug.cgi?id=206305
+        <rdar://problem/58613977>
+
+        Reviewed by Antti Koivisto.
+
+        We already have the width information of the candidate runs. Let's not loop through the runs just to re-collect the logical width.
+        ~3% progression on PerformanceTests/Layout/line-layout-simple.html.
+
+        * layout/inlineformatting/InlineLineBreaker.cpp:
+        (WebCore::Layout::LineBreaker::shouldWrapInlineContent):
+        (WebCore::Layout::LineBreaker::tryWrappingInlineContent const):
+        (WebCore::Layout::ContinuousContent::ContinuousContent):
+        * layout/inlineformatting/InlineLineBreaker.h:
+        * layout/inlineformatting/LineLayoutContext.cpp:
+        (WebCore::Layout::LineCandidateContent::inlineContentLogicalWidth const):
+        (WebCore::Layout::LineCandidateContent::append):
+        (WebCore::Layout::LineCandidateContent::reset):
+        (WebCore::Layout::LineLayoutContext::tryAddingInlineItems):
+
 2020-01-15  Commit Queue  <commit-qu...@webkit.org>
 
         Unreviewed, rolling out r254565.

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp (254660 => 254661)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp	2020-01-16 02:09:56 UTC (rev 254660)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp	2020-01-16 02:23:39 UTC (rev 254661)
@@ -50,8 +50,20 @@
     return whitespace == WhiteSpace::Pre || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::BreakSpaces;
 }
 
+static inline Optional<size_t> lastWrapOpportunityIndex(const LineBreaker::RunList& runList)
+{
+    // <span style="white-space: pre">no_wrap</span><span>yes wrap</span><span style="white-space: pre">no_wrap</span>.
+    // [container start][no_wrap][container end][container start][yes] <- continuous content
+    // [ ] <- continuous content
+    // [wrap][container end][container start][no_wrap][container end] <- continuous content
+    // Return #0 as the index where the second continuous content can wrap at.
+    ASSERT(!runList.isEmpty());
+    auto lastItemIndex = runList.size() - 1;
+    return isWrappingAllowed(runList[lastItemIndex].inlineItem.style()) ? makeOptional(lastItemIndex) : WTF::nullopt;
+}
+
 struct ContinuousContent {
-    ContinuousContent(const LineBreaker::RunList&);
+    ContinuousContent(const LineBreaker::RunList&, InlineLayoutUnit contentLogicalWidth);
 
     const LineBreaker::RunList& runs() const { return m_runs; }
     bool isEmpty() const { return m_runs.isEmpty(); }
@@ -64,7 +76,6 @@
 
     bool hasTrailingCollapsibleContent() const { return !!m_trailingCollapsibleContent.width; }
     bool isTrailingContentFullyCollapsible() const { return m_trailingCollapsibleContent.isFullyCollapsible; }
-    Optional<size_t> lastWrapOpportunityIndex() const;
 
     Optional<size_t> firstTextRunIndex() const;
     Optional<size_t> lastContentRunIndex() const;
@@ -104,40 +115,44 @@
     return whitespace == WhiteSpace::Normal || whitespace == WhiteSpace::NoWrap || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::PreLine;
 }
 
-LineBreaker::Result LineBreaker::shouldWrapInlineContent(const RunList& candidateRuns, const LineStatus& lineStatus)
+LineBreaker::Result LineBreaker::shouldWrapInlineContent(const RunList& candidateRuns, InlineLayoutUnit candidateContentLogicalWidth, const LineStatus& lineStatus)
 {
-    auto candidateContent = ContinuousContent { candidateRuns };
-    ASSERT(!candidateContent.isEmpty());
-    auto result = tryWrappingInlineContent(candidateContent, lineStatus);
-    // If this is not the end of the line, hold on to the last eligible line wrap opportunity so that we could revert back
-    // to this position if no other line breaking opportunity exists in this content.
-    if (result.isEndOfLine == IsEndOfLine::Yes)
-        return result;
-    if (auto lastLineWrapOpportunityIndex = candidateContent.lastWrapOpportunityIndex()) {
-        auto isEligibleLineWrapOpportunity = [&] (auto& candidateItem) {
-            // Just check for leading collapsible whitespace for now.
-            if (!lineStatus.lineIsEmpty || !candidateItem.isText() || !downcast<InlineTextItem>(candidateItem).isWhitespace())
-                return true;
-            return shouldKeepBeginningOfLineWhitespace(candidateItem.style());
-        };
-        auto& inlineItem = candidateContent.runs()[*lastLineWrapOpportunityIndex].inlineItem;
-        if (isEligibleLineWrapOpportunity(inlineItem))
-            m_lastWrapOpportunity = &inlineItem;
+    auto inlineContentWrapping = [&] {
+        if (candidateContentLogicalWidth <= lineStatus.availableWidth)
+            return Result { Result::Action::Keep };
+#if USE_FLOAT_AS_INLINE_LAYOUT_UNIT
+        // Preferred width computation sums up floats while line breaker substracts them. This can lead to epsilon-scale differences.
+        if (WTF::areEssentiallyEqual(candidateContentLogicalWidth, lineStatus.availableWidth))
+            return Result { Result::Action::Keep };
+#endif
+        return tryWrappingInlineContent(candidateRuns, candidateContentLogicalWidth, lineStatus);
+    };
+
+    auto result = inlineContentWrapping();
+    if (result.action == Result::Action::Keep) {
+        // If this is not the end of the line, hold on to the last eligible line wrap opportunity so that we could revert back
+        // to this position if no other line breaking opportunity exists in this content.
+        if (auto lastLineWrapOpportunityIndex = lastWrapOpportunityIndex(candidateRuns)) {
+            auto isEligibleLineWrapOpportunity = [&] (auto& candidateItem) {
+                // Just check for leading collapsible whitespace for now.
+                if (!lineStatus.lineIsEmpty || !candidateItem.isText() || !downcast<InlineTextItem>(candidateItem).isWhitespace())
+                    return true;
+                return shouldKeepBeginningOfLineWhitespace(candidateItem.style());
+            };
+            auto& inlineItem = candidateRuns[*lastLineWrapOpportunityIndex].inlineItem;
+            if (isEligibleLineWrapOpportunity(inlineItem))
+                m_lastWrapOpportunity = &inlineItem;
+        }
     }
     return result;
 }
 
-LineBreaker::Result LineBreaker::tryWrappingInlineContent(const ContinuousContent& candidateContent, const LineStatus& lineStatus) const
+LineBreaker::Result LineBreaker::tryWrappingInlineContent(const RunList& candidateRuns, InlineLayoutUnit candidateContentLogicalWidth, const LineStatus& lineStatus) const
 {
-    if (candidateContent.width() <= lineStatus.availableWidth)
-        return { Result::Action::Keep };
+    auto candidateContent = ContinuousContent { candidateRuns, candidateContentLogicalWidth };
+    ASSERT(!candidateContent.isEmpty());
 
-#if USE_FLOAT_AS_INLINE_LAYOUT_UNIT
-    // Preferred width computation sums up floats while line breaker substracts them. This can lead to epsilon-scale differences.
-    if (WTF::areEssentiallyEqual(candidateContent.width(), lineStatus.availableWidth))
-        return { Result::Action::Keep };
-#endif
-
+    ASSERT(candidateContent.width() > lineStatus.availableWidth);
     if (candidateContent.hasTrailingCollapsibleContent()) {
         ASSERT(candidateContent.hasTextContentOnly());
         auto IsEndOfLine = isContentWrappingAllowed(candidateContent) ? IsEndOfLine::Yes : IsEndOfLine::No;
@@ -340,8 +355,9 @@
     return { };
 }
 
-ContinuousContent::ContinuousContent(const LineBreaker::RunList& runs)
+ContinuousContent::ContinuousContent(const LineBreaker::RunList& runs, InlineLayoutUnit contentLogicalWidth)
     : m_runs(runs)
+    , m_width(contentLogicalWidth)
 {
     // Figure out the trailing collapsible state.
     for (auto& run : WTF::makeReversedRange(m_runs)) {
@@ -372,13 +388,6 @@
             break;
         }
     }
-    // The trailing whitespace loop above is mostly about inspecting the last entry, so while it
-    // looks like we are looping through the m_runs twice, it's really just one full loop in addition to checking the last run.
-    for (auto& run : m_runs) {
-        // Line break is not considered an inline content.
-        ASSERT(!run.inlineItem.isLineBreak());
-        m_width += run.logicalWidth;
-    }
 }
 
 bool ContinuousContent::hasTextContentOnly() const
@@ -439,17 +448,6 @@
     return true;
 }
 
-Optional<size_t> ContinuousContent::lastWrapOpportunityIndex() const
-{
-    // <span style="white-space: pre">no_wrap</span><span>yes wrap</span><span style="white-space: pre">no_wrap</span>.
-    // [container start][no_wrap][container end][container start][yes] <- continuous content
-    // [ ] <- continuous content
-    // [wrap][container end][container start][no_wrap][container end] <- continuous content
-    // Return #0 as the index where the second continuous content can wrap at.
-    auto lastItemIndex = m_runs.size() - 1;
-    return isWrappingAllowed(m_runs[lastItemIndex].inlineItem.style()) ? makeOptional(lastItemIndex) : WTF::nullopt;
-}
-
 void ContinuousContent::TrailingCollapsibleContent::reset()
 {
     isFullyCollapsible = false;

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h (254660 => 254661)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h	2020-01-16 02:09:56 UTC (rev 254660)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h	2020-01-16 02:23:39 UTC (rev 254661)
@@ -79,7 +79,7 @@
         bool lineHasFullyCollapsibleTrailingRun { false };
         bool lineIsEmpty { true };
     };
-    Result shouldWrapInlineContent(const RunList& candidateRuns, const LineStatus&);
+    Result shouldWrapInlineContent(const RunList& candidateRuns, InlineLayoutUnit candidateContentLogicalWidth, const LineStatus&);
     bool shouldWrapFloatBox(InlineLayoutUnit floatLogicalWidth, InlineLayoutUnit availableWidth, bool lineIsEmpty);
 
     void setHyphenationDisabled() { n_hyphenationIsDisabled = true; }
@@ -95,7 +95,7 @@
     // [container start][span1][container end][between][container start][span2][container end]
     // see https://drafts.csswg.org/css-text-3/#line-break-details
     Optional<WrappedTextContent> wrapTextContent(const RunList&, const LineStatus&) const;
-    Result tryWrappingInlineContent(const ContinuousContent&, const LineStatus&) const;
+    Result tryWrappingInlineContent(const RunList&, InlineLayoutUnit candidateContentLogicalWidth, const LineStatus&) const;
     Optional<PartialRun> tryBreakingTextRun(const Run& overflowRun, InlineLayoutUnit availableWidth) const;
 
     enum class WordBreakRule {

Modified: trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp (254660 => 254661)


--- trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp	2020-01-16 02:09:56 UTC (rev 254660)
+++ trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp	2020-01-16 02:23:39 UTC (rev 254661)
@@ -181,6 +181,7 @@
 
     bool hasIntrusiveFloats() const { return !m_floats.isEmpty(); }
     const LineBreaker::RunList& inlineRuns() const { return m_inlineRuns; }
+    InlineLayoutUnit inlineContentLogicalWidth() const { return m_inlineContentLogicalWidth; }
     const LineLayoutContext::FloatList& floats() const { return m_floats; }
 
     const InlineItem* trailingLineBreak() const { return m_trailingLineBreak; }
@@ -190,6 +191,7 @@
 private:
     void setTrailingLineBreak(const InlineItem& lineBreakItem) { m_trailingLineBreak = &lineBreakItem; }
 
+    InlineLayoutUnit m_inlineContentLogicalWidth { 0 };
     LineBreaker::RunList m_inlineRuns;
     LineLayoutContext::FloatList m_floats;
     const InlineItem* m_trailingLineBreak { nullptr };
@@ -202,11 +204,13 @@
         return setTrailingLineBreak(inlineItem);
     if (inlineItem.isFloat())
         return m_floats.append(makeWeakPtr(inlineItem));
+    m_inlineContentLogicalWidth += *logicalWidth;
     m_inlineRuns.append({ inlineItem, *logicalWidth });
 }
 
 void LineCandidateContent::reset()
 {
+    m_inlineContentLogicalWidth = 0;
     m_inlineRuns.clear();
     m_floats.clear();
     m_trailingLineBreak = nullptr;
@@ -410,7 +414,7 @@
         lineBreaker.setHyphenationDisabled();
 
     auto& candidateRuns = candidateContent.inlineRuns();
-    auto result = lineBreaker.shouldWrapInlineContent(candidateRuns, lineStatus);
+    auto result = lineBreaker.shouldWrapInlineContent(candidateRuns, candidateContent.inlineContentLogicalWidth(), lineStatus);
     if (result.action == LineBreaker::Result::Action::Keep) {
         // This continuous content can be fully placed on the current line.
         commitContent(line, candidateRuns, { });
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to