[LFC] Implement BlockFormattingContext::layout logic and its dependencies
https://bugs.webkit.org/show_bug.cgi?id=185024

Reviewed by Antti Koivisto.

This patch implements the logic for block formatting context according to
https://www.w3.org/TR/CSS22/visuren.html#block-formatting

1. Traverse the tree iteratively (in post-order fashion) and compute the width/static position for the containers as
we visit the descendant nodes until we hit a leaf node.
2. Compute the position/geometry of the leaf node and move over to its sibling(s).
3. Finalize the container's height/final position as we climb back on the tree.
4. Run layout on the out-of-flow descendants.

Note that subtrees with a formatting context root need to be laid out completely before moving on to the next box.
The formatting root box is laid out in the formatting context it lives in, however its descendants get laid out
in a separate formatting context (excluding out-of-flow boxes that don't belong to the root).

* layout/FloatingContext.cpp:
(WebCore::Layout::FloatingContext::FloatingContext):
(WebCore::Layout::FloatingContext::computePosition):
* layout/FormattingContext.cpp:
(WebCore::Layout::FormattingContext::placeInFlowPositionedChildren const):
(WebCore::Layout::FormattingContext::layoutOutOfFlowDescendants const):
* layout/FormattingContext.h:
* layout/LayoutContext.cpp:
(WebCore::Layout::LayoutContext::updateLayout):
(WebCore::Layout::LayoutContext::establishedFormattingState):
* layout/LayoutContext.h:
* layout/blockformatting/BlockFormattingContext.cpp:
(WebCore::Layout::BlockFormattingContext::layout const):
(WebCore::Layout::BlockFormattingContext::layout): Deleted.
* layout/blockformatting/BlockFormattingContext.h:
* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::layout const):
(WebCore::Layout::InlineFormattingContext::layout): Deleted.
* layout/inlineformatting/InlineFormattingContext.h:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@231108 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 742c833..7d99846 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,43 @@
+2018-04-27  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC] Implement BlockFormattingContext::layout logic and its dependencies
+        https://bugs.webkit.org/show_bug.cgi?id=185024
+
+        Reviewed by Antti Koivisto.
+
+        This patch implements the logic for block formatting context according to
+        https://www.w3.org/TR/CSS22/visuren.html#block-formatting
+
+        1. Traverse the tree iteratively (in post-order fashion) and compute the width/static position for the containers as
+        we visit the descendant nodes until we hit a leaf node.
+        2. Compute the position/geometry of the leaf node and move over to its sibling(s).
+        3. Finalize the container's height/final position as we climb back on the tree.
+        4. Run layout on the out-of-flow descendants.  
+
+        Note that subtrees with a formatting context root need to be laid out completely before moving on to the next box.
+        The formatting root box is laid out in the formatting context it lives in, however its descendants get laid out
+        in a separate formatting context (excluding out-of-flow boxes that don't belong to the root). 
+
+        * layout/FloatingContext.cpp:
+        (WebCore::Layout::FloatingContext::FloatingContext):
+        (WebCore::Layout::FloatingContext::computePosition):
+        * layout/FormattingContext.cpp:
+        (WebCore::Layout::FormattingContext::placeInFlowPositionedChildren const):
+        (WebCore::Layout::FormattingContext::layoutOutOfFlowDescendants const):
+        * layout/FormattingContext.h:
+        * layout/LayoutContext.cpp:
+        (WebCore::Layout::LayoutContext::updateLayout):
+        (WebCore::Layout::LayoutContext::establishedFormattingState):
+        * layout/LayoutContext.h:
+        * layout/blockformatting/BlockFormattingContext.cpp:
+        (WebCore::Layout::BlockFormattingContext::layout const):
+        (WebCore::Layout::BlockFormattingContext::layout): Deleted.
+        * layout/blockformatting/BlockFormattingContext.h:
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::layout const):
+        (WebCore::Layout::InlineFormattingContext::layout): Deleted.
+        * layout/inlineformatting/InlineFormattingContext.h:
+
 2018-04-27  Youenn Fablet  <youenn@apple.com>
 
         Use NetworkLoadChecker for XHR/fetch loads
diff --git a/Source/WebCore/layout/FloatingContext.cpp b/Source/WebCore/layout/FloatingContext.cpp
index 92872aa3..f7430cd 100644
--- a/Source/WebCore/layout/FloatingContext.cpp
+++ b/Source/WebCore/layout/FloatingContext.cpp
@@ -25,3 +25,24 @@
 
 #include "config.h"
 #include "FloatingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(FloatingContext);
+
+FloatingContext::FloatingContext(FloatingState&)
+{
+}
+
+void FloatingContext::computePosition(const Box&)
+{
+}
+
+}
+}
+#endif
diff --git a/Source/WebCore/layout/FormattingContext.cpp b/Source/WebCore/layout/FormattingContext.cpp
index dca0475..9840a3f 100644
--- a/Source/WebCore/layout/FormattingContext.cpp
+++ b/Source/WebCore/layout/FormattingContext.cpp
@@ -85,6 +85,14 @@
     return 0;
 }
 
+void FormattingContext::placeInFlowPositionedChildren(const Container&) const
+{
+}
+
+void FormattingContext::layoutOutOfFlowDescendants() const
+{
+}
+
 }
 }
 #endif
diff --git a/Source/WebCore/layout/FormattingContext.h b/Source/WebCore/layout/FormattingContext.h
index bde31e2..c6c1461 100644
--- a/Source/WebCore/layout/FormattingContext.h
+++ b/Source/WebCore/layout/FormattingContext.h
@@ -37,6 +37,7 @@
 namespace Layout {
 
 class Box;
+class Container;
 class FormattingState;
 class LayoutContext;
 
@@ -46,7 +47,7 @@
     FormattingContext(const Box& formattingContextRoot, LayoutContext&);
     virtual ~FormattingContext();
 
-    virtual void layout(FormattingState&) = 0;
+    virtual void layout(LayoutContext&, FormattingState&) const = 0;
     virtual std::unique_ptr<FormattingState> createFormattingState(Ref<FloatingState>&&) const = 0;
     virtual Ref<FloatingState> createOrFindFloatingState() const = 0;
 
@@ -66,6 +67,9 @@
     virtual LayoutUnit marginBottom(const Box&) const;
     virtual LayoutUnit marginRight(const Box&) const;
 
+    void placeInFlowPositionedChildren(const Container&) const;
+    void layoutOutOfFlowDescendants() const;
+
 private:
     WeakPtr<Box> m_root;
     LayoutContext& m_layoutContext;
diff --git a/Source/WebCore/layout/LayoutContext.cpp b/Source/WebCore/layout/LayoutContext.cpp
index 4c9b6fd..9c2d4b7 100644
--- a/Source/WebCore/layout/LayoutContext.cpp
+++ b/Source/WebCore/layout/LayoutContext.cpp
@@ -50,7 +50,7 @@
 {
     auto context = formattingContext(*m_root);
     auto& state = establishedFormattingState(*m_root, *context);
-    context->layout(state);
+    context->layout(*this, state);
 }
 
 FormattingState& LayoutContext::formattingStateForBox(const Box& layoutBox) const
@@ -60,7 +60,7 @@
     return *m_formattingStates.get(&root);
 }
 
-FormattingState& LayoutContext::establishedFormattingState(Box& formattingContextRoot, const FormattingContext& context)
+FormattingState& LayoutContext::establishedFormattingState(const Box& formattingContextRoot, const FormattingContext& context)
 {
     return *m_formattingStates.ensure(&formattingContextRoot, [this, &context] {
         return context.createFormattingState(context.createOrFindFloatingState());
diff --git a/Source/WebCore/layout/LayoutContext.h b/Source/WebCore/layout/LayoutContext.h
index 6589e51..5768bd2 100644
--- a/Source/WebCore/layout/LayoutContext.h
+++ b/Source/WebCore/layout/LayoutContext.h
@@ -61,11 +61,10 @@
     bool needsLayout(const Box&) const;
 
     FormattingState& formattingStateForBox(const Box&) const;
-
-private:
-    FormattingState& establishedFormattingState(Box& formattingContextRoot, const FormattingContext&);
+    FormattingState& establishedFormattingState(const Box& formattingContextRoot, const FormattingContext&);
     std::unique_ptr<FormattingContext> formattingContext(const Box& formattingContextRoot);
 
+private:
     WeakPtr<Box> m_root;
     HashMap<const Box*, std::unique_ptr<FormattingState>> m_formattingStates;
 };
diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp b/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp
index e80545b..b57f1e7 100644
--- a/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp
+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp
@@ -29,7 +29,11 @@
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "BlockFormattingState.h"
+#include "FloatingContext.h"
 #include "FloatingState.h"
+#include "LayoutBox.h"
+#include "LayoutContainer.h"
+#include "LayoutContext.h"
 #include <wtf/IsoMallocInlines.h>
 
 namespace WebCore {
@@ -42,8 +46,63 @@
 {
 }
 
-void BlockFormattingContext::layout(FormattingState&)
+void BlockFormattingContext::layout(LayoutContext& layoutContext, FormattingState& formattingState) const
 {
+    // 9.4.1 Block formatting contexts
+    // In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block.
+    // The vertical distance between two sibling boxes is determined by the 'margin' properties.
+    // Vertical margins between adjacent block-level boxes in a block formatting context collapse.
+    if (!is<Container>(root()))
+        return;
+    auto& formattingRoot = downcast<Container>(root());
+    Vector<const Box*> layoutQueue;
+    FloatingContext floatingContext(formattingState.floatingState());
+    // This is a post-order tree traversal layout.
+    // The root container layout is done in the formatting context it lives in, not that one it creates, so let's start with the first child.
+    if (formattingRoot.hasInFlowOrFloatingChild())
+        layoutQueue.append(formattingRoot.firstInFlowOrFloatingChild());
+    // 1. Go all the way down to the leaf node
+    // 2. Compute static position and width as we traverse down
+    // 3. As we climb back on the tree, compute height and finialize position
+    // (Any subtrees with new formatting contexts need to layout synchronously)
+    while (!layoutQueue.isEmpty()) {
+        // Traverse down on the descendants and compute width/static position until we find a leaf node.
+        while (true) {
+            auto& layoutBox = *layoutQueue.last();
+            computeWidth(layoutBox);
+            computeStaticPosition(layoutBox);
+            if (layoutBox.establishesFormattingContext()) {
+                auto formattingContext = layoutContext.formattingContext(layoutBox);
+                formattingContext->layout(layoutContext, layoutContext.establishedFormattingState(layoutBox, *formattingContext));
+                break;
+            }
+            if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowOrFloatingChild())
+                break;
+            layoutQueue.append(downcast<Container>(layoutBox).firstInFlowOrFloatingChild());
+        }
+
+        // Climb back on the ancestors and compute height/final position.
+        while (!layoutQueue.isEmpty()) {
+            // All inflow descendants (if there are any) are laid out by now. Let's compute the box's height.
+            auto& layoutBox = *layoutQueue.takeLast();
+            computeHeight(layoutBox);
+            // Adjust position now that we have all the previous floats placed in this context -if needed.
+            floatingContext.computePosition(layoutBox);
+            if (!is<Container>(layoutBox))
+                continue;
+            auto& container = downcast<Container>(layoutBox);
+            // Move in-flow positioned children to their final position.
+            placeInFlowPositionedChildren(container);
+            if (auto* nextSibling = container.nextInFlowOrFloatingSibling()) {
+                layoutQueue.append(nextSibling);
+                break;
+            }
+        }
+    }
+    // Place the inflow positioned children.
+    placeInFlowPositionedChildren(formattingRoot);
+    // And take care of out-of-flow boxes as the final step.
+    layoutOutOfFlowDescendants();
 }
 
 std::unique_ptr<FormattingState> BlockFormattingContext::createFormattingState(Ref<FloatingState>&& floatingState) const
diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContext.h b/Source/WebCore/layout/blockformatting/BlockFormattingContext.h
index 026398b..aade5a6 100644
--- a/Source/WebCore/layout/blockformatting/BlockFormattingContext.h
+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContext.h
@@ -45,7 +45,7 @@
 public:
     BlockFormattingContext(const Box& formattingContextRoot, LayoutContext&);
 
-    void layout(FormattingState&) override;
+    void layout(LayoutContext&, FormattingState&) const override;
     std::unique_ptr<FormattingState> createFormattingState(Ref<FloatingState>&&) const override;
     Ref<FloatingState> createOrFindFloatingState() const override;
 
diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
index 22a4807..2235266b 100644
--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
@@ -44,7 +44,7 @@
 {
 }
 
-void InlineFormattingContext::layout(FormattingState&)
+void InlineFormattingContext::layout(LayoutContext&, FormattingState&) const
 {
 }
 
diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h
index 36580e1..f2587f4 100644
--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h
+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h
@@ -43,7 +43,7 @@
 public:
     InlineFormattingContext(const Box& formattingContextRoot, LayoutContext&);
 
-    void layout(FormattingState&) override;
+    void layout(LayoutContext&, FormattingState&) const override;
     std::unique_ptr<FormattingState> createFormattingState(Ref<FloatingState>&&) const override;
     Ref<FloatingState> createOrFindFloatingState() const override;
 };