Title: [272412] trunk/Source/WebCore
Revision
272412
Author
[email protected]
Date
2021-02-05 06:29:23 -0800 (Fri, 05 Feb 2021)

Log Message

[LFC][IFC] Incorrect last potential wrap position when inline box is present
https://bugs.webkit.org/show_bug.cgi?id=221437

Reviewed by Antti Koivisto.

The trailing run of the continuous content is not necessarily a legal wrap position.
In some cases when the text content is embedded in an inline box, the wrap position may be part of
the subsequent content e.g.

<div>content<span> <-space</span></div>

This maps to the following set of inline items:
[content][inline box start][ ][<-space][inline box end]

This content produces 3 sets of continuous content for line breaking
[non-whitespace content]
[inline box start][whitespace content]
[non-whitespace content][inline box end]

While the soft wrap opportunity is at [whitespace content], the content boundary is at a different position, right before
the [inline box start] since the content inside the inline box always belongs to the inline box.
(as opposed to [non-whitespace content][inline box start] and a separate [whitespace content] set)

In this patch we start tracking the "has trailing soft wrap opportunity" in LineCandidate and pass it to the InlineContentBreaker
as part of the line status.

* layout/inlineformatting/InlineContentBreaker.cpp:
(WebCore::Layout::InlineContentBreaker::isWrappingAllowed):
(WebCore::Layout::InlineContentBreaker::processInlineContent):
(WebCore::Layout::InlineContentBreaker::processOverflowingContent const):
(WebCore::Layout::InlineContentBreaker::processOverflowingContentWithText const):
(WebCore::Layout::InlineContentBreaker::wordBreakBehavior const):
(WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const):
(WebCore::Layout::isWrappingAllowed): Deleted.
(WebCore::Layout::lastWrapOpportunityIndex): Deleted.
* layout/inlineformatting/InlineContentBreaker.h:
* layout/inlineformatting/InlineLineBuilder.cpp:
(WebCore::Layout::LineCandidate::InlineContent::setHasTrailingSoftWrapOpportunity):
(WebCore::Layout::LineCandidate::InlineContent::hasTrailingSoftWrapOpportunity const):
(WebCore::Layout::LineBuilder::candidateContentForLine):
(WebCore::Layout::LineBuilder::handleInlineContent):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (272411 => 272412)


--- trunk/Source/WebCore/ChangeLog	2021-02-05 14:26:44 UTC (rev 272411)
+++ trunk/Source/WebCore/ChangeLog	2021-02-05 14:29:23 UTC (rev 272412)
@@ -1,3 +1,47 @@
+2021-02-05  Zalan Bujtas  <[email protected]>
+
+        [LFC][IFC] Incorrect last potential wrap position when inline box is present
+        https://bugs.webkit.org/show_bug.cgi?id=221437
+
+        Reviewed by Antti Koivisto.
+
+        The trailing run of the continuous content is not necessarily a legal wrap position.
+        In some cases when the text content is embedded in an inline box, the wrap position may be part of
+        the subsequent content e.g.
+        
+        <div>content<span> <-space</span></div>
+        
+        This maps to the following set of inline items:
+        [content][inline box start][ ][<-space][inline box end]
+        
+        This content produces 3 sets of continuous content for line breaking
+        [non-whitespace content]
+        [inline box start][whitespace content]
+        [non-whitespace content][inline box end]
+
+        While the soft wrap opportunity is at [whitespace content], the content boundary is at a different position, right before
+        the [inline box start] since the content inside the inline box always belongs to the inline box.
+        (as opposed to [non-whitespace content][inline box start] and a separate [whitespace content] set)
+
+        In this patch we start tracking the "has trailing soft wrap opportunity" in LineCandidate and pass it to the InlineContentBreaker
+        as part of the line status.
+
+        * layout/inlineformatting/InlineContentBreaker.cpp:
+        (WebCore::Layout::InlineContentBreaker::isWrappingAllowed):
+        (WebCore::Layout::InlineContentBreaker::processInlineContent):
+        (WebCore::Layout::InlineContentBreaker::processOverflowingContent const):
+        (WebCore::Layout::InlineContentBreaker::processOverflowingContentWithText const):
+        (WebCore::Layout::InlineContentBreaker::wordBreakBehavior const):
+        (WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const):
+        (WebCore::Layout::isWrappingAllowed): Deleted.
+        (WebCore::Layout::lastWrapOpportunityIndex): Deleted.
+        * layout/inlineformatting/InlineContentBreaker.h:
+        * layout/inlineformatting/InlineLineBuilder.cpp:
+        (WebCore::Layout::LineCandidate::InlineContent::setHasTrailingSoftWrapOpportunity):
+        (WebCore::Layout::LineCandidate::InlineContent::hasTrailingSoftWrapOpportunity const):
+        (WebCore::Layout::LineBuilder::candidateContentForLine):
+        (WebCore::Layout::LineBuilder::handleInlineContent):
+
 2021-02-05  Carlos Garcia Campos  <[email protected]>
 
         [SOUP] Fix error handling in WebKitFormDataInputStream

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp (272411 => 272412)


--- trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp	2021-02-05 14:26:44 UTC (rev 272411)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp	2021-02-05 14:29:23 UTC (rev 272412)
@@ -112,24 +112,13 @@
     return { };
 }
 
-static inline bool isWrappingAllowed(const RenderStyle& style)
+bool InlineContentBreaker::isWrappingAllowed(const InlineItem& inlineItem)
 {
+    auto& styleToUse = inlineItem.isBox() ? inlineItem.layoutBox().parent().style() : inlineItem.layoutBox().style(); 
     // Do not try to wrap overflown 'pre' and 'no-wrap' content to next line.
-    return style.whiteSpace() != WhiteSpace::Pre && style.whiteSpace() != WhiteSpace::NoWrap;
+    return styleToUse.whiteSpace() != WhiteSpace::Pre && styleToUse.whiteSpace() != WhiteSpace::NoWrap;
 }
 
-static inline Optional<size_t> lastWrapOpportunityIndex(const InlineContentBreaker::ContinuousContent::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;
-}
-
 bool InlineContentBreaker::shouldKeepEndOfLineWhitespace(const ContinuousContent& continuousContent) const
 {
     // Grab the style and check for white-space property to decide whether we should let this whitespace content overflow the current line.
@@ -154,31 +143,12 @@
     };
 
     auto result = processCandidateContent();
-    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(candidateContent.runs())) {
-            auto isEligibleLineWrapOpportunity = [&] (auto& candidateItem) {
-                // Just check for leading preserved whitespace for now.
-                if (lineStatus.hasContent || !is<InlineTextItem>(candidateItem))
-                    return true;
-                auto inlineTextItem = downcast<InlineTextItem>(candidateItem);
-                return !inlineTextItem.isWhitespace() || InlineTextItem::shouldPreserveSpacesAndTabs(inlineTextItem);
-            };
-            auto& lastWrapOpportunityCandidateItem = candidateContent.runs()[*lastLineWrapOpportunityIndex].inlineItem;
-            if (isEligibleLineWrapOpportunity(lastWrapOpportunityCandidateItem)) {
-                result.lastWrapOpportunityItem = &lastWrapOpportunityCandidateItem;
-                m_hasWrapOpportunityAtPreviousPosition = true;
-            }
-        }
-    } else if (result.action == Result::Action::Wrap) {
-        if (lineStatus.trailingSoftHyphenWidth && hasLeadingTextContent(candidateContent)) {
-            // A trailing soft hyphen with a wrapped text content turns into a visible hyphen.
-            // Let's check if there's enough space for the hyphen character.
-            auto hyphenOverflows = *lineStatus.trailingSoftHyphenWidth > lineStatus.availableWidth; 
-            auto action = "" ? Result::Action::RevertToLastNonOverflowingWrapOpportunity : Result::Action::WrapWithHyphen;
-            result = { action, IsEndOfLine::Yes };
-        }
+    if (result.action == Result::Action::Wrap && lineStatus.trailingSoftHyphenWidth && hasLeadingTextContent(candidateContent)) {
+        // A trailing soft hyphen with a wrapped text content turns into a visible hyphen.
+        // Let's check if there's enough space for the hyphen character.
+        auto hyphenOverflows = *lineStatus.trailingSoftHyphenWidth > lineStatus.availableWidth;
+        auto action = "" ? Result::Action::RevertToLastNonOverflowingWrapOpportunity : Result::Action::WrapWithHyphen;
+        result = { action, IsEndOfLine::Yes };
     }
     return result;
 }
@@ -274,23 +244,19 @@
     }
 
     // If we are not allowed to break this overflowing content, we still need to decide whether keep it or wrap it to the next line.
-    if (!lineStatus.hasContent) {
-        ASSERT(!m_hasWrapOpportunityAtPreviousPosition);
+    if (!lineStatus.hasContent)
         return { Result::Action::Keep, IsEndOfLine::No };
-    }
     // Now either wrap this content over to the next line or revert back to an earlier wrapping opportunity, or not wrap at all.
     auto shouldWrapUnbreakableContentToNextLine = [&] {
-        auto overflowingInlineItem = continuousContent.runs()[overflowingRunIndex].inlineItem;
-        auto& styleToUse = overflowingInlineItem.isBox() ? overflowingInlineItem.layoutBox().parent().style() : overflowingInlineItem.layoutBox().style(); 
         // The individual runs in this continuous content don't break, let's check if we are allowed to wrap this content to next line (e.g. pre would prevent us from wrapping).
         // Parent style drives the wrapping behavior here.
         // e.g. <div style="white-space: nowrap">some text<div style="display: inline-block; white-space: pre-wrap"></div></div>.
         // While the inline-block has pre-wrap which allows wrapping, the content lives in a nowrap context.
-        return isWrappingAllowed(styleToUse);
+        return isWrappingAllowed(continuousContent.runs()[overflowingRunIndex].inlineItem);
     };
     if (shouldWrapUnbreakableContentToNextLine())
         return { Result::Action::Wrap, IsEndOfLine::Yes };
-    if (m_hasWrapOpportunityAtPreviousPosition)
+    if (lineStatus.hasWrapOpportunityAtPreviousPosition)
         return { Result::Action::RevertToLastWrapOpportunity, IsEndOfLine::Yes };
     return { Result::Action::Keep, IsEndOfLine::No };
 }
@@ -308,7 +274,7 @@
             return false;
         }
         // Check if this text run needs to stay on the current line.  
-        return isWrappingAllowed(run.inlineItem.style());
+        return isWrappingAllowed(run.inlineItem);
     };
 
     auto findTrailingRunIndex = [&] (auto breakableRunIndex) -> Optional<size_t> {
@@ -345,7 +311,7 @@
         auto overflowingRun = runs[overflowingRunIndex];
         if (!isBreakableRun(overflowingRun))
             return { };
-        if (auto partialRun = tryBreakingTextRun(overflowingRun, lineStatus.contentLogicalRight + accumulatedContentWidth, std::max(0.0f, lineStatus.availableWidth - accumulatedContentWidth))) {
+        if (auto partialRun = tryBreakingTextRun(overflowingRun, lineStatus.contentLogicalRight + accumulatedContentWidth, std::max(0.0f, lineStatus.availableWidth - accumulatedContentWidth), lineStatus.hasWrapOpportunityAtPreviousPosition)) {
             if (partialRun->length)
                 return OverflowingTextContent::BreakingPosition { overflowingRunIndex, OverflowingTextContent::BreakingPosition::TrailingContent { false, partialRun } };
             // When the breaking position is at the beginning of the run, the trailing run is the previous one.
@@ -369,7 +335,7 @@
             if (!isBreakableRun(run))
                 continue;
             ASSERT(run.inlineItem.isText());
-            if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + previousContentWidth, { })) {
+            if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + previousContentWidth, { }, lineStatus.hasWrapOpportunityAtPreviousPosition)) {
                 // We know this run fits, so if breaking is allowed on the run, it should return a non-empty left-side
                 // since it's either at hyphen position or the entire run is returned.
                 ASSERT(partialRun->length);
@@ -391,7 +357,7 @@
             if (isBreakableRun(run)) {
                 ASSERT(run.inlineItem.isText());
                 // We know that this run does not fit the available space. If we can break it at any position, let's just use the start of the run.
-                if (wordBreakBehavior(run.inlineItem.style()) == WordBreakRule::AtArbitraryPosition) {
+                if (wordBreakBehavior(run.inlineItem.style(), lineStatus.hasWrapOpportunityAtPreviousPosition) == WordBreakRule::AtArbitraryPosition) {
                     // We must be on an inline box boundary. Let's go back to the run in front of the inline box start run.
                     // e.g. <span>unbreakable_and_overflow<span style="word-break: break-all">breakable</span>
                     // We are at "breakable", <span> is at index - 1 and the trailing run is at index - 2.
@@ -405,7 +371,7 @@
                     ASSERT(*trailingRunIndex >= overflowingRunIndex);
                     return OverflowingTextContent::BreakingPosition { *trailingRunIndex, OverflowingTextContent::BreakingPosition::TrailingContent { true } };
                 }
-                if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + nextContentWidth, { })) {
+                if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + nextContentWidth, { }, lineStatus.hasWrapOpportunityAtPreviousPosition)) {
                     ASSERT(partialRun->length);
                     // We managed to break this text run mid content. It has to be a hyphen break.
                     return OverflowingTextContent::BreakingPosition { index, OverflowingTextContent::BreakingPosition::TrailingContent { true, partialRun } };
@@ -425,7 +391,7 @@
     return { overflowingRunIndex };
 }
 
-InlineContentBreaker::WordBreakRule InlineContentBreaker::wordBreakBehavior(const RenderStyle& style) const
+InlineContentBreaker::WordBreakRule InlineContentBreaker::wordBreakBehavior(const RenderStyle& style, bool hasWrapOpportunityAtPreviousPosition) const
 {
     // Disregard any prohibition against line breaks mandated by the word-break property.
     // The different wrapping opportunities must not be prioritized. Hyphenation is not applied.
@@ -439,10 +405,10 @@
         return WordBreakRule::NoBreak;
     // For compatibility with legacy content, the word-break property also supports a deprecated break-word keyword.
     // When specified, this has the same effect as word-break: normal and overflow-wrap: anywhere, regardless of the actual value of the overflow-wrap property.
-    if (style.wordBreak() == WordBreak::BreakWord && !m_hasWrapOpportunityAtPreviousPosition)
+    if (style.wordBreak() == WordBreak::BreakWord && !hasWrapOpportunityAtPreviousPosition)
         return WordBreakRule::AtArbitraryPosition;
     // OverflowWrap::Break: An otherwise unbreakable sequence of characters may be broken at an arbitrary point if there are no otherwise-acceptable break points in the line.
-    if (style.overflowWrap() == OverflowWrap::Break && !m_hasWrapOpportunityAtPreviousPosition)
+    if (style.overflowWrap() == OverflowWrap::Break && !hasWrapOpportunityAtPreviousPosition)
         return WordBreakRule::AtArbitraryPosition;
 
     if (!n_hyphenationIsDisabled && style.hyphens() == Hyphens::Auto && canHyphenate(style.computedLocale()))
@@ -451,7 +417,7 @@
     return WordBreakRule::NoBreak;
 }
 
-Optional<InlineContentBreaker::PartialRun> InlineContentBreaker::tryBreakingTextRun(const ContinuousContent::Run& overflowingRun, InlineLayoutUnit logicalLeft, Optional<InlineLayoutUnit> availableWidth) const
+Optional<InlineContentBreaker::PartialRun> InlineContentBreaker::tryBreakingTextRun(const ContinuousContent::Run& overflowingRun, InlineLayoutUnit logicalLeft, Optional<InlineLayoutUnit> availableWidth, bool hasWrapOpportunityAtPreviousPosition) const
 {
     ASSERT(overflowingRun.inlineItem.isText());
     auto& inlineTextItem = downcast<InlineTextItem>(overflowingRun.inlineItem);
@@ -458,7 +424,7 @@
     auto& style = inlineTextItem.style();
     auto availableSpaceIsInfinite = !availableWidth.hasValue();
 
-    auto breakRule = wordBreakBehavior(style);
+    auto breakRule = wordBreakBehavior(style, hasWrapOpportunityAtPreviousPosition);
     if (breakRule == WordBreakRule::AtArbitraryPosition) {
         if (!inlineTextItem.length()) {
             // Empty text runs may be breakable based on style, but in practice we can't really split them any further.

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h (272411 => 272412)


--- trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h	2021-02-05 14:26:44 UTC (rev 272411)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h	2021-02-05 14:29:23 UTC (rev 272412)
@@ -111,15 +111,17 @@
         Optional<InlineLayoutUnit> trailingSoftHyphenWidth;
         bool hasFullyCollapsibleTrailingRun { false };
         bool hasContent { false };
+        bool hasWrapOpportunityAtPreviousPosition { false };
     };
     Result processInlineContent(const ContinuousContent&, const LineStatus&);
-
     void setHyphenationDisabled() { n_hyphenationIsDisabled = true; }
 
+    static bool isWrappingAllowed(const InlineItem&);
+
 private:
     Result processOverflowingContent(const ContinuousContent&, const LineStatus&) const;
     OverflowingTextContent processOverflowingContentWithText(const ContinuousContent&, const LineStatus&) const;
-    Optional<PartialRun> tryBreakingTextRun(const ContinuousContent::Run& overflowRun, InlineLayoutUnit logicalLeft, Optional<InlineLayoutUnit> availableWidth) const;
+    Optional<PartialRun> tryBreakingTextRun(const ContinuousContent::Run& overflowRun, InlineLayoutUnit logicalLeft, Optional<InlineLayoutUnit> availableWidth, bool hasWrapOpportunityAtPreviousPosition) const;
 
     enum class WordBreakRule {
         NoBreak,
@@ -126,11 +128,10 @@
         AtArbitraryPosition,
         OnlyHyphenationAllowed
     };
-    WordBreakRule wordBreakBehavior(const RenderStyle&) const;
+    WordBreakRule wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;
     bool shouldKeepEndOfLineWhitespace(const ContinuousContent&) const;
 
     bool n_hyphenationIsDisabled { false };
-    bool m_hasWrapOpportunityAtPreviousPosition { false };
 };
 
 inline InlineContentBreaker::ContinuousContent::Run::Run(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (272411 => 272412)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2021-02-05 14:26:44 UTC (rev 272411)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2021-02-05 14:29:23 UTC (rev 272412)
@@ -140,6 +140,9 @@
         bool isEmpty() const { return m_continuousContent.runs().isEmpty() && !trailingWordBreakOpportunity() && !trailingLineBreak(); }
         bool hasInlineLevelBox() const { return m_hasInlineLevelBox; }
 
+        void setHasTrailingSoftWrapOpportunity(bool hasTrailingSoftWrapOpportunity) { m_hasTrailingSoftWrapOpportunity = hasTrailingSoftWrapOpportunity; }
+        bool hasTrailingSoftWrapOpportunity() const { return m_hasTrailingSoftWrapOpportunity; }
+
     private:
         bool m_ignoreTrailingLetterSpacing { false };
 
@@ -147,6 +150,7 @@
         const InlineItem* m_trailingLineBreak { nullptr };
         const InlineItem* m_trailingWordBreakOpportunity { nullptr };
         bool m_hasInlineLevelBox { false };
+        bool m_hasTrailingSoftWrapOpportunity { false };
     };
 
     // Candidate content is a collection of inline content or a float box.
@@ -501,6 +505,39 @@
         }
         ASSERT_NOT_REACHED();
     }
+    auto inlineContentEndsInSoftWrapOpportunity = [&] {
+        if (!softWrapOpportunityIndex || softWrapOpportunityIndex == layoutRange.end) {
+            // This candidate inline content ends because the entire content ends and not because there's a soft wrap opportunity.
+            return false;
+        }
+        // See https://www.w3.org/TR/css-text-3/#line-break-details
+        auto& trailingInlineItem = m_inlineItems[softWrapOpportunityIndex - 1];
+        if (trailingInlineItem.isBox() || trailingInlineItem.isLineBreak() || trailingInlineItem.isWordBreakOpportunity() || trailingInlineItem.isInlineBoxEnd()) {
+            // For Web-compatibility there is a soft wrap opportunity before and after each replaced element or other atomic inline.
+            return true;
+        }
+        if (trailingInlineItem.isText()) {
+            auto& inlineTextItem = downcast<InlineTextItem>(trailingInlineItem);
+            if (inlineTextItem.isWhitespace())
+                return true;
+            // Now in case of non-whitespace trailing content, we need to check if the actual soft wrap opportunity belongs to the next set.
+            // e.g. "this_is_the_trailing_run<span> <-but_this_space_here_is_the_soft_wrap_opportunity"
+            // When there's an inline box start(<span>)/end(</span>) between the trailing and the (next)leading run, while we break before the inline box start (<span>)
+            // the actual soft wrap position is after the inline box start (<span>) but in terms of line breaking continuity the inline box start (<span>) and the whitespace run belong together.
+            RELEASE_ASSERT(layoutRange.end <= m_inlineItems.size());
+            for (auto index = softWrapOpportunityIndex; index < layoutRange.end; ++index) {
+                if (m_inlineItems[index].isInlineBoxStart() || m_inlineItems[index].isInlineBoxEnd())
+                    continue;
+                // FIXME: Check if [non-whitespace][inline-box][no-whitespace] content has rules about it.
+                // For now let's say the soft wrap position belongs to the next set of runs when [non-whitespace][inline-box][whitespace], [non-whitespace][inline-box][box] etc.
+                return m_inlineItems[index].isText() && !downcast<InlineTextItem>(m_inlineItems[index]).isWhitespace();
+            }
+            return true;
+        }
+        ASSERT_NOT_REACHED();
+        return true;
+    };
+    lineCandidate.inlineContent.setHasTrailingSoftWrapOpportunity(inlineContentEndsInSoftWrapOpportunity());
 }
 
 size_t LineBuilder::nextWrapOpportunity(size_t startIndex, const LineBuilder::InlineItemRange& layoutRange) const
@@ -630,10 +667,8 @@
     auto availableWidth = lineLogicalRectForCandidateContent.width() - m_line.contentLogicalRight();
     // While the floats are not considered to be on the line, they make the line contentful for line breaking.
     auto lineHasContent = !m_line.runs().isEmpty() || m_contentIsConstrainedByFloat;
-    auto lineStatus = InlineContentBreaker::LineStatus { m_line.contentLogicalRight(), availableWidth, m_line.trimmableTrailingWidth(), m_line.trailingSoftHyphenWidth(), m_line.isTrailingRunFullyTrimmable(), lineHasContent };
+    auto lineStatus = InlineContentBreaker::LineStatus { m_line.contentLogicalRight(), availableWidth, m_line.trimmableTrailingWidth(), m_line.trailingSoftHyphenWidth(), m_line.isTrailingRunFullyTrimmable(), lineHasContent, !m_wrapOpportunityList.isEmpty() };
     auto result = inlineContentBreaker.processInlineContent(continuousInlineContent, lineStatus);
-    if (result.lastWrapOpportunityItem)
-        m_wrapOpportunityList.append(result.lastWrapOpportunityItem);
     auto& candidateRuns = continuousInlineContent.runs();
     if (result.action == InlineContentBreaker::Result::Action::Keep) {
         // This continuous content can be fully placed on the current line.
@@ -640,6 +675,14 @@
         m_lineLogicalRect = lineLogicalRectForCandidateContent;
         for (auto& run : candidateRuns)
             m_line.append(run.inlineItem, run.logicalWidth);
+        if (lineCandidate.inlineContent.hasTrailingSoftWrapOpportunity()) {
+            // Check if we are allowed to wrap at this position.
+            auto& trailingItem = candidateRuns.last().inlineItem;
+            // FIXME: There must be a way to decide if the trailing run actually ended up on the line.
+            // Let's just deal with collapsed leading whitespace for now.
+            if (!m_line.runs().isEmpty() && InlineContentBreaker::isWrappingAllowed(trailingItem))
+                m_wrapOpportunityList.append(&trailingItem);
+        }
         return { result.isEndOfLine, { candidateRuns.size(), false } };
     }
     if (result.action == InlineContentBreaker::Result::Action::Wrap) {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to