Title: [267924] trunk/Source/WebCore
Revision
267924
Author
[email protected]
Date
2020-10-03 08:12:17 -0700 (Sat, 03 Oct 2020)

Log Message

[LFC][IFC][Soft hyphen] InlineContentBreaker should return Action::Revert when the trailing soft hyphen does not fit
https://bugs.webkit.org/show_bug.cgi?id=217269

Reviewed by Antti Koivisto.

A trailing soft hyphen turns action "Push" to action "Revert" when the hyphen overflows.
e.g <div>1&shy;2&shy;3&shy;4</div>
line has: 123&shy; and when '4' overflows InlineContentBreaker normally returns with Action::Push ('4' is pushed over to the next line).
However Action::Push turns the trailing soft hyphen into a visible hyphen and now we need to check if 'Push' is actually a 'Revert' instead.

* layout/inlineformatting/InlineContentBreaker.cpp:
(WebCore::Layout::InlineContentBreaker::processInlineContent):
(WebCore::Layout::InlineContentBreaker::processOverflowingContent const):
(WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const):
* layout/inlineformatting/InlineContentBreaker.h:
* layout/inlineformatting/InlineLine.cpp:
(WebCore::Layout::Line::initialize):
(WebCore::Layout::Line::appendWith):
(WebCore::Layout::Line::appendTextContent):
(WebCore::Layout::Line::appendNonReplacedInlineBox):
(WebCore::Layout::Line::appendLineBreak):
* layout/inlineformatting/InlineLine.h:
(WebCore::Layout::Line::trailingSoftHyphenWidth const):
* layout/inlineformatting/InlineLineBuilder.cpp:
(WebCore::Layout::LineBuilder::handleFloatsAndInlineContent):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (267923 => 267924)


--- trunk/Source/WebCore/ChangeLog	2020-10-03 15:02:22 UTC (rev 267923)
+++ trunk/Source/WebCore/ChangeLog	2020-10-03 15:12:17 UTC (rev 267924)
@@ -1,5 +1,33 @@
 2020-10-03  Zalan Bujtas  <[email protected]>
 
+        [LFC][IFC][Soft hyphen] InlineContentBreaker should return Action::Revert when the trailing soft hyphen does not fit
+        https://bugs.webkit.org/show_bug.cgi?id=217269
+
+        Reviewed by Antti Koivisto.
+
+        A trailing soft hyphen turns action "Push" to action "Revert" when the hyphen overflows.
+        e.g <div>1&shy;2&shy;3&shy;4</div>
+        line has: 123&shy; and when '4' overflows InlineContentBreaker normally returns with Action::Push ('4' is pushed over to the next line).
+        However Action::Push turns the trailing soft hyphen into a visible hyphen and now we need to check if 'Push' is actually a 'Revert' instead.
+
+        * layout/inlineformatting/InlineContentBreaker.cpp:
+        (WebCore::Layout::InlineContentBreaker::processInlineContent):
+        (WebCore::Layout::InlineContentBreaker::processOverflowingContent const):
+        (WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const):
+        * layout/inlineformatting/InlineContentBreaker.h:
+        * layout/inlineformatting/InlineLine.cpp:
+        (WebCore::Layout::Line::initialize):
+        (WebCore::Layout::Line::appendWith):
+        (WebCore::Layout::Line::appendTextContent):
+        (WebCore::Layout::Line::appendNonReplacedInlineBox):
+        (WebCore::Layout::Line::appendLineBreak):
+        * layout/inlineformatting/InlineLine.h:
+        (WebCore::Layout::Line::trailingSoftHyphenWidth const):
+        * layout/inlineformatting/InlineLineBuilder.cpp:
+        (WebCore::Layout::LineBuilder::handleFloatsAndInlineContent):
+
+2020-10-03  Zalan Bujtas  <[email protected]>
+
         Floating-point math causes shrink-wrapped content to line wrap sometimes
         https://bugs.webkit.org/show_bug.cgi?id=217136
         <rdar://problem/69801790>

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp (267923 => 267924)


--- trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp	2020-10-03 15:02:22 UTC (rev 267923)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp	2020-10-03 15:12:17 UTC (rev 267924)
@@ -159,7 +159,7 @@
         if (auto lastLineWrapOpportunityIndex = lastWrapOpportunityIndex(candidateContent.runs())) {
             auto isEligibleLineWrapOpportunity = [&] (auto& candidateItem) {
                 // Just check for leading collapsible whitespace for now.
-                if (!lineStatus.lineIsEmpty || !candidateItem.isText() || !downcast<InlineTextItem>(candidateItem).isWhitespace())
+                if (!lineStatus.isEmpty || !candidateItem.isText() || !downcast<InlineTextItem>(candidateItem).isWhitespace())
                     return true;
                 return shouldKeepBeginningOfLineWhitespace(candidateItem.style());
             };
@@ -169,6 +169,10 @@
                 m_hasWrapOpportunityAtPreviousPosition = true;
             }
         }
+    } else if (result.action == Result::Action::Push && lineStatus.trailingSoftHyphenWidth) {
+        // A trailing soft hyphen may turn action "Push" to action "Revert".
+        if (*lineStatus.trailingSoftHyphenWidth > lineStatus.availableWidth && isTextContentOnly(candidateContent))
+            result = { Result::Action::RevertToLastNonOverflowingWrapOpportunity, IsEndOfLine::Yes };
     }
     return result;
 }
@@ -192,7 +196,7 @@
         if (continuousContent.nonCollapsibleLogicalWidth() <= lineStatus.availableWidth)
             return { Result::Action::Keep, IsEndOfLine };
         // Now check if we can trim the line too.
-        if (lineStatus.lineHasFullyCollapsibleTrailingRun && continuousContent.isFullyCollapsible()) {
+        if (lineStatus.hasFullyCollapsibleTrailingRun && continuousContent.isFullyCollapsible()) {
             // If this new content is fully collapsible, it should surely fit.
             return { Result::Action::Keep, IsEndOfLine };
         }
@@ -214,8 +218,8 @@
                 // We tried to break the content but the available space can't even accommodate the first character.
                 // 1. Push the content over to the next line when we've got content on the line already.
                 // 2. Keep the first character on the empty line (or keep the whole run if it has only one character).
-                if (!lineStatus.lineIsEmpty)
-                    return { Result::Action::Push, IsEndOfLine::Yes, { } };
+                if (!lineStatus.isEmpty)
+                    return { Result::Action::Push, IsEndOfLine::Yes };
                 auto leadingTextRunIndex = *firstTextRunIndex(continuousContent);
                 auto& inlineTextItem = downcast<InlineTextItem>(continuousContent.runs()[leadingTextRunIndex].inlineItem);
                 ASSERT(inlineTextItem.length());
@@ -230,7 +234,7 @@
         }
     }
     // If we are not allowed to break this overflowing content, we still need to decide whether keep it or push it to the next line.
-    if (lineStatus.lineIsEmpty) {
+    if (lineStatus.isEmpty) {
         ASSERT(!m_hasWrapOpportunityAtPreviousPosition);
         return { Result::Action::Keep, IsEndOfLine::No };
     }
@@ -359,7 +363,6 @@
             return { };
 
         unsigned leftSideLength = runLength;
-        // FIXME: We might want to cache the hyphen width.
         auto& fontCascade = style.fontCascade();
         auto hyphenWidth = InlineLayoutUnit { fontCascade.width(TextRun { StringView { style.hyphenString() } }) };
         if (!findLastBreakablePosition) {

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h (267923 => 267924)


--- trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h	2020-10-03 15:02:22 UTC (rev 267923)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h	2020-10-03 15:12:17 UTC (rev 267924)
@@ -51,7 +51,9 @@
             Keep, // Keep content on the current line.
             Break, // Partial content is on the current line.
             Push, // Content is pushed to the next line.
-            RevertToLastWrapOpportunity // The current content overflows and can't get wrapped. The content needs to be reverted back to the last wrapping opportunity.
+            // The current content overflows and can't get broken up into smaller bits.
+            RevertToLastWrapOpportunity, // The content needs to be reverted back to the last wrap opportunity.
+            RevertToLastNonOverflowingWrapOpportunity // The content needs to be reverted back to a wrap opportunity that still fits the line.
         };
         struct PartialTrailingContent {
             size_t trailingRunIndex { 0 };
@@ -107,8 +109,9 @@
     struct LineStatus {
         InlineLayoutUnit availableWidth { 0 };
         InlineLayoutUnit collapsibleWidth { 0 };
-        bool lineHasFullyCollapsibleTrailingRun { false };
-        bool lineIsEmpty { true };
+        Optional<InlineLayoutUnit> trailingSoftHyphenWidth;
+        bool hasFullyCollapsibleTrailingRun { false };
+        bool isEmpty { true };
     };
     Result processInlineContent(const ContinuousContent&, const LineStatus&);
 

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp (267923 => 267924)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp	2020-10-03 15:02:22 UTC (rev 267923)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp	2020-10-03 15:12:17 UTC (rev 267924)
@@ -61,6 +61,7 @@
     m_contentLogicalWidth = { };
     m_isVisuallyEmpty = true;
     m_runs.clear();
+    m_trailingSoftHyphenWidth = { };
     m_trimmableTrailingContent.reset();
     m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = { };
 }
@@ -238,7 +239,7 @@
     else
         ASSERT_NOT_REACHED();
 
-    // Check if this freshly appended content makes the line visually non-empty.
+    // Check if this newly appended content makes the line visually non-empty.
     if (m_isVisuallyEmpty && !m_runs.isEmpty() && isRunVisuallyNonEmpty(m_runs.last()))
         m_isVisuallyEmpty = false;
 }
@@ -271,6 +272,7 @@
 
 void Line::appendTextContent(const InlineTextItem& inlineTextItem, InlineLayoutUnit logicalWidth, bool needsHyphen)
 {
+    auto& style = inlineTextItem.style();
     auto willCollapseCompletely = [&] {
         if (!inlineTextItem.isCollapsible())
             return false;
@@ -290,7 +292,7 @@
             ASSERT(run.isContainerStart() || run.isContainerEnd() || run.isWordBreakOpportunity());
         }
         // Leading whitespace.
-        return !isWhitespacePreserved(inlineTextItem.style());
+        return !isWhitespacePreserved(style);
     };
 
     if (willCollapseCompletely())
@@ -312,7 +314,7 @@
         m_runs.append({ inlineTextItem, contentLogicalWidth(), logicalWidth, needsHyphen });
     m_contentLogicalWidth += logicalWidth;
     // Set the trailing trimmable content.
-    if (inlineTextItem.isWhitespace() && !TextUtil::shouldPreserveTrailingWhitespace(inlineTextItem.style())) {
+    if (inlineTextItem.isWhitespace() && !TextUtil::shouldPreserveTrailingWhitespace(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())
@@ -321,8 +323,10 @@
     }
     // Any non-whitespace, no-trimmable content resets the existing trimmable.
     m_trimmableTrailingContent.reset();
-    if (!formattingContext().layoutState().shouldIgnoreTrailingLetterSpacing() && !inlineTextItem.isWhitespace() && inlineTextItem.style().letterSpacing() > 0)
-        m_trimmableTrailingContent.addPartiallyTrimmableContent(m_runs.size() - 1, inlineTextItem.style().letterSpacing());
+    if (!formattingContext().layoutState().shouldIgnoreTrailingLetterSpacing() && !inlineTextItem.isWhitespace() && style.letterSpacing() > 0)
+        m_trimmableTrailingContent.addPartiallyTrimmableContent(m_runs.size() - 1, style.letterSpacing());
+    if (inlineTextItem.hasTrailingSoftHyphen())
+        m_trailingSoftHyphenWidth = InlineLayoutUnit { style.fontCascade().width(TextRun { StringView { style.hyphenString() } }) };
 }
 
 void Line::appendNonReplacedInlineBox(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)
@@ -333,6 +337,7 @@
     m_runs.append({ inlineItem, contentLogicalWidth() + horizontalMargin.start, logicalWidth });
     m_contentLogicalWidth += logicalWidth + horizontalMargin.start + horizontalMargin.end;
     m_trimmableTrailingContent.reset();
+    m_trailingSoftHyphenWidth = { };
 }
 
 void Line::appendReplacedInlineBox(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)
@@ -344,6 +349,7 @@
 
 void Line::appendLineBreak(const InlineItem& inlineItem)
 {
+    m_trailingSoftHyphenWidth = { };
     if (inlineItem.isHardLineBreak())
         return m_runs.append({ inlineItem, contentLogicalWidth(), 0_lu });
     // Soft line breaks (preserved new line characters) require inline text boxes for compatibility reasons.

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.h (267923 => 267924)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.h	2020-10-03 15:02:22 UTC (rev 267923)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.h	2020-10-03 15:12:17 UTC (rev 267924)
@@ -56,6 +56,8 @@
     InlineLayoutUnit trimmableTrailingWidth() const { return m_trimmableTrailingContent.width(); }
     bool isTrailingRunFullyTrimmable() const { return m_trimmableTrailingContent.isTrailingRunFullyTrimmable(); }
 
+    Optional<InlineLayoutUnit> trailingSoftHyphenWidth() const { return m_trailingSoftHyphenWidth; }
+
     void moveLogicalLeft(InlineLayoutUnit);
     void moveLogicalRight(InlineLayoutUnit);
 
@@ -183,6 +185,7 @@
     InlineLayoutUnit m_lineLogicalLeft { 0 };
     InlineLayoutUnit m_horizontalConstraint { 0 };
     InlineLayoutUnit m_contentLogicalWidth { 0 };
+    Optional<InlineLayoutUnit> m_trailingSoftHyphenWidth { 0 };
     bool m_isVisuallyEmpty { true };
     Optional<bool> m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent;
 };

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (267923 => 267924)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2020-10-03 15:02:22 UTC (rev 267923)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2020-10-03 15:12:17 UTC (rev 267924)
@@ -592,7 +592,7 @@
     // Check if this new content fits.
     auto availableWidth = m_line.availableWidth() - floatContent.intrusiveWidth();
     auto isLineConsideredEmpty = m_line.isVisuallyEmpty() && !m_contentIsConstrainedByFloat;
-    auto lineStatus = InlineContentBreaker::LineStatus { availableWidth, m_line.trimmableTrailingWidth(), m_line.isTrailingRunFullyTrimmable(), isLineConsideredEmpty };
+    auto lineStatus = InlineContentBreaker::LineStatus { availableWidth, m_line.trimmableTrailingWidth(), m_line.trailingSoftHyphenWidth(), m_line.isTrailingRunFullyTrimmable(), isLineConsideredEmpty };
     auto result = inlineContentBreaker.processInlineContent(continuousInlineContent, lineStatus);
     if (result.lastWrapOpportunityItem)
         m_wrapOpportunityList.append(result.lastWrapOpportunityItem);
@@ -615,6 +615,11 @@
         ASSERT(!m_wrapOpportunityList.isEmpty());
         return { InlineContentBreaker::IsEndOfLine::Yes, { rebuildLine(layoutRange), true } };
     }
+    if (result.action == InlineContentBreaker::Result::Action::RevertToLastNonOverflowingWrapOpportunity) {
+        ASSERT(result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes);
+        ASSERT_NOT_IMPLEMENTED_YET();
+        return { InlineContentBreaker::IsEndOfLine::Yes, { } };
+    }
     if (result.action == InlineContentBreaker::Result::Action::Break) {
         ASSERT(result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes);
         // Commit the combination of full and partial content on the current line.
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to