Title: [272168] trunk/Source/WebCore
Revision
272168
Author
[email protected]
Date
2021-02-01 12:51:53 -0800 (Mon, 01 Feb 2021)

Log Message

[LFC][IFC] "Find the trailing index" logic repeated in InlineContentBreaker::processOverflowingTextContent
https://bugs.webkit.org/show_bug.cgi?id=221204

Reviewed by Antti Koivisto.

Refactor InlineContentBreaker::processOverflowingTextContent as described below
1. find the overflowing run first
2. test if the overflowing run is breakable and try breaking it. If so, return the trailing content.
3. test if any run on the left side is breakable and try breaking it. If so, return the trailing content.
4. test if any run on the right side is breakable and try breaking it. If so, return the trailing content.
5. this content is not breakable

The "return the trailing content" code checks if we are at an inline box boundary and adjust the tailing index accordingly.
This was implemented for both #2 and #4. This logic now lives in findTrailingRunIndex.

* layout/inlineformatting/InlineContentBreaker.cpp:
(WebCore::Layout::InlineContentBreaker::processOverflowingTextContent const):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (272167 => 272168)


--- trunk/Source/WebCore/ChangeLog	2021-02-01 20:37:44 UTC (rev 272167)
+++ trunk/Source/WebCore/ChangeLog	2021-02-01 20:51:53 UTC (rev 272168)
@@ -1,3 +1,23 @@
+2021-02-01  Zalan Bujtas  <[email protected]>
+
+        [LFC][IFC] "Find the trailing index" logic repeated in InlineContentBreaker::processOverflowingTextContent
+        https://bugs.webkit.org/show_bug.cgi?id=221204
+
+        Reviewed by Antti Koivisto.
+
+        Refactor InlineContentBreaker::processOverflowingTextContent as described below
+        1. find the overflowing run first
+        2. test if the overflowing run is breakable and try breaking it. If so, return the trailing content.
+        3. test if any run on the left side is breakable and try breaking it. If so, return the trailing content.
+        4. test if any run on the right side is breakable and try breaking it. If so, return the trailing content.
+        5. this content is not breakable
+
+        The "return the trailing content" code checks if we are at an inline box boundary and adjust the tailing index accordingly.
+        This was implemented for both #2 and #4. This logic now lives in findTrailingRunIndex.
+
+        * layout/inlineformatting/InlineContentBreaker.cpp:
+        (WebCore::Layout::InlineContentBreaker::processOverflowingTextContent const):
+
 2021-02-01  Youenn Fablet  <[email protected]>
 
         ReadableStream C++ methods should check for exception

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp (272167 => 272168)


--- trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp	2021-02-01 20:37:44 UTC (rev 272167)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineContentBreaker.cpp	2021-02-01 20:51:53 UTC (rev 272168)
@@ -258,6 +258,9 @@
 
 Optional<TrailingTextContent> InlineContentBreaker::processOverflowingTextContent(const ContinuousContent& continuousContent, const LineStatus& lineStatus) const
 {
+    auto& runs = continuousContent.runs();
+    ASSERT(!runs.isEmpty());
+
     auto isBreakableRun = [] (auto& run) {
         ASSERT(run.inlineItem.isText() || run.inlineItem.isInlineBoxStart() || run.inlineItem.isInlineBoxEnd());
         if (!run.inlineItem.isText()) {
@@ -269,93 +272,113 @@
         return isWrappingAllowed(run.inlineItem.style());
     };
 
+    auto findTrailingRunIndex = [&] (auto breakableRunIndex) -> Optional<size_t> {
+        // When the breaking position is at the beginning of the run, the trailing run is the previous one.
+        if (!breakableRunIndex)
+            return { };
+        // Try not break content at inline box boundary
+        // e.g. <span>fits</span><span>overflows</span>
+        // when the text "overflows" completely overflows, let's break the content right before the '<span>'.
+        // FIXME: Add support for subsequent empty inline boxes e.g.
+        auto trailingCandidateIndex = breakableRunIndex - 1;
+        auto isAtInlineBox = runs[trailingCandidateIndex].inlineItem.isInlineBoxStart();
+        return !isAtInlineBox ? trailingCandidateIndex : trailingCandidateIndex ? makeOptional(trailingCandidateIndex - 1) : WTF::nullopt;
+    };
+
     // Check where the overflow occurs and use the corresponding style to figure out the breaking behaviour.
     // <span style="word-break: normal">first</span><span style="word-break: break-all">second</span><span style="word-break: normal">third</span>
-    auto& runs = continuousContent.runs();
-    auto accumulatedRunWidth = InlineLayoutUnit { }; 
-    size_t index = 0;
-    while (index < runs.size()) {
-        auto& run = runs[index];
-        ASSERT(run.inlineItem.isText() || run.inlineItem.isInlineBoxStart() || run.inlineItem.isInlineBoxEnd());
-        if (accumulatedRunWidth + run.logicalWidth > lineStatus.availableWidth && isBreakableRun(run)) {
-            // At this point the available width can very well be negative e.g. when some part of the continuous text content can not be broken into parts ->
-            // <span style="word-break: keep-all">textcontentwithnobreak</span><span>textcontentwithyesbreak</span>
-            // When the first span computes longer than the available space, by the time we get to the second span, the adjusted available space becomes negative.
-            auto adjustedAvailableWidth = std::max<InlineLayoutUnit>(0, lineStatus.availableWidth - accumulatedRunWidth);
-            if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + accumulatedRunWidth, adjustedAvailableWidth)) {
-                if (partialRun->length)
-                    return TrailingTextContent { index, false, partialRun };
-                // When the breaking position is at the beginning of the run, the trailing run is the previous one.
-                if (!index) {
-                    // Sometimes we can't accommodate even the very first character.
-                    // Note that this is different from when there's no breakable run in this set.
-                    return TrailingTextContent { { }, true, { } };
-                }
-                auto trailingCandidateIndex = index - 1;
-                // Try not break content at inline box boundary
-                // e.g. <span>fits</span><span>overflows</span>
-                // when the text "overflows" completely overflows, let's break the content right before the '<span>'.
-                auto isAtInlineBox = runs[trailingCandidateIndex].inlineItem.isInlineBoxStart();
-                if (!isAtInlineBox)
-                    return TrailingTextContent { trailingCandidateIndex, false, { } };
-                // Let's return the run right before the inline box start, or return nothing if we are at the beginning of the set.
-                // e.g. <span>this_is_breakable</span> vs. some_breakable_content<span>and_more</span>
-                if (trailingCandidateIndex)
-                    return TrailingTextContent { trailingCandidateIndex - 1, false, { } };
-                // This content starts with an inline box and clearly does not fit.
-                return TrailingTextContent { { }, true, { } };
-            }
-            // If this run is not breakable, we need to check if any previous or next runs are breakable.
+
+    // First find the overflowing run. 
+    auto accumulatedContentWidth = InlineLayoutUnit { };
+    auto overflowingRunIndex = runs.size(); 
+    for (size_t index = 0; index < runs.size(); ++index) {
+        auto runLogicalWidth = runs[index].logicalWidth;
+        if (accumulatedContentWidth + runLogicalWidth  > lineStatus.availableWidth) {
+            overflowingRunIndex = index;
             break;
         }
-        accumulatedRunWidth += run.logicalWidth;
-        ++index;
+        accumulatedContentWidth += runLogicalWidth;
     }
-    // We did not manage to break the run that actually overflows the line.
-    // Let's try to find the a previous breaking position starting from the overflowing run. It surely fits.
-    auto previousRunIndex = index;
-    auto previousAccumulatedContentWidth = accumulatedRunWidth;
-    while (previousRunIndex--) {
-        auto& run = runs[previousRunIndex];
-        previousAccumulatedContentWidth -= run.logicalWidth;
-        if (isBreakableRun(run)) {
+    // We have to have an overflowing run.
+    RELEASE_ASSERT(overflowingRunIndex < runs.size());
+
+    auto tryBreakingOverflowingRun = [&]() -> Optional<TrailingTextContent> {
+        auto overflowingRun = runs[overflowingRunIndex];
+        if (!isBreakableRun(overflowingRun))
+            return { };
+        if (auto partialRun = tryBreakingTextRun(overflowingRun, lineStatus.contentLogicalRight + accumulatedContentWidth, std::max(0.0f, lineStatus.availableWidth - accumulatedContentWidth))) {
+            if (partialRun->length)
+                return TrailingTextContent { overflowingRunIndex, false, partialRun };
+            // When the breaking position is at the beginning of the run, the trailing run is the previous one.
+            if (auto trailingRunIndex = findTrailingRunIndex(overflowingRunIndex))
+                return TrailingTextContent { *trailingRunIndex, false, { } };
+            // Sometimes we can't accommodate even the very first character. 
+            // Note that this is different from when there's no breakable run in this set.
+            return TrailingTextContent { { }, true, { } };
+        }
+        return { };
+    };
+    // Check if we actually break this run.
+    if (auto trailingContent = tryBreakingOverflowingRun())
+        return trailingContent;
+
+    auto tryBreakingPreviousNonOverflowingRuns = [&]() -> Optional<TrailingTextContent> {
+        auto previousContentWidth = accumulatedContentWidth;
+        for (auto index = overflowingRunIndex; index--;) {
+            auto& run = runs[index];
+            previousContentWidth -= run.logicalWidth;
+            if (!isBreakableRun(run))
+                continue;
             ASSERT(run.inlineItem.isText());
-            if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + previousAccumulatedContentWidth, { })) {
+            if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + previousContentWidth, { })) {
                 // 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 hypen position or the entire run is returned.
                 ASSERT(partialRun->length);
                 auto runIsFullyAccommodated = partialRun->length == downcast<InlineTextItem>(run.inlineItem).length();
-                return TrailingTextContent { previousRunIndex, false, runIsFullyAccommodated ? WTF::nullopt : partialRun };
+                return TrailingTextContent { index, false, runIsFullyAccommodated ? WTF::nullopt : partialRun };
             }
         }
-    }
+        return { };
+    };
+    // We did not manage to break the run that actually overflows the line.
+    // Let's try to find a previous breaking position starting from the overflowing run. It surely fits.
+    if (auto trailingContent = tryBreakingPreviousNonOverflowingRuns())
+        return trailingContent;
+
+    auto tryBreakingNextOverflowingRuns = [&]() -> Optional<TrailingTextContent> {
+        auto nextContentWidth = accumulatedContentWidth + runs[overflowingRunIndex].logicalWidth;
+        for (auto index = overflowingRunIndex + 1; index < runs.size(); ++index) {
+            auto& run = runs[index];
+            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) {
+                    // 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.
+                    ASSERT(runs[index - 1].inlineItem.isInlineBoxStart());
+                    auto trailingRunIndex = findTrailingRunIndex(index);
+                    ASSERT(trailingRunIndex);
+                    // At worst we are back to the overflowing run, like in the example above.
+                    ASSERT(*trailingRunIndex >= overflowingRunIndex);
+                    return TrailingTextContent { *trailingRunIndex, true, { } };
+                }
+                if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + nextContentWidth, { })) {
+                    ASSERT(partialRun->length);
+                    // We managed to break this text run mid content. It has to be a hyphen break.
+                    return TrailingTextContent { index, true, partialRun };
+                }
+            }
+            nextContentWidth += run.logicalWidth;
+        }
+        return { };
+    };
     // At this point we know that there's no breakable run all the way to the overflowing run.
     // Now we need to check if any run after the overflowing content can break.
     // e.g. <span>this_content_overflows_but_not_breakable<span><span style="word-break: break-all">but_this_is_breakable</span>
-    auto nextRunIndex = index;
-    auto nextAccumulatedContentWidth = accumulatedRunWidth;
-    while (++nextRunIndex < runs.size()) {
-        auto& run = runs[nextRunIndex];
-        nextAccumulatedContentWidth += run.logicalWidth;
-        if (isBreakableRun(run)) {
-            ASSERT(run.inlineItem.isText());
-            // We know that this run does not fit the available space. If we can break at any position, let's just use the start of the run.
-            if (wordBreakBehavior(run.inlineItem.style()) == 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 nextRunIndex - 1 and the trailing run is at nextRunIndex - 2.
-                ASSERT(runs[nextRunIndex - 1].inlineItem.isInlineBoxStart());
-                // At worst we are back to the overflowing run, like in the example above.
-                ASSERT(nextRunIndex - 2 >= index);
-                return TrailingTextContent { nextRunIndex - 2, true, { } };
-            }
-            if (auto partialRun = tryBreakingTextRun(run, lineStatus.contentLogicalRight + nextAccumulatedContentWidth, { })) {
-                ASSERT(partialRun->length);
-                // We managed to break this text run mid content. It has to be a hyphen break.
-                return TrailingTextContent { nextRunIndex, true, partialRun };
-            }
-        }
-    }
+    if (auto trailingContent = tryBreakingNextOverflowingRuns())
+        return trailingContent;
+
     // Give up, there's no breakable run in here.
     return { };
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to