Title: [253909] trunk/Source/WebCore
Revision
253909
Author
[email protected]
Date
2019-12-25 07:52:28 -0800 (Wed, 25 Dec 2019)

Log Message

[LFC][IFC] Forward scan for soft wrap opportunities
https://bugs.webkit.org/show_bug.cgi?id=205584
<rdar://problem/58188386>

Reviewed by Antti Koivisto.

This patch implements forward scanning to find wrap opportunities in inline content.
e.g <span></span>example<span><span></span> content</span>
When we reach "ex-" content, in order to figure out if it is at a wrap opportunity, we scan the content
forward until after we reach another inline content, in this case " " right before the "content" and
check if we can break the content between these 2 inline items.

isAtSoftWrapOpportunity: takes 2 (adjacent by skipping non-content inline items) and return true if there's
a soft wrap opportunity in between them.
LineBreaker::nextWrapOpportunity: returns the next wrap opportunity (either a soft wrap opportunity or a line break or the end of the content)

* layout/inlineformatting/InlineLineBreaker.cpp:
(WebCore::Layout::endsWithSoftWrapOpportunity):
(WebCore::Layout::isAtSoftWrapOpportunity):
(WebCore::Layout::LineBreaker::nextWrapOpportunity):
(WebCore::Layout::LineBreaker::ContinousContent::ContinousContent):
(WebCore::Layout::LineBreaker::lastSoftWrapOpportunity): Deleted.
* layout/inlineformatting/InlineLineBreaker.h:
* layout/inlineformatting/LineLayoutContext.cpp:
(WebCore::Layout::LineCandidateContent::isLineBreak const):
(WebCore::Layout::LineCandidateContent::append):
(WebCore::Layout::LineCandidateContent::setIsLineBreak):
(WebCore::Layout::LineLayoutContext::layoutLine):
(WebCore::Layout::LineLayoutContext::nextContentForLine):
(WebCore::Layout::ContinousContent::hasIntrusiveFloats const): Deleted.
(WebCore::Layout::ContinousContent::runs const): Deleted.
(WebCore::Layout::ContinousContent::floats const): Deleted.
(WebCore::Layout::ContinousContent::endsWithLineBreak const): Deleted.
(WebCore::Layout::ContinousContent::setEndsWithLineBreak): Deleted.
(WebCore::Layout::ContinousContent::append): Deleted.
(WebCore::Layout::LineLayoutContext::nextContinousContentForLine): Deleted.
* layout/inlineformatting/LineLayoutContext.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (253908 => 253909)


--- trunk/Source/WebCore/ChangeLog	2019-12-25 13:09:04 UTC (rev 253908)
+++ trunk/Source/WebCore/ChangeLog	2019-12-25 15:52:28 UTC (rev 253909)
@@ -1,5 +1,45 @@
 2019-12-25  Zalan Bujtas  <[email protected]>
 
+        [LFC][IFC] Forward scan for soft wrap opportunities
+        https://bugs.webkit.org/show_bug.cgi?id=205584
+        <rdar://problem/58188386>
+
+        Reviewed by Antti Koivisto.
+
+        This patch implements forward scanning to find wrap opportunities in inline content.
+        e.g <span></span>example<span><span></span> content</span>
+        When we reach "ex-" content, in order to figure out if it is at a wrap opportunity, we scan the content
+        forward until after we reach another inline content, in this case " " right before the "content" and
+        check if we can break the content between these 2 inline items.
+
+        isAtSoftWrapOpportunity: takes 2 (adjacent by skipping non-content inline items) and return true if there's
+        a soft wrap opportunity in between them.
+        LineBreaker::nextWrapOpportunity: returns the next wrap opportunity (either a soft wrap opportunity or a line break or the end of the content)
+
+        * layout/inlineformatting/InlineLineBreaker.cpp:
+        (WebCore::Layout::endsWithSoftWrapOpportunity):
+        (WebCore::Layout::isAtSoftWrapOpportunity):
+        (WebCore::Layout::LineBreaker::nextWrapOpportunity):
+        (WebCore::Layout::LineBreaker::ContinousContent::ContinousContent):
+        (WebCore::Layout::LineBreaker::lastSoftWrapOpportunity): Deleted.
+        * layout/inlineformatting/InlineLineBreaker.h:
+        * layout/inlineformatting/LineLayoutContext.cpp:
+        (WebCore::Layout::LineCandidateContent::isLineBreak const):
+        (WebCore::Layout::LineCandidateContent::append):
+        (WebCore::Layout::LineCandidateContent::setIsLineBreak):
+        (WebCore::Layout::LineLayoutContext::layoutLine):
+        (WebCore::Layout::LineLayoutContext::nextContentForLine):
+        (WebCore::Layout::ContinousContent::hasIntrusiveFloats const): Deleted.
+        (WebCore::Layout::ContinousContent::runs const): Deleted.
+        (WebCore::Layout::ContinousContent::floats const): Deleted.
+        (WebCore::Layout::ContinousContent::endsWithLineBreak const): Deleted.
+        (WebCore::Layout::ContinousContent::setEndsWithLineBreak): Deleted.
+        (WebCore::Layout::ContinousContent::append): Deleted.
+        (WebCore::Layout::LineLayoutContext::nextContinousContentForLine): Deleted.
+        * layout/inlineformatting/LineLayoutContext.h:
+
+2019-12-25  Zalan Bujtas  <[email protected]>
+
         [LFC][IFC] LineBreaker should tell whether the line should receive no more content
         https://bugs.webkit.org/show_bug.cgi?id=205587
         <rdar://problem/58188635>

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp (253908 => 253909)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp	2019-12-25 13:09:04 UTC (rev 253908)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp	2019-12-25 15:52:28 UTC (rev 253909)
@@ -263,20 +263,20 @@
     return { };
 }
 
-static bool endsWithSoftWrapOpportunity(const InlineTextItem& previousTextItem, const InlineTextItem& nextInlineTextItem)
+static bool endsWithSoftWrapOpportunity(const InlineTextItem& currentTextItem, const InlineTextItem& nextInlineTextItem)
 {
     ASSERT(!nextInlineTextItem.isWhitespace());
+    // We are at the position after a whitespace.
+    if (currentTextItem.isWhitespace())
+        return true;
     // When both these non-whitespace runs belong to the same layout box, it's guaranteed that
     // they are split at a soft breaking opportunity. See InlineTextItem::moveToNextBreakablePosition.
-    if (&previousTextItem.layoutBox() == &nextInlineTextItem.layoutBox())
+    if (&currentTextItem.layoutBox() == &nextInlineTextItem.layoutBox())
         return true;
-    // We are at the position after a whitespace.
-    if (previousTextItem.isWhitespace())
-        return true;
     // Now we need to collect at least 3 adjacent characters to be able to make a descision whether the previous text item ends with breaking opportunity.
     // [ex-][ample] <- second to last[x] last[-] current[a]
     // We need at least 1 character in the current inline text item and 2 more from previous inline items.
-    auto previousContent = previousTextItem.layoutBox().textContext()->content;
+    auto previousContent = currentTextItem.layoutBox().textContext()->content;
     auto lineBreakIterator = LazyLineBreakIterator { nextInlineTextItem.layoutBox().textContext()->content };
     auto previousContentLength = previousContent.length();
     // FIXME: We should look into the entire uncommitted content for more text context.
@@ -289,110 +289,116 @@
     return !TextUtil::findNextBreakablePosition(lineBreakIterator, 0, nextInlineTextItem.style());
 }
 
-Optional<size_t> LineBreaker::lastSoftWrapOpportunity(const InlineItem& inlineItem, const RunList& priorContent)
+static bool isAtSoftWrapOpportunity(const InlineItem& current, const InlineItem& next)
 {
+    // "is at" simple means that there's a soft wrap opportunity right after the [current].
+    // [text][ ][text][container start]... (<div>text content<span>..</div>)
+    // soft wrap indexes: 0 and 1 definitely, 2 depends on the content after the [container start].
+
     // https://drafts.csswg.org/css-text-3/#line-break-details
     // Figure out if the new incoming content puts the uncommitted content on a soft wrap opportunity.
     // e.g. [container start][prior_continuous_content][container end] (<span>prior_continuous_content</span>)
     // An incoming <img> box would enable us to commit the "<span>prior_continuous_content</span>" content
     // but an incoming text content would not necessarily.
-    ASSERT(!inlineItem.isFloat() && !inlineItem.isLineBreak());
-    if (priorContent.isEmpty()) {
-        // Can't decide it yet.
-        return { };
+    ASSERT(current.isText() || current.isBox());
+    ASSERT(next.isText() || next.isBox());
+    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);
+}
 
-    auto lastInlineItemWithContent = [&] () -> const InlineItem* {
-        for (auto& previousRun : WTF::makeReversedRange(priorContent)) {
-            auto& previousInlineItem = previousRun.inlineItem;
-            if (previousInlineItem.isText() || previousInlineItem.isBox())
-                return &previousInlineItem;
-            ASSERT(previousInlineItem.isContainerStart() || previousInlineItem.isContainerEnd());
+size_t LineBreaker::nextWrapOpportunity(const InlineItems& inlineContent, unsigned startIndex)
+{
+    // 1. Find the start candidate by skipping leading non-content items e.g <span><span>start : skip "<span><span>"
+    // 2. Find the end candidate by skipping non-content items inbetween e.g. <span><span>start</span>end: skip "</span>"
+    // 3. Check if there's a soft wrap opportunity between the 2 candidate inline items and repeat.
+    // 4. Any force line break inbetween is considered as a wrap opportunity.
+
+    // [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();
+
+    struct WrapContent {
+        WrapContent(size_t index, bool isAtLineBreak)
+            : m_index(index)
+            , m_isAtLineBreak(isAtLineBreak)
+        {
         }
-        return nullptr;
+        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) {
+        // Break at the first text/box/line break inline item.
+        for (; index < end; ++index) {
+            auto& inlineItem = *inlineContent[index];
+            if (inlineItem.isText() || inlineItem.isBox() || inlineItem.isLineBreak())
+                return WrapContent { index, inlineItem.isLineBreak() };
+        }
+        return WrapContent { end, false };
+    };
 
-    auto* lastUncomittedContent = &priorContent.last().inlineItem;
-    if (inlineItem.isText()) {
-        if (inlineItem.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 priorContent.size();
-        }
-        if (downcast<InlineTextItem>(inlineItem).isWhitespace()) {
-            // [prior content][ ] (<span>some_content</span> )
-            // white-space: break-spaces: line breaking opportunity exists after every preserved white space character, but not before.
-            auto isAtSoftWrapOpportunityBeforeWhitespace = inlineItem.style().whiteSpace() != WhiteSpace::BreakSpaces;
-            // [ ][ ] : adjacent whitespace content has soft wrap opportunity.
-            if (lastUncomittedContent->isText() && downcast<InlineTextItem>(*lastUncomittedContent).isWhitespace())
-                isAtSoftWrapOpportunityBeforeWhitespace = true;
-            return isAtSoftWrapOpportunityBeforeWhitespace ? makeOptional(priorContent.size()) : WTF::nullopt;
-        }
-        if (lastUncomittedContent->isContainerStart()) {
-            // [container start][text] (<span>text) : the [container start] and the [text] content form a continuous content.
-            return { };
-        }
-        if (lastUncomittedContent->isContainerEnd()) {
-            // [container end][text] (</span>text)
-            // Need to check what's before the </span> to be able to decide whether it's a continuous content.
-            // e.g.
-            // [text][container end][text] (text</span>text) : there's no soft wrap opportunity here.
-            // [inline box][container end][text] (<img></span>text) : after [container end] position is a soft wrap opportunity.
-            lastUncomittedContent = lastInlineItemWithContent();
-            if (!lastUncomittedContent) {
-                // Did not find any content at all (e.g. [container start][container end][text] (<span></span>text)).
-                return { };
-            }
-        }
-        if (lastUncomittedContent->isText()) {
-            // [text][text] : is a continuous content.
-            // [text-][text] : after [hyphen] position is a soft wrap opportunity.
-            // [ ][text] : after [whitespace] position is a soft wrap opportunity.
-            auto lastUncomittedEndsWithSoftWrapOpportunity = endsWithSoftWrapOpportunity(downcast<InlineTextItem>(*lastUncomittedContent), downcast<InlineTextItem>(inlineItem));
-            return lastUncomittedEndsWithSoftWrapOpportunity ? makeOptional(priorContent.size()) : WTF::nullopt;
-        }
-        if (lastUncomittedContent->isBox()) {
-            // [inline box][text] (<img>text) : after [inline box] position is a soft wrap opportunity.
-            return priorContent.size();
-        }
-        ASSERT_NOT_REACHED();
+    // Start at the first inline item with content.
+    // [container start][ex-] : start at [ex-]
+    auto startContent = nextInlineItemWithContent(startIndex);
+    if (startContent.isAtLineBreak()) {
+        // Content starts with a line break. The wrap position is after the line break.
+        return *startContent + 1;
     }
-    if (inlineItem.isBox()) {
-        if (lastUncomittedContent->isContainerStart()) {
-            // [container start][inline box] (<spam><img>) : the [container start] and the [inline box] form a continuous content.
-            return { };
-        }
-        if (lastUncomittedContent->isContainerEnd()) {
-            // [container end][inline box] (</span><img>) : after [container end] position is a soft wrap opportunity.
-            return priorContent.size();
-        }
-        if (lastUncomittedContent->isText() || lastUncomittedContent->isBox()) {
-            // [inline box][text] (<img>text) and [inline box][inline box] (<img><img>) : after first [inline box] position is a soft wrap opportunity.
-            return priorContent.size();
-        }
-        ASSERT_NOT_REACHED();
-    }
 
-    if (inlineItem.isContainerStart() || inlineItem.isContainerEnd()) {
-        if (lastUncomittedContent->isContainerStart() || lastUncomittedContent->isContainerEnd()) {
-            // [container start][container end] (<span><span>) or
-            // [container end][container start] (</span><span>) : need more content to decide.
-            return { };
+    while (*startContent != end) {
+        // 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 || nextContent.isAtLineBreak())
+            return *nextContent;
+        if (isAtSoftWrapOpportunity(*inlineContent[*startContent], *inlineContent[*nextContent])) {
+            // 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) {
+                if (inlineContent[candidateIndex]->isContainerStart()) {
+                    // inline content and [container start] and [container end] form unbreakable content.
+                    // ex-<span></span>ample  : wrap opportunity is after "ex-".
+                    // ex-</span></span>ample : wrap opportunity is after "ex-</span></span>".
+                    // ex-</span><span>ample</span> : wrap opportunity is after "ex-</span>".
+                    // ex-<span><span>ample</span></span> : wrap opportunity is after "ex-".
+                    return candidateIndex;
+                }
+            }
+            return candidateIndex;
         }
-        if (lastUncomittedContent->isText()) {
-            // [ ][container start] ( <span>) : after [whitespace] position is a soft wrap opportunity.
-            // [text][container start] (text<span>) : Need more content to decide (e.g. text<span>text vs. text<span><img>).
-            return downcast<InlineTextItem>(*lastUncomittedContent).isWhitespace() ? makeOptional(priorContent.size()) : WTF::nullopt;
-        }
-        if (lastUncomittedContent->isBox()) {
-            // [inline box][container start] (<img><span>) : after [inline box] position is a soft wrap opportunity.
-            // [inline box][container end] (<img></span>) : the [inline box] and the [container end] form a continuous content.
-            return inlineItem.isContainerStart() ? makeOptional(priorContent.size()) : WTF::nullopt;
-        }
-        ASSERT_NOT_REACHED();
+        startContent = nextContent;
     }
-    ASSERT_NOT_REACHED();
-    return true;
+    return end;
 }
 
 LineBreaker::ContinousContent::ContinousContent(const RunList& runs)
@@ -401,7 +407,7 @@
     // Figure out the trailing collapsible state.
     for (auto& run : WTF::makeReversedRange(m_runs)) {
         auto& inlineItem = run.inlineItem;
-        if (inlineItem.isBox() || inlineItem.isLineBreak()) {
+        if (inlineItem.isBox()) {
             // We did reach a non-collapsible content. We have all the trailing whitespace now.
             break;
         }
@@ -426,8 +432,11 @@
     }
     // 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)
+    for (auto& run : m_runs) {
+        // Line break is not considered an inline content.
+        ASSERT(!run.inlineItem.isLineBreak());
         m_width += run.logicalWidth;
+    }
 }
 
 bool LineBreaker::ContinousContent::hasTextContentOnly() const

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h (253908 => 253909)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h	2019-12-25 13:09:04 UTC (rev 253908)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h	2019-12-25 15:52:28 UTC (rev 253909)
@@ -67,7 +67,7 @@
         InlineLayoutUnit logicalWidth { 0 };
     };
     using RunList = Vector<Run, 30>;
-    static Optional<size_t> lastSoftWrapOpportunity(const InlineItem&, const RunList& priorContent);
+    static size_t nextWrapOpportunity(const InlineItems&, unsigned startIndex);
 
     // This struct represents the amount of content committed to line breaking at a time e.g.
     // text content <span>span1</span>between<span>span2</span>

Modified: trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp (253908 => 253909)


--- trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp	2019-12-25 13:09:04 UTC (rev 253908)
+++ trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp	2019-12-25 15:52:28 UTC (rev 253909)
@@ -34,7 +34,7 @@
 namespace WebCore {
 namespace Layout {
 
-struct ContinousContent {
+struct LineCandidateContent {
     void append(const InlineItem&, Optional<InlineLayoutUnit> logicalWidth);
 
     bool hasIntrusiveFloats() const { return !m_floats.isEmpty(); }
@@ -41,16 +41,16 @@
     const LineBreaker::RunList& runs() const { return m_runs; }
     const LineLayoutContext::FloatList& floats() const { return m_floats; }
 
-    bool endsWithLineBreak() const { return m_endsWithLineBreak; }
-    void setEndsWithLineBreak() { m_endsWithLineBreak = true; }
+    bool isLineBreak() const { return m_isLineBreak; }
+    void setIsLineBreak();
 
 private:
     LineBreaker::RunList m_runs;
     LineLayoutContext::FloatList m_floats;
-    bool m_endsWithLineBreak { false };
+    bool m_isLineBreak { false };
 };
 
-void ContinousContent::append(const InlineItem& inlineItem, Optional<InlineLayoutUnit> logicalWidth)
+void LineCandidateContent::append(const InlineItem& inlineItem, Optional<InlineLayoutUnit> logicalWidth)
 {
     if (inlineItem.isFloat())
         return m_floats.append(makeWeakPtr(inlineItem));
@@ -57,6 +57,13 @@
     m_runs.append({ inlineItem, *logicalWidth });
 }
 
+void LineCandidateContent::setIsLineBreak()
+{
+    ASSERT(!hasIntrusiveFloats());
+    ASSERT(runs().isEmpty());
+    m_isLineBreak = true;
+}
+
 static InlineLayoutUnit inlineItemWidth(const FormattingContext& formattingContext, const InlineItem& inlineItem, InlineLayoutUnit contentLogicalLeft)
 {
     if (inlineItem.isLineBreak())
@@ -111,7 +118,11 @@
         // 2. Apply floats and shrink the available horizontal space e.g. <span>intru_<div style="float: left"></div>sive_float</span>.
         // 3. Check if the content fits the line and commit the content accordingly (full, partial or not commit at all).
         // 4. Return if we are at the end of the line either by not being able to fit more content or because of an explicit line break.
-        auto candidateContent = nextContinousContentForLine(currentItemIndex, partialLeadingContentLength, line.lineBox().logicalWidth());
+        auto candidateContent = nextContentForLine(currentItemIndex, partialLeadingContentLength, line.lineBox().logicalWidth());
+        if (candidateContent.isLineBreak()) {
+            line.append(*m_inlineItems[currentItemIndex], 0);
+            return close(line, leadingInlineItemIndex, ++committedInlineItemCount, { });
+        }
         if (candidateContent.hasIntrusiveFloats()) {
             // Add floats first because they shrink the available horizontal space for the rest of the content.
             auto floatContent = addFloatItems(line, candidateContent.floats());
@@ -121,7 +132,6 @@
                 return close(line, leadingInlineItemIndex, committedInlineItemCount, { });
             }
         }
-        ASSERT(!candidateContent.runs().isEmpty() || candidateContent.endsWithLineBreak() || candidateContent.hasIntrusiveFloats());
         if (!candidateContent.runs().isEmpty()) {
             // Now check if we can put this content on the current line.
             auto committedContent = placeInlineContentOnCurrentLine(line, candidateContent.runs());
@@ -131,14 +141,6 @@
                 return close(line, leadingInlineItemIndex, committedInlineItemCount, committedContent.partialContent);
             }
         }
-        // We could fit more content but sadly this line ends with an explicit line break.
-        if (candidateContent.endsWithLineBreak()) {
-            // Make sure the trailing line break ends up on the line too.
-            auto lineBreakIndex = leadingInlineItemIndex + committedInlineItemCount;
-            ASSERT(m_inlineItems[lineBreakIndex]->isLineBreak());
-            line.append(*m_inlineItems[lineBreakIndex], 0);
-            return close(line, leadingInlineItemIndex, ++committedInlineItemCount, { });
-        }
         currentItemIndex = leadingInlineItemIndex + committedInlineItemCount;
         partialLeadingContentLength = { };
     }
@@ -176,12 +178,16 @@
     return LineContent { trailingInlineItemIndex, partialContent, WTFMove(m_floats), line.close(isLastLineWithInlineContent), line.lineBox() };
 }
 
-ContinousContent LineLayoutContext::nextContinousContentForLine(unsigned inlineItemIndex, Optional<unsigned> partialLeadingContentLength, InlineLayoutUnit currentLogicalRight)
+LineCandidateContent LineLayoutContext::nextContentForLine(unsigned inlineItemIndex, Optional<unsigned> partialLeadingContentLength, InlineLayoutUnit currentLogicalRight)
 {
-    auto candidateContent = ContinousContent { };
     // 1. Simply add any overflow content from the previous line to the candidate content. It's always a text content.
     // 2. Find the next soft wrap position or explicit line break.
     // 3. Collect floats between the inline content.
+    auto softWrapOpportunityIndex = LineBreaker::nextWrapOpportunity(m_inlineItems, inlineItemIndex);
+    // softWrapOpportunityIndex == m_inlineItems.size() means we don't have any wrap opportunity in this content.
+    ASSERT(softWrapOpportunityIndex <= m_inlineItems.size());
+
+    auto candidateContent = LineCandidateContent { };
     if (partialLeadingContentLength) {
         // Handle leading partial content first (split text from the previous line).
         // Construct a partial leading inline item.
@@ -192,8 +198,16 @@
         ++inlineItemIndex;
     }
 
-    while (inlineItemIndex < m_inlineItems.size()) {
-        auto& inlineItem = *m_inlineItems[inlineItemIndex];
+    // Are we wrapping at a line break?
+    auto isSingleItem = softWrapOpportunityIndex < m_inlineItems.size() && inlineItemIndex + 1 == softWrapOpportunityIndex;
+    if (isSingleItem && m_inlineItems[inlineItemIndex]->isLineBreak()) {
+        candidateContent.setIsLineBreak();
+        return candidateContent;
+    }
+
+    for (auto index = inlineItemIndex; index < softWrapOpportunityIndex; ++index) {
+        auto& inlineItem = *m_inlineItems[index];
+        ASSERT(!inlineItem.isLineBreak());
         if (inlineItem.isFloat()) {
             // Floats are not part of the line context.
             // FIXME: Check if their width should be added to currentLogicalRight.
@@ -200,20 +214,9 @@
             candidateContent.append(inlineItem, { });
             continue;
         }
-        if (inlineItem.isLineBreak()) {
-            // We definitely need to stop at the line break.
-            candidateContent.setEndsWithLineBreak();
-            return candidateContent;
-        }
-        if (LineBreaker::lastSoftWrapOpportunity(inlineItem, candidateContent.runs())) {
-            // Let's wrap up this list, if we arrived to a soft wrap opportunity.
-            return candidateContent;
-        }
-
         auto inlineItenmWidth = inlineItemWidth(formattingContext(), inlineItem, currentLogicalRight);
         candidateContent.append(inlineItem, inlineItenmWidth);
         currentLogicalRight += inlineItenmWidth;
-        ++inlineItemIndex;
     }
     return candidateContent;
 }

Modified: trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.h (253908 => 253909)


--- trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.h	2019-12-25 13:09:04 UTC (rev 253908)
+++ trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.h	2019-12-25 15:52:28 UTC (rev 253909)
@@ -33,7 +33,7 @@
 namespace WebCore {
 namespace Layout {
 
-struct ContinousContent;
+struct LineCandidateContent;
 
 class LineLayoutContext {
 public:
@@ -59,7 +59,7 @@
         size_t count { 0 };
         Optional <LineContent::PartialContent> partialContent;
     };
-    ContinousContent nextContinousContentForLine(unsigned inlineItemIndex, Optional<unsigned> overflowLength, InlineLayoutUnit currentLogicalRight);
+    LineCandidateContent nextContentForLine(unsigned inlineItemIndex, Optional<unsigned> overflowLength, InlineLayoutUnit currentLogicalRight);
     CommittedContent addFloatItems(LineBuilder&, const FloatList&);
     CommittedContent placeInlineContentOnCurrentLine(LineBuilder&, const LineBreaker::RunList&);
     void commitContent(LineBuilder&, const LineBreaker::RunList&, Optional<LineBreaker::BreakingContext::PartialTrailingContent>);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to