[LFC][Floating] Add basic clearance support
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Aug 2018 14:48:10 +0000 (14:48 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Aug 2018 14:48:10 +0000 (14:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=188492

Reviewed by Antti Koivisto.

Source/WebCore:

Adjust final position of a block level box with clearance when float is present.

Test: fast/block/block-only/floating-left-and-right-with-clearance.html

* layout/FloatingContext.cpp:
(WebCore::Layout::FloatingContext::positionForFloat const):
(WebCore::Layout::FloatingContext::verticalPositionWithClearance const):
(WebCore::Layout::FloatingContext::alignWithContainingBlock const):
(WebCore::Layout::FloatingContext::toContainingBlock const):
(WebCore::Layout::FloatingContext::computePosition const): Deleted.
* layout/FloatingContext.h:
* layout/FloatingState.cpp:
(WebCore::Layout::FloatingState::bottom const):
* layout/FloatingState.h:
(WebCore::Layout::FloatingState::leftBottom const):
(WebCore::Layout::FloatingState::rightBottom const):
(WebCore::Layout::FloatingState::bottom const):
* layout/Verification.cpp:
(WebCore::Layout::LayoutContext::verifyAndOutputMismatchingLayoutTree const):
* layout/blockformatting/BlockFormattingContext.cpp:
(WebCore::Layout::BlockFormattingContext::layout const):
(WebCore::Layout::BlockFormattingContext::computeFloatingPosition const):
(WebCore::Layout::BlockFormattingContext::computeVerticalPositionWithClearance const):
* layout/blockformatting/BlockFormattingContext.h:
* layout/layouttree/LayoutBox.cpp:
(WebCore::Layout::Box::hasClearance const):
* layout/layouttree/LayoutBox.h:

Tools:

* LayoutReloaded/misc/LFC-passing-tests.txt:

LayoutTests:

* fast/block/block-only/floating-left-and-right-with-clearance-expected.txt: Added.
* fast/block/block-only/floating-left-and-right-with-clearance.html: Added.

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

14 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/block/block-only/floating-left-and-right-with-clearance-expected.txt [new file with mode: 0644]
LayoutTests/fast/block/block-only/floating-left-and-right-with-clearance.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/layout/FloatingContext.cpp
Source/WebCore/layout/FloatingContext.h
Source/WebCore/layout/FloatingState.cpp
Source/WebCore/layout/FloatingState.h
Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp
Source/WebCore/layout/blockformatting/BlockFormattingContext.h
Source/WebCore/layout/layouttree/LayoutBox.cpp
Source/WebCore/layout/layouttree/LayoutBox.h
Tools/ChangeLog
Tools/LayoutReloaded/misc/LFC-passing-tests.txt

index 1a4b395..1fa9e38 100644 (file)
@@ -1,3 +1,13 @@
+2018-08-13  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][Floating] Add basic clearance support
+        https://bugs.webkit.org/show_bug.cgi?id=188492
+
+        Reviewed by Antti Koivisto.
+
+        * fast/block/block-only/floating-left-and-right-with-clearance-expected.txt: Added.
+        * fast/block/block-only/floating-left-and-right-with-clearance.html: Added.
+
 2018-08-13  Zan Dobersek  <zdobersek@igalia.com>
 
         Unreviewed WPE gardening. Cleaned up test expectations for tests under
diff --git a/LayoutTests/fast/block/block-only/floating-left-and-right-with-clearance-expected.txt b/LayoutTests/fast/block/block-only/floating-left-and-right-with-clearance-expected.txt
new file mode 100644 (file)
index 0000000..5348d87
--- /dev/null
@@ -0,0 +1,19 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x518
+  RenderBlock {HTML} at (0,0) size 800x518
+    RenderBody {BODY} at (8,8) size 784x502
+      RenderBlock {DIV} at (0,0) size 502x502 [border: (1px solid #000000)]
+        RenderBlock {DIV} at (1,1) size 12x12 [border: (1px solid #000000)]
+        RenderBlock (floating) {DIV} at (1,13) size 52x102 [border: (1px solid #000000)]
+        RenderBlock {DIV} at (1,13) size 500x115 [border: (1px solid #008000)]
+          RenderBlock {DIV} at (11,102) size 12x12 [border: (1px solid #000000)]
+        RenderBlock {DIV} at (1,178) size 12x12 [border: (1px solid #000000)]
+        RenderBlock (floating) {DIV} at (449,190) size 52x22 [border: (1px solid #000000)]
+        RenderBlock {DIV} at (1,275) size 12x12 [border: (1px solid #000000)]
+        RenderBlock (floating) {DIV} at (1,287) size 52x102 [border: (1px solid #000000)]
+        RenderBlock (floating) {DIV} at (449,287) size 52x112 [border: (1px solid #000000)]
+        RenderBlock {DIV} at (1,399) size 12x12 [border: (1px solid #000000)]
+layer at (9,213) size 500x35
+  RenderBlock (relative positioned) {DIV} at (1,190) size 500x35 [border: (1px solid #008000)]
+    RenderBlock {DIV} at (1,22) size 12x12 [border: (1px solid #000000)]
diff --git a/LayoutTests/fast/block/block-only/floating-left-and-right-with-clearance.html b/LayoutTests/fast/block/block-only/floating-left-and-right-with-clearance.html
new file mode 100644 (file)
index 0000000..5bf1dc1
--- /dev/null
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+div {
+       border: 1px solid black;
+}
+</style>
+</head>
+<body>
+<div style="width: 500px; height: 500px;">
+       <div style="width: 10px; height: 10px;"></div>
+       <div style="float: left; width: 50px; height: 100px;"></div>
+       <div style="padding-left: 10px; border: 1px solid green">
+               <div style="clear: left; width: 10px; height: 10px;"></div>
+       </div>
+
+       <div style="width: 10px; height: 10px; margin-top: 50px;"></div>
+       <div style="float: right; width: 50px; height: 20px;"></div>
+       <div style="position: relative; top: 15px; 10px; border: 1px solid green">
+               <div style="clear: right; width: 10px; height: 10px;"></div>
+       </div>
+
+
+       <div style="width: 10px; height: 10px; margin-top: 50px;"></div>
+       <div style="float: left; width: 50px; height: 100px;"></div>
+       <div style="float: right; width: 50px; height: 110px;"></div>
+       <div style="clear: both; width: 10px; height: 10px;"></div>
+</div>
+</body>
+</html>
\ No newline at end of file
index 1c8c95e..53b6825 100644 (file)
@@ -1,3 +1,38 @@
+2018-08-13  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][Floating] Add basic clearance support
+        https://bugs.webkit.org/show_bug.cgi?id=188492
+
+        Reviewed by Antti Koivisto.
+
+        Adjust final position of a block level box with clearance when float is present.
+
+        Test: fast/block/block-only/floating-left-and-right-with-clearance.html
+
+        * layout/FloatingContext.cpp:
+        (WebCore::Layout::FloatingContext::positionForFloat const):
+        (WebCore::Layout::FloatingContext::verticalPositionWithClearance const):
+        (WebCore::Layout::FloatingContext::alignWithContainingBlock const):
+        (WebCore::Layout::FloatingContext::toContainingBlock const):
+        (WebCore::Layout::FloatingContext::computePosition const): Deleted.
+        * layout/FloatingContext.h:
+        * layout/FloatingState.cpp:
+        (WebCore::Layout::FloatingState::bottom const):
+        * layout/FloatingState.h:
+        (WebCore::Layout::FloatingState::leftBottom const):
+        (WebCore::Layout::FloatingState::rightBottom const):
+        (WebCore::Layout::FloatingState::bottom const):
+        * layout/Verification.cpp:
+        (WebCore::Layout::LayoutContext::verifyAndOutputMismatchingLayoutTree const):
+        * layout/blockformatting/BlockFormattingContext.cpp:
+        (WebCore::Layout::BlockFormattingContext::layout const):
+        (WebCore::Layout::BlockFormattingContext::computeFloatingPosition const):
+        (WebCore::Layout::BlockFormattingContext::computeVerticalPositionWithClearance const):
+        * layout/blockformatting/BlockFormattingContext.h:
+        * layout/layouttree/LayoutBox.cpp:
+        (WebCore::Layout::Box::hasClearance const):
+        * layout/layouttree/LayoutBox.h:
+
 2018-08-13  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
 
         Expose CloseEvent and CustomEvent to workers
index 6f78dfc..390b858 100644 (file)
@@ -113,7 +113,7 @@ FloatingContext::FloatingContext(FloatingState& floatingState)
 {
 }
 
-Position FloatingContext::computePosition(const Box& layoutBox) const
+Position FloatingContext::positionForFloat(const Box& layoutBox) const
 {
     ASSERT(layoutBox.isFloatingPositioned());
     FloatingState::FloatItem floatItem = { layoutBox, m_floatingState };
@@ -131,6 +131,50 @@ Position FloatingContext::computePosition(const Box& layoutBox) const
     return toContainingBlock(floatItem, floatPosition);
 }
 
+std::optional<LayoutUnit> FloatingContext::verticalPositionWithClearance(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.hasClearance());
+    ASSERT(layoutBox.isBlockLevelBox());
+
+    if (m_floatingState.isEmpty())
+        return { };
+
+    auto bottom = [&](std::optional<LayoutUnit> floatBottom) -> std::optional<LayoutUnit> {
+        // 'bottom' is in the formatting root's coordinate system.
+        if (!floatBottom)
+            return { };
+
+        // 9.5.2 Controlling flow next to floats: the 'clear' property
+        // Then the amount of clearance is set to the greater of:
+        //
+        // 1. The amount necessary to place the border edge of the block even with the bottom outer edge of the lowest float that is to be cleared.
+        // 2. The amount necessary to place the top border edge of the block at its hypothetical position.
+
+        auto absoluteDisplayBox = FormattingContext::mapToAncestor(layoutContext(), layoutBox, downcast<Container>(m_floatingState.root()));
+        if (floatBottom <= absoluteDisplayBox.rectWithMargin().top())
+            return { };
+
+        // The return vertical position is in the containing block's coordinate system.
+        auto* containingBlockDisplayBox = layoutContext().displayBoxForLayoutBox(*layoutBox.containingBlock());
+        return *floatBottom - containingBlockDisplayBox->top();
+    };
+
+    auto clear = layoutBox.style().clear();
+    auto& formattingContextRoot = layoutBox.formattingContextRoot();
+
+    if (clear == Clear::Left)
+        return bottom(m_floatingState.leftBottom(formattingContextRoot));
+
+    if (clear == Clear::Right)
+        return bottom(m_floatingState.rightBottom(formattingContextRoot));
+
+    if (clear == Clear::Both)
+        return bottom(m_floatingState.bottom(formattingContextRoot));
+
+    ASSERT_NOT_REACHED();
+    return { };
+}
+
 Position FloatingContext::floatingPosition(const FloatingState::FloatItem& floatItem) const
 {
     auto initialVerticalPosition = this->initialVerticalPosition(floatItem);
@@ -176,13 +220,13 @@ LayoutUnit FloatingContext::alignWithContainingBlock(const FloatingState::FloatI
 {
     // If there is no floating to align with, push the box to the left/right edge of its containing block's content box.
     // (Either there's no floats at all or this box does not fit at any vertical positions where the floats are.)
-    auto& containgBlockDisplayBox = floatItem.containingBlockDisplayBox();
-    auto containingBlockContentBoxLeft = containgBlockDisplayBox.left() + containgBlockDisplayBox.contentBoxLeft();
+    auto& containingBlockDisplayBox = floatItem.containingBlockDisplayBox();
+    auto containingBlockContentBoxLeft = containingBlockDisplayBox.left() + containingBlockDisplayBox.contentBoxLeft();
 
     if (floatItem.layoutBox().isLeftFloatingPositioned())
         return containingBlockContentBoxLeft;
 
-    return containingBlockContentBoxLeft + containgBlockDisplayBox.contentBoxWidth() - floatItem.displayBox().marginBox().width();
+    return containingBlockContentBoxLeft + containingBlockDisplayBox.contentBoxWidth() - floatItem.displayBox().marginBox().width();
 }
 
 LayoutUnit FloatingContext::alignWithFloatings(const FloatingPair& floatingPair, const FloatingState::FloatItem& floatItem) const
@@ -227,8 +271,8 @@ Position FloatingContext::toContainingBlock(const FloatingState::FloatItem& floa
     if (&floatItem.containingBlock() == &m_floatingState.root())
         return position;
 
-    auto& containgBlockDisplayBox = floatItem.containingBlockDisplayBox();
-    return { position.x - containgBlockDisplayBox.left(), position.y - containgBlockDisplayBox.top() };
+    auto& containingBlockDisplayBox = floatItem.containingBlockDisplayBox();
+    return { position.x - containingBlockDisplayBox.left(), position.y - containingBlockDisplayBox.top() };
 }
 
 FloatingPair::FloatingPair(const FloatingState::FloatList& floats)
index cdb4cc2..d903a93 100644 (file)
@@ -49,7 +49,8 @@ public:
 
     FloatingState& floatingState() const { return m_floatingState; }
 
-    Position computePosition(const Box&) const;
+    Position positionForFloat(const Box&) const;
+    std::optional<LayoutUnit> verticalPositionWithClearance(const Box&) const;
 
 private:
     LayoutContext& layoutContext() const { return m_floatingState.layoutContext(); }
index be0ddd0..91d7c2e 100644 (file)
@@ -90,7 +90,7 @@ void FloatingState::append(const Box& layoutBox)
     m_floats.append({ layoutBox, *this });
 }
 
-std::optional<LayoutUnit> FloatingState::bottom(const Box& formattingContextRoot) const
+std::optional<LayoutUnit> FloatingState::bottom(const Box& formattingContextRoot, Clear type) const
 {
     if (m_floats.isEmpty())
         return { };
@@ -103,6 +103,10 @@ std::optional<LayoutUnit> FloatingState::bottom(const Box& formattingContextRoot
         if (&formattingContextRoot != &floatItem.layoutBox().formattingContextRoot())
             continue;
 
+        if ((type == Clear::Left && !floatItem.layoutBox().isLeftFloatingPositioned())
+            || (type == Clear::Right && !floatItem.layoutBox().isRightFloatingPositioned()))
+            continue;
+
         auto floatsBottom = floatItem.displayBox().rectWithMargin().bottom();
         if (bottom) {
             bottom = std::max(*bottom, floatsBottom);
index 62cf25d..9a7e2b4 100644 (file)
@@ -51,6 +51,9 @@ public:
     void remove(const Box& layoutBox);
 
     bool isEmpty() const { return m_floats.isEmpty(); }
+
+    std::optional<LayoutUnit> leftBottom(const Box& formattingContextRoot) const;
+    std::optional<LayoutUnit> rightBottom(const Box& formattingContextRoot) const;
     std::optional<LayoutUnit> bottom(const Box& formattingContextRoot) const;
 
     class FloatItem {
@@ -81,11 +84,28 @@ private:
     LayoutContext& layoutContext() const { return m_layoutContext; }
     const Box& root() const { return *m_formattingContextRoot; }
 
+    std::optional<LayoutUnit> bottom(const Box& formattingContextRoot, Clear) const;
+
     LayoutContext& m_layoutContext;
     WeakPtr<Box> m_formattingContextRoot;
     FloatList m_floats;
 };
 
+inline std::optional<LayoutUnit> FloatingState::leftBottom(const Box& formattingContextRoot) const
+{ 
+    return bottom(formattingContextRoot, Clear::Left);
+}
+
+inline std::optional<LayoutUnit> FloatingState::rightBottom(const Box& formattingContextRoot) const
+{
+    return bottom(formattingContextRoot, Clear::Right);
+}
+
+inline std::optional<LayoutUnit> FloatingState::bottom(const Box& formattingContextRoot) const
+{
+    return bottom(formattingContextRoot, Clear::Both);
+}
+
 }
 }
 #endif
index 8cd6aa9..b82ec05 100644 (file)
@@ -110,8 +110,10 @@ void BlockFormattingContext::layout(LayoutContext& layoutContext, FormattingStat
             LOG_WITH_STREAM(FormattingContextLayout, stream << "[Compute] -> [Height][Margin] -> for layoutBox(" << &layoutBox << ")");
             // Formatting root boxes are special-cased and they don't come here.
             ASSERT(!layoutBox.establishesFormattingContext());
-
             computeHeightAndMargin(layoutContext, layoutBox, displayBox);
+            // Finalize position with clearance.
+            if (layoutBox.hasClearance())
+                computeVerticalPositionWithClearance(floatingContext, layoutBox, displayBox);
             if (!is<Container>(layoutBox))
                 continue;
             auto& container = downcast<Container>(layoutBox);
@@ -157,10 +159,17 @@ void BlockFormattingContext::computeStaticPosition(LayoutContext& layoutContext,
 void BlockFormattingContext::computeFloatingPosition(FloatingContext& floatingContext, const Box& layoutBox, Display::Box& displayBox) const
 {
     ASSERT(layoutBox.isFloatingPositioned());
-    displayBox.setTopLeft(floatingContext.computePosition(layoutBox));
+    displayBox.setTopLeft(floatingContext.positionForFloat(layoutBox));
     floatingContext.floatingState().append(layoutBox);
 }
 
+void BlockFormattingContext::computeVerticalPositionWithClearance(const FloatingContext& floatingContext, const Box& layoutBox, Display::Box& displayBox) const
+{
+    ASSERT(layoutBox.hasClearance());
+    if (auto verticalPositionWithClearance = floatingContext.verticalPositionWithClearance(layoutBox))
+        displayBox.setTop(*verticalPositionWithClearance);
+}
+
 void BlockFormattingContext::computeInFlowPositionedPosition(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
 {
     displayBox.setTopLeft(Geometry::inFlowPositionedPosition(layoutContext, layoutBox));
index cf5a6ea..41af118 100644 (file)
@@ -57,6 +57,7 @@ private:
 
     void computeStaticPosition(LayoutContext&, const Box&, Display::Box&) const override;
     void computeFloatingPosition(FloatingContext&, const Box&, Display::Box&) const;
+    void computeVerticalPositionWithClearance(const FloatingContext&, const Box&, Display::Box&) const;
     void computeInFlowPositionedPosition(LayoutContext&, const Box&, Display::Box&) const override;
     void computeInFlowWidthAndMargin(LayoutContext&, const Box&, Display::Box&) const;
     void computeInFlowHeightAndMargin(LayoutContext&, const Box&, Display::Box&) const;
index b602569..3bd0328 100644 (file)
@@ -114,6 +114,11 @@ bool Box::isRightFloatingPositioned() const
     return m_style.floating() == Float::Right;
 }
 
+bool Box::hasClearance() const
+{
+    return m_style.clear() != Clear::None;
+}
+
 const Container* Box::containingBlock() const
 {
     // The containing block in which the root element lives is a rectangle called the initial containing block.
index f544515..21dc657 100644 (file)
@@ -62,6 +62,7 @@ public:
     bool isFloatingPositioned() const;
     bool isLeftFloatingPositioned() const;
     bool isRightFloatingPositioned() const;
+    bool hasClearance() const;
 
     bool isFloatingOrOutOfFlowPositioned() const { return isFloatingPositioned() || isOutOfFlowPositioned(); }
 
index 76e7a2c..c496605 100644 (file)
@@ -1,3 +1,12 @@
+2018-08-13  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][Floating] Add basic clearance support
+        https://bugs.webkit.org/show_bug.cgi?id=188492
+
+        Reviewed by Antti Koivisto.
+
+        * LayoutReloaded/misc/LFC-passing-tests.txt:
+
 2018-08-12  Zalan Bujtas  <zalan@apple.com>
 
         [LFC] Float prev/next sibling should prevent top/bottom margin collapsing with parent.
index 45b2f64..aba76f4 100644 (file)
@@ -47,3 +47,4 @@ fast/block/block-only/floating-multiple-lefts-in-body.html
 fast/block/block-only/floating-multiple-lefts-multiple-lines.html
 fast/block/block-only/floating-multiple-lefts.html
 fast/block/block-only/floating-and-next-previous-inflow-with-margin.html
+fast/block/block-only/floating-left-and-right-with-clearance.html