[LFC][IFC] Optimize nextWrapOpportunity/isAtSoftWrapOpportunity for the most common inline content
https://bugs.webkit.org/show_bug.cgi?id=206395
<rdar://problem/58670070>

Reviewed by Antti Koivisto.

~2% progression on PerformanceTests/Layout/line-layout-simple.html.

* layout/inlineformatting/LineLayoutContext.cpp:
(WebCore::Layout::isAtSoftWrapOpportunity):
(WebCore::Layout::nextWrapOpportunity):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@254743 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 337b12c..4bf4945 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,17 @@
+2020-01-17  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][IFC] Optimize nextWrapOpportunity/isAtSoftWrapOpportunity for the most common inline content
+        https://bugs.webkit.org/show_bug.cgi?id=206395
+        <rdar://problem/58670070>
+
+        Reviewed by Antti Koivisto.
+
+        ~2% progression on PerformanceTests/Layout/line-layout-simple.html.
+
+        * layout/inlineformatting/LineLayoutContext.cpp:
+        (WebCore::Layout::isAtSoftWrapOpportunity):
+        (WebCore::Layout::nextWrapOpportunity):
+
 2020-01-16  Carlos Alberto Lopez Perez  <clopez@igalia.com>
 
         [GTK] Turn off antialiasing when rendering with Ahem (v2)
diff --git a/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp b/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp
index dfbf9ca..5060983 100644
--- a/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp
+++ b/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp
@@ -74,31 +74,35 @@
     // but an incoming text content would not necessarily.
     ASSERT(current.isText() || current.isBox());
     ASSERT(next.isText() || next.isBox());
+    if (current.isText() && next.isText()) {
+        auto& currentInlineTextItem = downcast<InlineTextItem>(current);
+        auto& nextInlineTextItem = downcast<InlineTextItem>(next);
+        if (currentInlineTextItem.isWhitespace()) {
+            // [ ][text] : after [whitespace] position is a soft wrap opportunity.
+            return true;
+        }
+        if (nextInlineTextItem.isWhitespace()) {
+            // [text][ ] (<span>text</span> )
+            // white-space: break-spaces: line breaking opportunity exists after every preserved white space character, but not before.
+            return nextInlineTextItem.style().whiteSpace() != WhiteSpace::BreakSpaces;
+        }
+        if (current.style().lineBreak() == LineBreak::Anywhere || next.style().lineBreak() == LineBreak::Anywhere) {
+            // There is a soft wrap opportunity around every typographic character unit, including around any punctuation character
+            // or preserved white spaces, or in the middle of words.
+            return true;
+        }
+        // Both current and next items are non-whitespace text.
+        // [text][text] : is a continuous content.
+        // [text-][text] : after [hyphen] position is a soft wrap opportunity.
+        return endsWithSoftWrapOpportunity(currentInlineTextItem, nextInlineTextItem);
+    }
     if (current.isBox() || next.isBox()) {
         // [text][container start][container end][inline box] (text<span></span><img>) : there's a soft wrap opportunity between the [text] and [img].
         // The line breaking behavior of a replaced element or other atomic inline is equivalent to an ideographic character.
         return true;
     }
-    if (current.style().lineBreak() == LineBreak::Anywhere || next.style().lineBreak() == LineBreak::Anywhere) {
-        // There is a soft wrap opportunity around every typographic character unit, including around any punctuation character
-        // or preserved white spaces, or in the middle of words.
-        return true;
-    }
-    auto& currentInlineTextItem = downcast<InlineTextItem>(current);
-    auto& nextInlineTextItem = downcast<InlineTextItem>(next);
-    if (currentInlineTextItem.isWhitespace()) {
-        // [ ][text] : after [whitespace] position is a soft wrap opportunity.
-        return true;
-    }
-    if (nextInlineTextItem.isWhitespace()) {
-        // [text][ ] (<span>text</span> )
-        // white-space: break-spaces: line breaking opportunity exists after every preserved white space character, but not before.
-        return nextInlineTextItem.style().whiteSpace() != WhiteSpace::BreakSpaces;
-    }
-    // Both current and next items are non-whitespace text.
-    // [text][text] : is a continuous content.
-    // [text-][text] : after [hyphen] position is a soft wrap opportunity.
-    return endsWithSoftWrapOpportunity(currentInlineTextItem, nextInlineTextItem);
+    ASSERT_NOT_REACHED();
+    return true;
 }
 
 static inline size_t nextWrapOpportunity(const InlineItems& inlineContent, unsigned startIndex)
@@ -111,56 +115,47 @@
     // [ex-][container start][container end][float][ample] (ex-<span></span><div style="float:left"></div>ample) : wrap index is at [ex-].
     // [ex][container start][amp-][container start][le] (ex<span>amp-<span>ample) : wrap index is at [amp-].
     // [ex-][container start][line break][ample] (ex-<span><br>ample) : wrap index is after [br].
-    auto end = inlineContent.size();
+    unsigned inlineItemCount = inlineContent.size();
+    auto isAtLineBreak = false;
 
-    struct WrapContent {
-        WrapContent(size_t index, bool isAtLineBreak)
-            : m_index(index)
-            , m_isAtLineBreak(isAtLineBreak)
-        {
-        }
-        size_t operator*() const { return m_index; }
-        bool isAtLineBreak() const { return m_isAtLineBreak; }
-
-    private:
-        size_t m_index { 0 };
-        bool m_isAtLineBreak { false };
-    };
-    auto nextInlineItemWithContent = [&] (auto index) {
+    auto inlineItemIndexWithContent = [&] (auto index) {
         // Break at the first text/box/line break inline item.
-        for (; index < end; ++index) {
+        for (; index < inlineItemCount; ++index) {
             auto& inlineItem = *inlineContent[index];
-            if (inlineItem.isText() || inlineItem.isBox() || inlineItem.isLineBreak())
-                return WrapContent { index, inlineItem.isLineBreak() };
+            if (inlineItem.isText() || inlineItem.isBox())
+                return index;
+            if (inlineItem.isLineBreak()) {
+                isAtLineBreak = true;
+                return index;
+            }
         }
-        return WrapContent { end, false };
+        return inlineItemCount;
     };
 
     // Start at the first inline item with content.
     // [container start][ex-] : start at [ex-]
-    auto startContent = nextInlineItemWithContent(startIndex);
-    if (startContent.isAtLineBreak()) {
+    auto startContentIndex = inlineItemIndexWithContent(startIndex);
+    if (isAtLineBreak) {
         // Content starts with a line break. The wrap position is after the line break.
-        return *startContent + 1;
+        return startContentIndex + 1;
     }
 
-    while (*startContent != end) {
+    while (startContentIndex < inlineItemCount) {
         // 1. Find the next inline item with content.
         // 2. Check if there's a soft wrap opportunity between the start and the next inline item.
-        auto nextContent = nextInlineItemWithContent(*startContent + 1);
-        if (*nextContent == end)
-            return *nextContent;
-        if (nextContent.isAtLineBreak()) {
+        auto nextContentIndex = inlineItemIndexWithContent(startContentIndex + 1);
+        if (nextContentIndex == inlineItemCount)
+            return nextContentIndex;
+        if (isAtLineBreak) {
             // We always stop at line breaks. The wrap position is after the line break.
-            return *nextContent + 1;
+            return nextContentIndex + 1;
         }
-        if (isAtSoftWrapOpportunity(*inlineContent[*startContent], *inlineContent[*nextContent])) {
+        if (isAtSoftWrapOpportunity(*inlineContent[startContentIndex], *inlineContent[nextContentIndex])) {
             // There's a soft wrap opportunity between the start and the nextContent.
             // Now forward-find from the start position to see where we can actually wrap.
             // [ex-][ample] vs. [ex-][container start][container end][ample]
             // where [ex-] is startContent and [ample] is the nextContent.
-            auto candidateIndex = *startContent + 1;
-            for (; candidateIndex < *nextContent; ++candidateIndex) {
+            for (auto candidateIndex = startContentIndex + 1; candidateIndex < nextContentIndex; ++candidateIndex) {
                 if (inlineContent[candidateIndex]->isContainerStart()) {
                     // inline content and [container start] and [container end] form unbreakable content.
                     // ex-<span></span>ample  : wrap opportunity is after "ex-".
@@ -170,11 +165,11 @@
                     return candidateIndex;
                 }
             }
-            return candidateIndex;
+            return nextContentIndex;
         }
-        startContent = nextContent;
+        startContentIndex = nextContentIndex;
     }
-    return end;
+    return inlineItemCount;
 }
 
 struct LineCandidateContent {