[LFC][IFC] Add support for preserved new line (pre, pre-wrap, break-spaces, pre-line)
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 25 Oct 2019 15:12:37 +0000 (15:12 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 25 Oct 2019 15:12:37 +0000 (15:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=203395
<rdar://problem/56606243>

Reviewed by Antti Koivisto.

<pre>Let's wrap the line
when the inline item's type is forced line break.</pre>

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* layout/LayoutContext.h:
* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::collectInlineContent):
(WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
* layout/inlineformatting/InlineFormattingContextQuirks.cpp:
(WebCore::Layout::InlineFormattingContext::Quirks::lineDescentNeedsCollapsing const):
* layout/inlineformatting/InlineItem.cpp: Copied from Source/WebCore/layout/tableformatting/TableInvalidation.cpp.
(WebCore::Layout::InlineItem::isForcedLineBreak const):
(WebCore::Layout::InlineItem::isText const):
* layout/inlineformatting/InlineItem.h:
(WebCore::Layout::InlineItem::isBox const):
(WebCore::Layout::InlineItem::isText const): Deleted.
(WebCore::Layout::InlineItem::isHardLineBreak const): Deleted.
(WebCore::Layout::InlineItem::isLineBreak const): Deleted.
* layout/inlineformatting/InlineLine.cpp:
(WebCore::Layout::Line::close):
(WebCore::Layout::Line::alignContentVertically):
(WebCore::Layout::Line::append):
(WebCore::Layout::Line::appendLineBreak):
(WebCore::Layout::Line::adjustBaselineAndLineHeight):
(WebCore::Layout::Line::inlineItemContentHeight const):
(WebCore::Layout::Line::appendHardLineBreak): Deleted.
* layout/inlineformatting/InlineLine.h:
(WebCore::Layout::Line::Run::isText const):
(WebCore::Layout::Line::Run::isForcedLineBreak const):
(WebCore::Layout::Line::Run::isLineBreak const): Deleted.
* layout/inlineformatting/InlineLineLayout.cpp:
(WebCore::Layout::inlineItemWidth):
(WebCore::Layout::LineLayout::placeInlineItem):
(WebCore::Layout::LineLayout::shouldProcessUncommittedContent const):
* layout/inlineformatting/InlineTextItem.h:
(WebCore::Layout::InlineTextItem::isSegmentBreak const):
* layout/tableformatting/TableInvalidation.cpp:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@251590 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/layout/LayoutContext.h
Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp
Source/WebCore/layout/inlineformatting/InlineItem.cpp [new file with mode: 0644]
Source/WebCore/layout/inlineformatting/InlineItem.h
Source/WebCore/layout/inlineformatting/InlineLine.cpp
Source/WebCore/layout/inlineformatting/InlineLine.h
Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp
Source/WebCore/layout/inlineformatting/InlineTextItem.h
Source/WebCore/layout/tableformatting/TableInvalidation.cpp

index 4382d22..5ef218f 100644 (file)
@@ -1,3 +1,50 @@
+2019-10-25  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][IFC] Add support for preserved new line (pre, pre-wrap, break-spaces, pre-line)
+        https://bugs.webkit.org/show_bug.cgi?id=203395
+        <rdar://problem/56606243>
+
+        Reviewed by Antti Koivisto.
+
+        <pre>Let's wrap the line
+        when the inline item's type is forced line break.</pre>
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * layout/LayoutContext.h:
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::collectInlineContent):
+        (WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
+        * layout/inlineformatting/InlineFormattingContextQuirks.cpp:
+        (WebCore::Layout::InlineFormattingContext::Quirks::lineDescentNeedsCollapsing const):
+        * layout/inlineformatting/InlineItem.cpp: Copied from Source/WebCore/layout/tableformatting/TableInvalidation.cpp.
+        (WebCore::Layout::InlineItem::isForcedLineBreak const):
+        (WebCore::Layout::InlineItem::isText const):
+        * layout/inlineformatting/InlineItem.h:
+        (WebCore::Layout::InlineItem::isBox const):
+        (WebCore::Layout::InlineItem::isText const): Deleted.
+        (WebCore::Layout::InlineItem::isHardLineBreak const): Deleted.
+        (WebCore::Layout::InlineItem::isLineBreak const): Deleted.
+        * layout/inlineformatting/InlineLine.cpp:
+        (WebCore::Layout::Line::close):
+        (WebCore::Layout::Line::alignContentVertically):
+        (WebCore::Layout::Line::append):
+        (WebCore::Layout::Line::appendLineBreak):
+        (WebCore::Layout::Line::adjustBaselineAndLineHeight):
+        (WebCore::Layout::Line::inlineItemContentHeight const):
+        (WebCore::Layout::Line::appendHardLineBreak): Deleted.
+        * layout/inlineformatting/InlineLine.h:
+        (WebCore::Layout::Line::Run::isText const):
+        (WebCore::Layout::Line::Run::isForcedLineBreak const):
+        (WebCore::Layout::Line::Run::isLineBreak const): Deleted.
+        * layout/inlineformatting/InlineLineLayout.cpp:
+        (WebCore::Layout::inlineItemWidth):
+        (WebCore::Layout::LineLayout::placeInlineItem):
+        (WebCore::Layout::LineLayout::shouldProcessUncommittedContent const):
+        * layout/inlineformatting/InlineTextItem.h:
+        (WebCore::Layout::InlineTextItem::isSegmentBreak const):
+        * layout/tableformatting/TableInvalidation.cpp:
+
 2019-10-25  Chris Dumez  <cdumez@apple.com>
 
         DatabaseContext should not prevent entering the back/forward cache
index 89dcf8e..21d6e1e 100644 (file)
@@ -1439,6 +1439,7 @@ layout/inlineformatting/InlineFormattingContextGeometry.cpp
 layout/inlineformatting/InlineFormattingContextQuirks.cpp
 layout/inlineformatting/InlineFormattingState.cpp
 layout/inlineformatting/InlineInvalidation.cpp
+layout/inlineformatting/InlineItem.cpp
 layout/inlineformatting/InlineLine.cpp
 layout/inlineformatting/InlineLineBreaker.cpp
 layout/inlineformatting/InlineLineLayout.cpp
index 2171d65..76b93cb 100644 (file)
                6F73918C2106CEDD006AF262 /* LayoutUnits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LayoutUnits.h; sourceTree = "<group>"; };
                6F77868523491AC6004D9636 /* DisplayPainter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DisplayPainter.cpp; sourceTree = "<group>"; };
                6F77868723491AD7004D9636 /* DisplayPainter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DisplayPainter.h; sourceTree = "<group>"; };
+               6F7B8CEC23626E6600C9FF15 /* InlineItem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineItem.cpp; sourceTree = "<group>"; };
                6F7CA3C4208C2956002F29AB /* LayoutState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LayoutState.h; sourceTree = "<group>"; };
                6F7CA3C5208C2956002F29AB /* LayoutState.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LayoutState.cpp; sourceTree = "<group>"; };
                6F7CA3C8208C2B2E002F29AB /* InlineFormattingContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InlineFormattingContext.h; sourceTree = "<group>"; };
                                115CFA7C208B8E10001E6991 /* InlineFormattingState.h */,
                                1123AFDD209ABBBA00736ACC /* InlineInvalidation.cpp */,
                                1123AFDC209ABBBA00736ACC /* InlineInvalidation.h */,
+                               6F7B8CEC23626E6600C9FF15 /* InlineItem.cpp */,
                                6FE7CFA02177EEF1005B1573 /* InlineItem.h */,
                                6F0CD692229ED31900C5994E /* InlineLine.cpp */,
                                6F0CD694229ED32700C5994E /* InlineLine.h */,
index 1415ac1..68bd822 100644 (file)
@@ -33,6 +33,7 @@
 
 namespace WebCore {
 
+class GraphicsContext;
 class RenderView;
 
 namespace Layout {
index 19e5da3..40b697f 100644 (file)
@@ -353,7 +353,7 @@ void InlineFormattingContext::collectInlineContent()
             if (treatAsInlineContainer(layoutBox))
                 formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::ContainerEnd));
             else if (layoutBox.isLineBreakBox())
-                formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::HardLineBreak));
+                formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::LineBreakBox));
             else if (layoutBox.isFloatingPositioned())
                 formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::Float));
             else {
@@ -449,7 +449,7 @@ void InlineFormattingContext::setDisplayBoxesForLine(const LineLayout::LineConte
         auto& layoutBox = lineRun->layoutBox();
         auto& displayBox = formattingState.displayBox(layoutBox);
 
-        if (lineRun->isLineBreak()) {
+        if (lineRun->isForcedLineBreak()) {
             displayBox.setTopLeft(logicalRect.topLeft());
             displayBox.setContentBoxWidth(logicalRect.width());
             displayBox.setContentBoxHeight(logicalRect.height());
index 2f5e5bb..c1c2705 100644 (file)
@@ -47,7 +47,7 @@ bool InlineFormattingContext::Quirks::lineDescentNeedsCollapsing(const Line::Run
         if (run->isContainerEnd() || layoutBox.style().verticalAlign() != VerticalAlign::Baseline)
             continue;
 
-        if (run->isLineBreak())
+        if (run->isForcedLineBreak())
             return false;
         if (run->isText()) {
             if (!run->isVisuallyEmpty())
diff --git a/Source/WebCore/layout/inlineformatting/InlineItem.cpp b/Source/WebCore/layout/inlineformatting/InlineItem.cpp
new file mode 100644 (file)
index 0000000..6168980
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InlineItem.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "InlineTextItem.h"
+
+namespace WebCore {
+namespace Layout {
+
+bool InlineItem::isForcedLineBreak() const
+{
+    if (type() == Type::LineBreakBox)
+        return true;
+    if (type() != Type::Text || !static_cast<const InlineTextItem*>(this)->isSegmentBreak())
+        return false;
+    // Segment break with preserve new line style (white-space: pre, pre-wrap, break-spaces and pre-line)
+    return style().preserveNewline();
+}
+
+bool InlineItem::isText() const
+{
+    // If this setup turns out to be a perf hit, we could easily switch over to generate-the-narrow-type way.
+    return type() == Type::Text && !isForcedLineBreak();
+}
+
+}
+}
+#endif
index 7906196..8e107c9 100644 (file)
@@ -36,18 +36,17 @@ namespace Layout {
 class InlineItem : public CanMakeWeakPtr<InlineItem> {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    enum class Type { Text, HardLineBreak, Box, Float, ContainerStart, ContainerEnd };
+    enum class Type { Text, LineBreakBox, Box, Float, ContainerStart, ContainerEnd };
     InlineItem(const Box& layoutBox, Type);
 
     Type type() const { return m_type; }
     const Box& layoutBox() const { return m_layoutBox; }
     const RenderStyle& style() const { return m_layoutBox.style(); }
 
-    bool isText() const { return type() == Type::Text; }
+    bool isText() const;
     bool isBox() const { return type() == Type::Box; }
-    bool isHardLineBreak() const { return type() == Type::HardLineBreak; }
     bool isFloat() const { return type() == Type::Float; }
-    bool isLineBreak() const { return type() == Type::HardLineBreak; }
+    bool isForcedLineBreak() const;
     bool isContainerStart() const { return type() == Type::ContainerStart; }
     bool isContainerEnd() const { return type() == Type::ContainerEnd; }
 
index 059632c..ae064be 100644 (file)
@@ -138,8 +138,10 @@ Line::RunList Line::close()
             continue;
         }
         auto& currentRun = m_runList[index];
-        if (&currentRun->layoutBox() != &previousRun->layoutBox()) {
-            // Do not merge runs from different boxes (<span>foo</span><span>bar</span>).
+        if (!currentRun->isText() || &currentRun->layoutBox() != &previousRun->layoutBox()) {
+            // Do not merge runs from different boxes (<span>foo</span><span>bar</span>)
+            // or within the same layout box but with preserved \n
+            // (<span>text\n<span <- both the "text" and "\" belong to the same layout box)
             ++index;
             continue;
         }
@@ -183,7 +185,7 @@ void Line::alignContentVertically()
 
         switch (verticalAlign) {
         case VerticalAlign::Baseline:
-            if (run->isLineBreak() || run->isText())
+            if (run->isForcedLineBreak() || run->isText())
                 logicalTop = baselineOffset() - ascent;
             else if (run->isContainerStart()) {
                 auto& boxGeometry = formattingContext.geometryForBox(layoutBox);
@@ -303,8 +305,8 @@ LayoutUnit Line::trailingTrimmableWidth() const
 
 void Line::append(const InlineItem& inlineItem, LayoutUnit logicalWidth)
 {
-    if (inlineItem.isHardLineBreak())
-        return appendHardLineBreak(inlineItem);
+    if (inlineItem.isForcedLineBreak())
+        return appendLineBreak(inlineItem);
     if (is<InlineTextItem>(inlineItem))
         return appendTextContent(downcast<InlineTextItem>(inlineItem), logicalWidth);
     if (inlineItem.isContainerStart())
@@ -430,7 +432,7 @@ void Line::appendReplacedInlineBox(const InlineItem& inlineItem, LayoutUnit logi
         m_runList.last()->m_displayRun.setImage(*replaced->cachedImage());
 }
 
-void Line::appendHardLineBreak(const InlineItem& inlineItem)
+void Line::appendLineBreak(const InlineItem& inlineItem)
 {
     auto logicalRect = Display::Rect { };
     logicalRect.setLeft(contentLogicalWidth());
@@ -466,7 +468,7 @@ void Line::adjustBaselineAndLineHeight(const InlineItem& inlineItem)
         return;
     }
 
-    if (inlineItem.isText() || inlineItem.isHardLineBreak()) {
+    if (inlineItem.isText() || inlineItem.isForcedLineBreak()) {
         // For text content we set the baseline either through the initial strut (set by the formatting context root) or
         // through the inline container (start) -see above. Normally the text content itself does not stretch the line.
         if (!m_initialStrut)
@@ -531,7 +533,7 @@ LayoutUnit Line::inlineItemContentHeight(const InlineItem& inlineItem) const
 {
     ASSERT(!m_skipAlignment);
     auto& fontMetrics = inlineItem.style().fontMetrics();
-    if (inlineItem.isLineBreak() || is<InlineTextItem>(inlineItem))
+    if (inlineItem.isForcedLineBreak() || is<InlineTextItem>(inlineItem))
         return fontMetrics.height();
 
     auto& layoutBox = inlineItem.layoutBox();
index 1b1f6c7..527d41f 100644 (file)
@@ -76,9 +76,9 @@ public:
         bool isVisuallyEmpty() const { return m_isVisuallyEmpty; }
         bool isCollapsed() const { return m_isCollapsed; }
 
-        bool isText() const { return m_inlineItem.isText(); }
+        bool isText() const { return m_inlineItem.isText() && !isForcedLineBreak(); }
         bool isBox() const { return m_inlineItem.isBox(); }
-        bool isLineBreak() const { return m_inlineItem.isLineBreak(); }
+        bool isForcedLineBreak() const { return m_inlineItem.isForcedLineBreak(); }
         bool isContainerStart() const { return m_inlineItem.isContainerStart(); }
         bool isContainerEnd() const { return m_inlineItem.isContainerEnd(); }
 
@@ -126,7 +126,7 @@ private:
     void appendReplacedInlineBox(const InlineItem&, LayoutUnit logicalWidth);
     void appendInlineContainerStart(const InlineItem&, LayoutUnit logicalWidth);
     void appendInlineContainerEnd(const InlineItem&, LayoutUnit logicalWidth);
-    void appendHardLineBreak(const InlineItem&);
+    void appendLineBreak(const InlineItem&);
 
     void removeTrailingTrimmableContent();
     void alignContentHorizontally();
index ffb9cf3..28f3bcf 100644 (file)
@@ -37,7 +37,7 @@ namespace Layout {
 
 static LayoutUnit inlineItemWidth(const FormattingContext& formattingContext, const InlineItem& inlineItem, LayoutUnit contentLogicalLeft)
 {
-    if (inlineItem.isLineBreak())
+    if (inlineItem.isForcedLineBreak())
         return 0;
 
     if (is<InlineTextItem>(inlineItem)) {
@@ -182,8 +182,8 @@ LineLayout::IsEndOfLine LineLayout::placeInlineItem(const InlineItem& inlineItem
         m_lineHasIntrusiveFloat = true;
         return IsEndOfLine::No;
     }
-    // Explicit line breaks are also special.
-    if (inlineItem.isHardLineBreak()) {
+    // Forced line breaks are also special.
+    if (inlineItem.isForcedLineBreak()) {
         auto isEndOfLine = !m_uncommittedContent.isEmpty() ? processUncommittedContent() : IsEndOfLine::No;
         // When the uncommitted content fits(or the line is empty), add the line break to this line as well.
         if (isEndOfLine == IsEndOfLine::No) {
@@ -241,7 +241,7 @@ bool LineLayout::shouldProcessUncommittedContent(const InlineItem& inlineItem) c
     // [inline container start][text content][inline container end]
     // An incoming <img> box would enable us to commit the "<span>continuous</span>" content
     // while additional text content would not.
-    ASSERT(!inlineItem.isFloat() && !inlineItem.isHardLineBreak());
+    ASSERT(!inlineItem.isFloat() && !inlineItem.isForcedLineBreak());
     ASSERT(!m_uncommittedContent.isEmpty());
 
     auto* lastUncomittedContent = &m_uncommittedContent.runs().last().inlineItem;
index 70d0630..a214627 100644 (file)
@@ -48,7 +48,7 @@ public:
 
     bool isWhitespace() const;
     bool isCollapsible() const { return isWhitespace() && style().collapseWhiteSpace(); }
-    bool isSegmentBreak() const;
+    bool isSegmentBreak() const { return m_textItemType == TextItemType::SegmentBreak; }
 
     std::unique_ptr<InlineTextItem> left(unsigned length) const;
     std::unique_ptr<InlineTextItem> right(unsigned length) const;
@@ -63,11 +63,6 @@ private:
     TextItemType m_textItemType { TextItemType::Undefined };
 };
 
-inline bool InlineTextItem::isSegmentBreak() const
-{
-    return m_textItemType == TextItemType::SegmentBreak;
-}
-
 }
 }
 
index 9be75af..a982185 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "Invalidation.h"
 #include "LayoutBox.h"
+#include "LayoutContext.h"
 #include "TableFormattingState.h"
 #include <wtf/IsoMallocInlines.h>