Title: [267637] trunk
Revision
267637
Author
[email protected]
Date
2020-09-26 14:31:49 -0700 (Sat, 26 Sep 2020)

Log Message

[LFC][IFC] Add support for <wbr>
https://bugs.webkit.org/show_bug.cgi?id=217014

Reviewed by Antti Koivisto.

Source/WebCore:

Introduce the WordBreakOpportunity type of InlineItem and take it into account while searching for word break opportunities.
It generates an empty line run but it does not yet have geometry information.

Test: fast/layoutformattingcontext/wbr-simple.html

* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::collectInlineContentIfNeeded):
* layout/inlineformatting/InlineItem.h:
(WebCore::Layout::InlineItem::isWordBreakOpportunity const):
* layout/inlineformatting/InlineLine.cpp:
(WebCore::Layout::Line::appendWith):
(WebCore::Layout::Line::appendTextContent):
(WebCore::Layout::Line::appendWordBreakOpportunity):
* layout/inlineformatting/InlineLine.h:
(WebCore::Layout::Line::Run::isWordBreakOpportunity const):
* layout/inlineformatting/InlineLineBuilder.cpp:
(WebCore::Layout::nextWrapOpportunity):
(WebCore::Layout::LineCandidate::InlineContent::appendInlineItem):
(WebCore::Layout::LineBuilder::inlineItemWidth const):
(WebCore::Layout::LineBuilder::nextContentForLine):

LayoutTests:

* fast/layoutformattingcontext/wbr-simple-expected.html: Added.
* fast/layoutformattingcontext/wbr-simple.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (267636 => 267637)


--- trunk/LayoutTests/ChangeLog	2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/LayoutTests/ChangeLog	2020-09-26 21:31:49 UTC (rev 267637)
@@ -1,5 +1,15 @@
 2020-09-26  Zalan Bujtas  <[email protected]>
 
+        [LFC][IFC] Add support for <wbr>
+        https://bugs.webkit.org/show_bug.cgi?id=217014
+
+        Reviewed by Antti Koivisto.
+
+        * fast/layoutformattingcontext/wbr-simple-expected.html: Added.
+        * fast/layoutformattingcontext/wbr-simple.html: Added.
+
+2020-09-26  Zalan Bujtas  <[email protected]>
+
         [LFC][IFC] Line content logical width expands with run expansions (text-align: justify)
         https://bugs.webkit.org/show_bug.cgi?id=217011
 

Added: trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple-expected.html (0 => 267637)


--- trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple-expected.html	2020-09-26 21:31:49 UTC (rev 267637)
@@ -0,0 +1,8 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:LayoutFormattingContextEnabled=true internal:LayoutFormattingContextIntegrationEnabled=false ] -->
+<style>
+div {
+  background-color: green;
+}
+</style>
+<div style="width: 300px; height: 10px;"></div>
+<div style="width: 100px; height: 20px;"></div>

Added: trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple.html (0 => 267637)


--- trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple.html	                        (rev 0)
+++ trunk/LayoutTests/fast/layoutformattingcontext/wbr-simple.html	2020-09-26 21:31:49 UTC (rev 267637)
@@ -0,0 +1,11 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:LayoutFormattingContextEnabled=true internal:LayoutFormattingContextIntegrationEnabled=false ] -->
+<style>
+div {
+  background-color: green;
+  color: green;
+  font-family: Ahem;
+  font-size: 10px;
+}
+</style>
+<div style="width: 300px;">only_one_line<wbr>here</div>
+<div style="width: 100px">first_line<wbr>second_line</div>

Modified: trunk/Source/WebCore/ChangeLog (267636 => 267637)


--- trunk/Source/WebCore/ChangeLog	2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/ChangeLog	2020-09-26 21:31:49 UTC (rev 267637)
@@ -1,3 +1,31 @@
+2020-09-26  Zalan Bujtas  <[email protected]>
+
+        [LFC][IFC] Add support for <wbr>
+        https://bugs.webkit.org/show_bug.cgi?id=217014
+
+        Reviewed by Antti Koivisto.
+
+        Introduce the WordBreakOpportunity type of InlineItem and take it into account while searching for word break opportunities.
+        It generates an empty line run but it does not yet have geometry information.
+
+        Test: fast/layoutformattingcontext/wbr-simple.html
+
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::collectInlineContentIfNeeded):
+        * layout/inlineformatting/InlineItem.h:
+        (WebCore::Layout::InlineItem::isWordBreakOpportunity const):
+        * layout/inlineformatting/InlineLine.cpp:
+        (WebCore::Layout::Line::appendWith):
+        (WebCore::Layout::Line::appendTextContent):
+        (WebCore::Layout::Line::appendWordBreakOpportunity):
+        * layout/inlineformatting/InlineLine.h:
+        (WebCore::Layout::Line::Run::isWordBreakOpportunity const):
+        * layout/inlineformatting/InlineLineBuilder.cpp:
+        (WebCore::Layout::nextWrapOpportunity):
+        (WebCore::Layout::LineCandidate::InlineContent::appendInlineItem):
+        (WebCore::Layout::LineBuilder::inlineItemWidth const):
+        (WebCore::Layout::LineBuilder::nextContentForLine):
+
 2020-09-26  Alex Christensen  <[email protected]>
 
         Fix Big Sur clean build after r267613

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (267636 => 267637)


--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp	2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp	2020-09-26 21:31:49 UTC (rev 267637)
@@ -40,6 +40,7 @@
 #include "LayoutContext.h"
 #include "LayoutInitialContainingBlock.h"
 #include "LayoutInlineTextBox.h"
+#include "LayoutLineBreakBox.h"
 #include "LayoutReplacedBox.h"
 #include "LayoutState.h"
 #include "Logging.h"
@@ -370,9 +371,9 @@
 
         while (!layoutQueue.isEmpty()) {
             auto& layoutBox = *layoutQueue.takeLast();
-            if (layoutBox.isLineBreakBox()) {
-                // FIXME: Treat <wbr> as a word break opportunity instead.
-                formattingState.addInlineItem({ layoutBox, InlineItem::Type::HardLineBreak });
+            if (is<LineBreakBox>(layoutBox)) {
+                auto& lineBreakBox = downcast<LineBreakBox>(layoutBox);
+                formattingState.addInlineItem({ layoutBox, lineBreakBox.isOptional() ? InlineItem::Type::WordBreakOpportunity : InlineItem::Type::HardLineBreak });
             } else if (layoutBox.isFloatingPositioned())
                 formattingState.addInlineItem({ layoutBox, InlineItem::Type::Float });
             else if (layoutBox.isAtomicInlineLevelBox())

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineItem.h (267636 => 267637)


--- trunk/Source/WebCore/layout/inlineformatting/InlineItem.h	2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineItem.h	2020-09-26 21:31:49 UTC (rev 267637)
@@ -35,7 +35,7 @@
 
 class InlineItem {
 public:
-    enum class Type : uint8_t { Text, HardLineBreak, SoftLineBreak, Box, Float, ContainerStart, ContainerEnd };
+    enum class Type : uint8_t { Text, HardLineBreak, SoftLineBreak, WordBreakOpportunity, Box, Float, ContainerStart, ContainerEnd };
     InlineItem(const Box& layoutBox, Type);
 
     Type type() const { return m_type; }
@@ -46,6 +46,7 @@
     bool isBox() const { return type() == Type::Box; }
     bool isFloat() const { return type() == Type::Float; }
     bool isLineBreak() const { return isSoftLineBreak() || isHardLineBreak(); }
+    bool isWordBreakOpportunity() const { return type() == Type::WordBreakOpportunity; }
     bool isSoftLineBreak() const { return type() == Type::SoftLineBreak; }
     bool isHardLineBreak() const { return type() == Type::HardLineBreak; }
     bool isContainerStart() const { return type() == Type::ContainerStart; }

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp (267636 => 267637)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp	2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp	2020-09-26 21:31:49 UTC (rev 267637)
@@ -224,6 +224,8 @@
         appendTextContent(downcast<InlineTextItem>(inlineItem), inlineRunDetails.logicalWidth, inlineRunDetails.needsHyphen);
     else if (inlineItem.isLineBreak())
         appendLineBreak(inlineItem);
+    else if (inlineItem.isWordBreakOpportunity())
+        appendWordBreakOpportunity(inlineItem);
     else if (inlineItem.isContainerStart())
         appendInlineContainerStart(inlineItem, inlineRunDetails.logicalWidth);
     else if (inlineItem.isContainerEnd())
@@ -284,7 +286,7 @@
             // Not that when the inline container has preserve whitespace style, "<span style="white-space: pre">  </span> " <- this whitespace stays around.
             if (run.isText())
                 return run.hasCollapsibleTrailingWhitespace();
-            ASSERT(run.isContainerStart() || run.isContainerEnd());
+            ASSERT(run.isContainerStart() || run.isContainerEnd() || run.isWordBreakOpportunity());
         }
         // Leading whitespace.
         return !isWhitespacePreserved(inlineTextItem.style());
@@ -348,6 +350,11 @@
     m_runs.append({ downcast<InlineSoftLineBreakItem>(inlineItem), contentLogicalWidth() });
 }
 
+void Line::appendWordBreakOpportunity(const InlineItem& inlineItem)
+{
+    m_runs.append({ inlineItem, contentLogicalWidth(), 0_lu });
+}
+
 bool Line::isRunVisuallyNonEmpty(const Run& run) const
 {
     if (run.isText())
@@ -374,6 +381,9 @@
         return run.logicalWidth();
     }
 
+    if (run.isWordBreakOpportunity())
+        return false;
+
     ASSERT_NOT_REACHED();
     return false;
 }

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.h (267636 => 267637)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.h	2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.h	2020-09-26 21:31:49 UTC (rev 267637)
@@ -66,6 +66,7 @@
         bool isText() const { return m_type == InlineItem::Type::Text; }
         bool isBox() const { return m_type == InlineItem::Type::Box; }
         bool isLineBreak() const { return m_type == InlineItem::Type::HardLineBreak || m_type == InlineItem::Type::SoftLineBreak; }
+        bool isWordBreakOpportunity() const { return m_type == InlineItem::Type::WordBreakOpportunity; }
         bool isContainerStart() const { return m_type == InlineItem::Type::ContainerStart; }
         bool isContainerEnd() const { return m_type == InlineItem::Type::ContainerEnd; }
 
@@ -143,6 +144,7 @@
     void appendInlineContainerStart(const InlineItem&, InlineLayoutUnit logicalWidth);
     void appendInlineContainerEnd(const InlineItem&, InlineLayoutUnit logicalWidth);
     void appendLineBreak(const InlineItem&);
+    void appendWordBreakOpportunity(const InlineItem&);
 
     void removeTrailingTrimmableContent();
     void visuallyCollapsePreWrapOverflowContent();

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (267636 => 267637)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2020-09-26 21:08:55 UTC (rev 267636)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2020-09-26 21:31:49 UTC (rev 267637)
@@ -110,55 +110,41 @@
 
 static inline size_t nextWrapOpportunity(const InlineItems& inlineContent, size_t startIndex, const LineBuilder::InlineItemRange layoutRange)
 {
-    // 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>"
+    // 1. Find the start candidate by skipping leading non-content items e.g "<span><span>start". Opportunity is after "<span><span>".
+    // 2. Find the end candidate by skipping non-content items inbetween e.g. "<span><span>start</span>end". Opportunity is after "</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.
+    // 4. Any force line break/explicit wrap content inbetween is considered as 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 isAtLineBreak = false;
-
-    auto inlineItemIndexWithContent = [&] (auto index) {
-        // Note that floats are not part of the inline content. We should treat them as if they were not here as far as wrap opportunities are concerned.
-        // [text][float box][text] is essentially just [text][text]
-        for (; index < layoutRange.end; ++index) {
-            auto& inlineItem = inlineContent[index];
-            if (inlineItem.isText() || inlineItem.isBox())
-                return index;
-            if (inlineItem.isLineBreak()) {
-                isAtLineBreak = true;
-                return index;
-            }
+    // [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 previousInlineItemIndex = Optional<size_t> { };
+    for (auto index = startIndex; index < layoutRange.end; ++index) {
+        auto& inlineItem = inlineContent[index];
+        if (inlineItem.isLineBreak() || inlineItem.isWordBreakOpportunity()) {
+            // We always stop at explicit wrapping opportunities e.g. <br>. The wrap position is after the opportunity position.
+            return ++index;
         }
-        return layoutRange.end;
-    };
-
-    // Start at the first inline item with content.
-    // [container start][ex-] : start at [ex-]
-    auto startContentIndex = inlineItemIndexWithContent(startIndex);
-    if (isAtLineBreak) {
-        // Content starts with a line break. The wrap position is after the line break.
-        return startContentIndex + 1;
-    }
-
-    while (startContentIndex < layoutRange.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 nextContentIndex = inlineItemIndexWithContent(startContentIndex + 1);
-        if (nextContentIndex == layoutRange.end)
-            return nextContentIndex;
-        if (isAtLineBreak) {
-            // We always stop at line breaks. The wrap position is after the line break.
-            return nextContentIndex + 1;
+        if (inlineItem.isFloat()) {
+            // Floats are not part of the inline content. We ignore them as far as wrap opportunities are concerned.
+            // [text][float box][text] is essentially just [text][text]
+            continue;
         }
-        if (isAtSoftWrapOpportunity(inlineContent[startContentIndex], inlineContent[nextContentIndex])) {
-            // There's a soft wrap opportunity between the start and the nextContent.
+        if (inlineItem.isContainerStart() || inlineItem.isContainerEnd()) {
+            // There's no wrapping opportunity between <span>text, <span></span> or </span>text. 
+            continue;
+        }
+        ASSERT(inlineItem.isText() || inlineItem.isBox());
+        if (!previousInlineItemIndex) {
+            previousInlineItemIndex = index;
+            continue;
+        }
+        if (isAtSoftWrapOpportunity(inlineContent[*previousInlineItemIndex], inlineContent[index])) {
+            // There's a soft wrap opportunity between 'previousInlineItemIndex' and 'index'.
             // 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.
-            for (auto candidateIndex = startContentIndex + 1; candidateIndex < nextContentIndex; ++candidateIndex) {
+            for (auto candidateIndex = *previousInlineItemIndex + 1; candidateIndex < index; ++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-".
@@ -168,9 +154,9 @@
                     return candidateIndex;
                 }
             }
-            return nextContentIndex;
+            return index;
         }
-        startContentIndex = nextContentIndex;
+        previousInlineItemIndex = index;
     }
     return layoutRange.end;
 }
@@ -247,7 +233,7 @@
             auto& inlineTextItem = downcast<InlineTextItem>(inlineItem);
             return inlineTextItem.isWhitespace() && !TextUtil::shouldPreserveTrailingWhitespace(inlineTextItem.style());
         }
-        if (inlineItem.isContainerStart() || inlineItem.isContainerEnd())
+        if (inlineItem.isContainerStart() || inlineItem.isContainerEnd() || inlineItem.isWordBreakOpportunity())
             return false;
         ASSERT_NOT_REACHED();
         return true;
@@ -310,8 +296,8 @@
         return TextUtil::width(inlineTextItem, inlineTextItem.start(), end, contentLogicalLeft);
     }
 
-    if (inlineItem.isLineBreak())
-        return 0;
+    if (inlineItem.isLineBreak() || inlineItem.isWordBreakOpportunity())
+        return { };
 
     auto& layoutBox = inlineItem.layoutBox();
     auto& boxGeometry = m_inlineFormattingContext.geometryForBox(layoutBox);
@@ -537,7 +523,7 @@
             accumulatedWidth += floatWidth;
             continue;
         }
-        if (inlineItem.isText() || inlineItem.isContainerStart() || inlineItem.isContainerEnd() || inlineItem.isBox()) {
+        if (inlineItem.isText() || inlineItem.isContainerStart() || inlineItem.isContainerEnd() || inlineItem.isBox() || inlineItem.isWordBreakOpportunity()) {
             auto inlineItenmWidth = inlineItemWidth(inlineItem, currentLogicalRight);
             lineCandidate.inlineContent.appendInlineItem(inlineItem, inlineItenmWidth);
             currentLogicalRight += inlineItenmWidth;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to