Title: [274008] trunk
Revision
274008
Author
[email protected]
Date
2021-03-05 14:31:07 -0800 (Fri, 05 Mar 2021)

Log Message

word-wrap/overflow-wrap "overwrite" hyphens
https://bugs.webkit.org/show_bug.cgi?id=222548
<rdar://problem/75061741>

Reviewed by Antti Koivisto.

Source/WebCore:

According to https://drafts.csswg.org/css-text-3/#overflow-wrap-property

"overflow-wrap/word-wrap property specifies whether the UA may break at otherwise disallowed points within a line to prevent overflow,
when an otherwise-unbreakable string is too long to fit within the line box."

which means that in case of "hyphen: auto", we should only try to break the content at arbitrary position when there's no prior hyphenation opportunity.
This patch turns WordBreakRule into and OptionSet so that we can put both the hyphenation and arbitrary position break runles in there and prioritize them
in tryBreakingTextRun (check for the hyphenation value first/break the content at hyphenation position and do arbitrary position only
if no hyphenation opportunities are found).

Test: fast/inline/hyphenation-when-overflow-wrap-is-break-word.html

* layout/inlineformatting/InlineContentBreaker.cpp:
(WebCore::Layout::InlineContentBreaker::wordBreakBehavior const):
(WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const):
* layout/inlineformatting/InlineContentBreaker.h:

LayoutTests:

* fast/inline/hyphenation-when-overflow-wrap-is-break-word-expected.html: Added.
* fast/inline/hyphenation-when-overflow-wrap-is-break-word.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (274007 => 274008)


--- trunk/LayoutTests/ChangeLog	2021-03-05 22:28:15 UTC (rev 274007)
+++ trunk/LayoutTests/ChangeLog	2021-03-05 22:31:07 UTC (rev 274008)
@@ -1,3 +1,14 @@
+2021-03-05  Zalan Bujtas  <[email protected]>
+
+        word-wrap/overflow-wrap "overwrite" hyphens
+        https://bugs.webkit.org/show_bug.cgi?id=222548
+        <rdar://problem/75061741>
+
+        Reviewed by Antti Koivisto.
+
+        * fast/inline/hyphenation-when-overflow-wrap-is-break-word-expected.html: Added.
+        * fast/inline/hyphenation-when-overflow-wrap-is-break-word.html: Added.
+
 2021-03-05  Ryan Haddad  <[email protected]>
 
         REGRESSION (r269627?): ASSERTION FAILED: &layoutState().establishedFormattingState(layoutBox.formattingContextRoot()) == this in WebCore::Layout::FormattingState::boxGeometry

Added: trunk/LayoutTests/fast/inline/hyphenation-when-overflow-wrap-is-break-word-expected.html (0 => 274008)


--- trunk/LayoutTests/fast/inline/hyphenation-when-overflow-wrap-is-break-word-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hyphenation-when-overflow-wrap-is-break-word-expected.html	2021-03-05 22:31:07 UTC (rev 274008)
@@ -0,0 +1,9 @@
+<style>
+div {
+  font-size: 16px;
+  border: 1px solid green;
+  width: 50px;
+  -webkit-hyphens: auto;
+}
+</style>
+<div>Automatic hyphenation opportunities override arbitrary positions</div>

Added: trunk/LayoutTests/fast/inline/hyphenation-when-overflow-wrap-is-break-word.html (0 => 274008)


--- trunk/LayoutTests/fast/inline/hyphenation-when-overflow-wrap-is-break-word.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hyphenation-when-overflow-wrap-is-break-word.html	2021-03-05 22:31:07 UTC (rev 274008)
@@ -0,0 +1,11 @@
+<style>
+div {
+  font-size: 16px;
+  border: 1px solid green;
+  width: 50px;
+  -webkit-hyphens: auto;
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+}
+</style>
+<div>Automatic hyphenation opportunities override arbitrary positions</div>

Modified: trunk/Source/WebCore/ChangeLog (274007 => 274008)


--- trunk/Source/WebCore/ChangeLog	2021-03-05 22:28:15 UTC (rev 274007)
+++ trunk/Source/WebCore/ChangeLog	2021-03-05 22:31:07 UTC (rev 274008)
@@ -1,3 +1,28 @@
+2021-03-05  Zalan Bujtas  <[email protected]>
+
+        word-wrap/overflow-wrap "overwrite" hyphens
+        https://bugs.webkit.org/show_bug.cgi?id=222548
+        <rdar://problem/75061741>
+
+        Reviewed by Antti Koivisto.
+
+        According to https://drafts.csswg.org/css-text-3/#overflow-wrap-property
+
+        "overflow-wrap/word-wrap property specifies whether the UA may break at otherwise disallowed points within a line to prevent overflow,
+        when an otherwise-unbreakable string is too long to fit within the line box."
+
+        which means that in case of "hyphen: auto", we should only try to break the content at arbitrary position when there's no prior hyphenation opportunity.
+        This patch turns WordBreakRule into and OptionSet so that we can put both the hyphenation and arbitrary position break runles in there and prioritize them
+        in tryBreakingTextRun (check for the hyphenation value first/break the content at hyphenation position and do arbitrary position only
+        if no hyphenation opportunities are found).
+
+        Test: fast/inline/hyphenation-when-overflow-wrap-is-break-word.html
+
+        * layout/inlineformatting/InlineContentBreaker.cpp:
+        (WebCore::Layout::InlineContentBreaker::wordBreakBehavior const):
+        (WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const):
+        * layout/inlineformatting/InlineContentBreaker.h:
+
 2021-03-05  Lauro Moura  <[email protected]>
 
         Canvas: drawImage should normalize srcRect before checking if it's empty

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp (274007 => 274008)


--- trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp	2021-03-05 22:28:15 UTC (rev 274007)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp	2021-03-05 22:31:07 UTC (rev 274008)
@@ -392,30 +392,35 @@
     return { overflowingRunIndex };
 }
 
-InlineContentBreaker::WordBreakRule InlineContentBreaker::wordBreakBehavior(const RenderStyle& style, bool hasWrapOpportunityAtPreviousPosition) const
+OptionSet<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.
     if (style.lineBreak() == LineBreak::Anywhere)
-        return WordBreakRule::AtArbitraryPosition;
+        return { WordBreakRule::AtArbitraryPosition };
     // Breaking is allowed within “words”.
     if (style.wordBreak() == WordBreak::BreakAll)
-        return WordBreakRule::AtArbitraryPosition;
+        return { WordBreakRule::AtArbitraryPosition };
     // Breaking is forbidden within “words”.
     if (style.wordBreak() == WordBreak::KeepAll)
-        return WordBreakRule::NoBreak;
+        return { };
+
+    auto breakRules = OptionSet<WordBreakRule> { };
+    auto hyphenationIsAllowed = !n_hyphenationIsDisabled && style.hyphens() == Hyphens::Auto && canHyphenate(style.computedLocale());
+    if (hyphenationIsAllowed)
+        breakRules.add({ WordBreakRule::AtHyphenationOpportunities });
     // 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 && !hasWrapOpportunityAtPreviousPosition)
-        return WordBreakRule::AtArbitraryPosition;
+    if (style.wordBreak() == WordBreak::BreakWord && !hasWrapOpportunityAtPreviousPosition) {
+        breakRules.add({ WordBreakRule::AtArbitraryPosition });
+        return breakRules;
+    }
     // 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 && !hasWrapOpportunityAtPreviousPosition)
-        return WordBreakRule::AtArbitraryPosition;
-
-    if (!n_hyphenationIsDisabled && style.hyphens() == Hyphens::Auto && canHyphenate(style.computedLocale()))
-        return WordBreakRule::OnlyHyphenationAllowed;
-
-    return WordBreakRule::NoBreak;
+    if (style.overflowWrap() == OverflowWrap::Break && !hasWrapOpportunityAtPreviousPosition) {
+        breakRules.add({ WordBreakRule::AtArbitraryPosition });
+        return breakRules;
+    }
+    return breakRules;
 }
 
 Optional<InlineContentBreaker::PartialRun> InlineContentBreaker::tryBreakingTextRun(const ContinuousContent::Run& overflowingRun, InlineLayoutUnit logicalLeft, Optional<InlineLayoutUnit> availableWidth, bool hasWrapOpportunityAtPreviousPosition) const
@@ -425,64 +430,73 @@
     auto& style = inlineTextItem.style();
     auto availableSpaceIsInfinite = !availableWidth.hasValue();
 
-    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.
-            return PartialRun { };
-        }
-        if (availableSpaceIsInfinite) {
-            // When the run can be split at arbitrary position let's just return the entire run when it is intended to fit on the line.
-            ASSERT(inlineTextItem.length());
-            auto trailingPartialRunWidth = TextUtil::width(inlineTextItem, logicalLeft);
-            return PartialRun { inlineTextItem.length(), trailingPartialRunWidth };
-        }
-        if (!*availableWidth) {
-            // Fast path for cases when there's no room at all. The content is breakable but we don't have space for it.
-            return PartialRun { };
-        }
-        auto splitData = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, *availableWidth, logicalLeft);
-        return PartialRun { splitData.length, splitData.logicalWidth };
-    }
+    auto breakRules = wordBreakBehavior(style, hasWrapOpportunityAtPreviousPosition);
+    if (breakRules.isEmpty())
+        return { };
 
-    if (breakRule == WordBreakRule::OnlyHyphenationAllowed) {
-        // Find the hyphen position as follows:
-        // 1. Split the text by taking the hyphen width into account
-        // 2. Find the last hyphen position before the split position
-        if (!availableSpaceIsInfinite && !*availableWidth) {
-            // We won't be able to find hyphen location when there's no available space.
-            return { };
-        }
-        auto runLength = inlineTextItem.length();
-        unsigned limitBefore = style.hyphenationLimitBefore() == RenderStyle::initialHyphenationLimitBefore() ? 0 : style.hyphenationLimitBefore();
-        unsigned limitAfter = style.hyphenationLimitAfter() == RenderStyle::initialHyphenationLimitAfter() ? 0 : style.hyphenationLimitAfter();
-        // Check if this run can accommodate the before/after limits at all before start measuring text.
-        if (limitBefore >= runLength || limitAfter >= runLength || limitBefore + limitAfter > runLength)
-            return { };
+    if (breakRules.contains(WordBreakRule::AtHyphenationOpportunities)) {
+        auto tryBreakingAtHyphenationOpportunity = [&]() -> Optional<PartialRun> {
+            // Find the hyphen position as follows:
+            // 1. Split the text by taking the hyphen width into account
+            // 2. Find the last hyphen position before the split position
+            if (!availableSpaceIsInfinite && !*availableWidth) {
+                // We won't be able to find hyphen location when there's no available space.
+                return { };
+            }
+            auto runLength = inlineTextItem.length();
+            unsigned limitBefore = style.hyphenationLimitBefore() == RenderStyle::initialHyphenationLimitBefore() ? 0 : style.hyphenationLimitBefore();
+            unsigned limitAfter = style.hyphenationLimitAfter() == RenderStyle::initialHyphenationLimitAfter() ? 0 : style.hyphenationLimitAfter();
+            // Check if this run can accommodate the before/after limits at all before start measuring text.
+            if (limitBefore >= runLength || limitAfter >= runLength || limitBefore + limitAfter > runLength)
+                return { };
 
-        unsigned leftSideLength = runLength;
-        auto& fontCascade = style.fontCascade();
-        auto hyphenWidth = InlineLayoutUnit { fontCascade.width(TextRun { StringView { style.hyphenString() } }) };
-        if (!availableSpaceIsInfinite) {
-            auto availableWidthExcludingHyphen = *availableWidth - hyphenWidth;
-            if (availableWidthExcludingHyphen <= 0 || !enoughWidthForHyphenation(availableWidthExcludingHyphen, fontCascade.pixelSize()))
+            unsigned leftSideLength = runLength;
+            auto& fontCascade = style.fontCascade();
+            auto hyphenWidth = InlineLayoutUnit { fontCascade.width(TextRun { StringView { style.hyphenString() } }) };
+            if (!availableSpaceIsInfinite) {
+                auto availableWidthExcludingHyphen = *availableWidth - hyphenWidth;
+                if (availableWidthExcludingHyphen <= 0 || !enoughWidthForHyphenation(availableWidthExcludingHyphen, fontCascade.pixelSize()))
+                    return { };
+                leftSideLength = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, availableWidthExcludingHyphen, logicalLeft).length;
+            }
+            if (leftSideLength < limitBefore)
                 return { };
-            leftSideLength = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, availableWidthExcludingHyphen, logicalLeft).length;
-        }
-        if (leftSideLength < limitBefore)
-            return { };
-        // Adjust before index to accommodate the limit-after value (it's the last potential hyphen location in this run).
-        auto hyphenBefore = std::min(leftSideLength, runLength - limitAfter) + 1;
-        unsigned hyphenLocation = lastHyphenLocation(StringView(inlineTextItem.inlineTextBox().content()).substring(inlineTextItem.start(), inlineTextItem.length()), hyphenBefore, style.computedLocale());
-        if (!hyphenLocation || hyphenLocation < limitBefore)
-            return { };
-        // hyphenLocation is relative to the start of this InlineItemText.
-        ASSERT(inlineTextItem.start() + hyphenLocation < inlineTextItem.end());
-        auto trailingPartialRunWidthWithHyphen = TextUtil::width(inlineTextItem, inlineTextItem.start(), inlineTextItem.start() + hyphenLocation, logicalLeft); 
-        return PartialRun { hyphenLocation, trailingPartialRunWidthWithHyphen, hyphenWidth };
+            // Adjust before index to accommodate the limit-after value (it's the last potential hyphen location in this run).
+            auto hyphenBefore = std::min(leftSideLength, runLength - limitAfter) + 1;
+            unsigned hyphenLocation = lastHyphenLocation(StringView(inlineTextItem.inlineTextBox().content()).substring(inlineTextItem.start(), inlineTextItem.length()), hyphenBefore, style.computedLocale());
+            if (!hyphenLocation || hyphenLocation < limitBefore)
+                return { };
+            // hyphenLocation is relative to the start of this InlineItemText.
+            ASSERT(inlineTextItem.start() + hyphenLocation < inlineTextItem.end());
+            auto trailingPartialRunWidthWithHyphen = TextUtil::width(inlineTextItem, inlineTextItem.start(), inlineTextItem.start() + hyphenLocation, logicalLeft);
+            return PartialRun { hyphenLocation, trailingPartialRunWidthWithHyphen, hyphenWidth };
+        };
+        if (auto partialRun = tryBreakingAtHyphenationOpportunity())
+            return partialRun;
     }
 
-    ASSERT(breakRule == WordBreakRule::NoBreak);
+    if (breakRules.contains(WordBreakRule::AtArbitraryPosition)) {
+        auto tryBreakingAtArbitraryPosition = [&]() -> PartialRun {
+            if (!inlineTextItem.length()) {
+                // Empty text runs may be breakable based on style, but in practice we can't really split them any further.
+                return { };
+            }
+            if (availableSpaceIsInfinite) {
+                // When the run can be split at arbitrary position let's just return the entire run when it is intended to fit on the line.
+                ASSERT(inlineTextItem.length());
+                auto trailingPartialRunWidth = TextUtil::width(inlineTextItem, logicalLeft);
+                return { inlineTextItem.length(), trailingPartialRunWidth };
+            }
+            if (!*availableWidth) {
+                // Fast path for cases when there's no room at all. The content is breakable but we don't have space for it.
+                return { };
+            }
+            auto splitData = TextUtil::split(inlineTextItem, overflowingRun.logicalWidth, *availableWidth, logicalLeft);
+            return { splitData.length, splitData.logicalWidth };
+        };
+        // With arbitrary breaking there's always a valid breaking position (even if it is before the first position).
+        return tryBreakingAtArbitraryPosition();
+    }
     return { };
 }
 

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h (274007 => 274008)


--- trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h	2021-03-05 22:28:15 UTC (rev 274007)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.h	2021-03-05 22:31:07 UTC (rev 274008)
@@ -124,11 +124,10 @@
     Optional<PartialRun> tryBreakingTextRun(const ContinuousContent::Run& overflowRun, InlineLayoutUnit logicalLeft, Optional<InlineLayoutUnit> availableWidth, bool hasWrapOpportunityAtPreviousPosition) const;
 
     enum class WordBreakRule {
-        NoBreak,
-        AtArbitraryPosition,
-        OnlyHyphenationAllowed
+        AtArbitraryPosition        = 1 << 0,
+        AtHyphenationOpportunities = 1 << 1
     };
-    WordBreakRule wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;
+    OptionSet<WordBreakRule> wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;
     bool shouldKeepEndOfLineWhitespace(const ContinuousContent&) const;
 
     bool n_hyphenationIsDisabled { false };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to