[LFC] Align compute functions styles.
[WebKit-https.git] / Source / WebCore / layout / blockformatting / BlockFormattingContextGeometry.cpp
index 94bc292..58fa0aa 100644 (file)
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "FormattingContext.h"
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "FormattingContext.h"
+#include "Logging.h"
+#include <wtf/text/TextStream.h>
 
 namespace WebCore {
 namespace Layout {
 
 
 namespace WebCore {
 namespace Layout {
 
-static bool isStretchedToViewport(const Box& layoutBox)
+static bool isStretchedToViewport(const LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isInFlow());
     // In quirks mode, body and html stretch to the viewport.
 {
     ASSERT(layoutBox.isInFlow());
     // In quirks mode, body and html stretch to the viewport.
-    // if (!layoutBox.document().inQuirksMode())
-    //    return false;
+    if (!layoutContext.inQuirksMode())
+        return false;
 
 
-    if (!layoutBox.isDocumentBox() || !layoutBox.isBodyBox())
+    if (!layoutBox.isDocumentBox() && !layoutBox.isBodyBox())
         return false;
 
     return layoutBox.style().logicalHeight().isAuto();
         return false;
 
     return layoutBox.style().logicalHeight().isAuto();
@@ -54,12 +56,14 @@ static const Container& initialContainingBlock(const Box& layoutBox)
     return *containingBlock;
 }
 
     return *containingBlock;
 }
 
-LayoutUnit BlockFormattingContext::Geometry::inFlowNonReplacedHeight(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::HeightAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isInFlow() && !layoutBox.replaced());
 
     auto compute = [&]() -> LayoutUnit {
 {
     ASSERT(layoutBox.isInFlow() && !layoutBox.replaced());
 
     auto compute = [&]() -> LayoutUnit {
-        // https://www.w3.org/TR/CSS22/visudet.html
+        // 10.6.3 Block-level non-replaced elements in normal flow when 'overflow' computes to 'visible'
+        //
+        // If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
         // If 'height' is 'auto', the height depends on whether the element has any block-level children and whether it has padding or borders:
         // The element's height is the distance from its top content edge to the first applicable of the following:
         // 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
         // If 'height' is 'auto', the height depends on whether the element has any block-level children and whether it has padding or borders:
         // The element's height is the distance from its top content edge to the first applicable of the following:
         // 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
@@ -69,6 +73,7 @@ LayoutUnit BlockFormattingContext::Geometry::inFlowNonReplacedHeight(LayoutConte
         // 4. zero, otherwise
         // Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored,
         // and relatively positioned boxes are considered without their offset). Note that the child box may be an anonymous block box.
         // 4. zero, otherwise
         // Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored,
         // and relatively positioned boxes are considered without their offset). Note that the child box may be an anonymous block box.
+
         if (!layoutBox.style().logicalHeight().isAuto()) {
             // FIXME: Only fixed values yet.
             return layoutBox.style().logicalHeight().value();
         if (!layoutBox.style().logicalHeight().isAuto()) {
             // FIXME: Only fixed values yet.
             return layoutBox.style().logicalHeight().value();
@@ -86,7 +91,7 @@ LayoutUnit BlockFormattingContext::Geometry::inFlowNonReplacedHeight(LayoutConte
         // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin...
         auto* lastInFlowChild = downcast<Container>(layoutBox).lastInFlowChild();
         ASSERT(lastInFlowChild);
         // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin...
         auto* lastInFlowChild = downcast<Container>(layoutBox).lastInFlowChild();
         ASSERT(lastInFlowChild);
-        if (!BlockFormattingContext::MarginCollapse::isMarginBottomCollapsedWithParent(*lastInFlowChild)) {
+        if (!MarginCollapse::isMarginBottomCollapsedWithParent(*lastInFlowChild)) {
             auto* lastInFlowDisplayBox = layoutContext.displayBoxForLayoutBox(*lastInFlowChild);
             ASSERT(lastInFlowDisplayBox);
             return lastInFlowDisplayBox->bottom() + lastInFlowDisplayBox->marginBottom();
             auto* lastInFlowDisplayBox = layoutContext.displayBoxForLayoutBox(*lastInFlowChild);
             ASSERT(lastInFlowDisplayBox);
             return lastInFlowDisplayBox->bottom() + lastInFlowDisplayBox->marginBottom();
@@ -94,7 +99,7 @@ LayoutUnit BlockFormattingContext::Geometry::inFlowNonReplacedHeight(LayoutConte
 
         // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
         auto* inFlowChild = lastInFlowChild;
 
         // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
         auto* inFlowChild = lastInFlowChild;
-        while (inFlowChild && BlockFormattingContext::MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(*inFlowChild))
+        while (inFlowChild && MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(*inFlowChild))
             inFlowChild = inFlowChild->previousInFlowSibling();
         if (inFlowChild) {
             auto* inFlowDisplayBox = layoutContext.displayBoxForLayoutBox(*inFlowChild);
             inFlowChild = inFlowChild->previousInFlowSibling();
         if (inFlowChild) {
             auto* inFlowDisplayBox = layoutContext.displayBoxForLayoutBox(*inFlowChild);
@@ -106,14 +111,26 @@ LayoutUnit BlockFormattingContext::Geometry::inFlowNonReplacedHeight(LayoutConte
         return 0;
     };
 
         return 0;
     };
 
-    auto computedHeight = compute();
-    if (!isStretchedToViewport(layoutBox))
-        return computedHeight;
+    auto height = compute();
+    auto marginTop = MarginCollapse::marginTop(layoutContext, layoutBox);
+    auto marginBottom =  MarginCollapse::marginBottom(layoutContext, layoutBox);
+
+    if (!isStretchedToViewport(layoutContext, layoutBox)) {
+        LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> height(" << height << "px) margin(" << marginTop << "px, " << marginBottom << "px) -> layoutBox(" << &layoutBox << ")");
+        return { height, { marginTop, marginBottom } };
+    }
+
     auto initialContainingBlockHeight = layoutContext.displayBoxForLayoutBox(initialContainingBlock(layoutBox))->contentBox().height();
     auto initialContainingBlockHeight = layoutContext.displayBoxForLayoutBox(initialContainingBlock(layoutBox))->contentBox().height();
-    return std::max(computedHeight, initialContainingBlockHeight);
+    // Stretch but never overstretch with the margins.
+    if (height + marginTop + marginBottom < initialContainingBlockHeight)
+        height = initialContainingBlockHeight - marginTop - marginBottom;
+
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> streched to viewport -> height(" << height << "px) margin(" << marginTop << "px, " << marginBottom << "px) -> layoutBox(" << &layoutBox << ")");
+    return { height, { marginTop, marginBottom } };
 }
 
 }
 
-LayoutUnit BlockFormattingContext::Geometry::inFlowNonReplacedWidth(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::WidthAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox,
+    std::optional<LayoutUnit> precomputedWidth)
 {
     ASSERT(layoutBox.isInFlow() && !layoutBox.replaced());
 
 {
     ASSERT(layoutBox.isInFlow() && !layoutBox.replaced());
 
@@ -121,36 +138,128 @@ LayoutUnit BlockFormattingContext::Geometry::inFlowNonReplacedWidth(LayoutContex
         // 10.3.3 Block-level, non-replaced elements in normal flow
         // The following constraints must hold among the used values of the other properties:
         // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block
         // 10.3.3 Block-level, non-replaced elements in normal flow
         // The following constraints must hold among the used values of the other properties:
         // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block
+        //
+        // 1. If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' 
+        //    (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then
+        //    any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero.
+        //
+        // 2. If all of the above have a computed value other than 'auto', the values are said to be "over-constrained" and one of the used values will
+        //    have to be different from its computed value. If the 'direction' property of the containing block has the value 'ltr', the specified value
+        //    of 'margin-right' is ignored and the value is calculated so as to make the equality true. If the value of 'direction' is 'rtl',
+        //    this happens to 'margin-left' instead.
+        //
+        // 3. If there is exactly one value specified as 'auto', its used value follows from the equality.
+        //
+        // 4. If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality.
+        //
+        // 5. If both 'margin-left' and 'margin-right' are 'auto', their used values are equal. This horizontally centers the element with respect to the
+        //    edges of the containing block.
 
 
-        // If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality.
         auto& style = layoutBox.style();
         auto& style = layoutBox.style();
-        auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->width();
+        auto width = precomputedWidth ? Length { precomputedWidth.value(), Fixed } : style.logicalWidth();
+        auto* containingBlock = layoutBox.containingBlock();
+        auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*containingBlock)->width();
+        auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
 
         LayoutUnit computedWidthValue;
 
         LayoutUnit computedWidthValue;
-        auto width = style.logicalWidth();
-        if (width.isAuto()) {
-            auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
-            auto marginLeft = displayBox.marginLeft();
-            auto marginRight = displayBox.marginRight();
+        std::optional<LayoutUnit> computedMarginLeftValue;
+        std::optional<LayoutUnit> computedMarginRightValue;
+
+        auto marginLeft = style.marginLeft();
+        if (!marginLeft.isAuto())
+            computedMarginLeftValue = valueForLength(marginLeft, containingBlockWidth);
 
 
-            auto paddingLeft = displayBox.paddingLeft();
-            auto paddingRight = displayBox.paddingRight();
+        auto marginRight = style.marginRight();
+        if (!marginRight.isAuto())
+            computedMarginRightValue = valueForLength(marginRight, containingBlockWidth);
 
 
-            auto borderLeft = displayBox.borderLeft();
-            auto borderRight = displayBox.borderRight();
+        auto borderLeft = displayBox.borderLeft();
+        auto borderRight = displayBox.borderRight();
+        auto paddingLeft = displayBox.paddingLeft();
+        auto paddingRight = displayBox.paddingRight();
 
 
-            computedWidthValue = containingBlockWidth - (marginLeft + borderLeft + paddingLeft + paddingRight + borderRight + marginRight);
-        } else
+        // #1
+        if (!width.isAuto()) {
             computedWidthValue = valueForLength(width, containingBlockWidth);
             computedWidthValue = valueForLength(width, containingBlockWidth);
+            auto horizontalSpaceForMargin = containingBlockWidth - (computedMarginLeftValue.value_or(0) + borderLeft + paddingLeft 
+                + computedWidthValue + paddingRight + borderRight + computedMarginRightValue.value_or(0));
+
+            if (horizontalSpaceForMargin < 0) {
+                if (!computedMarginLeftValue)
+                    computedMarginLeftValue = LayoutUnit(0);
+                if (!computedMarginRightValue)
+                    computedMarginRightValue = LayoutUnit(0);
+            }
+        }
+
+        // #2
+        if (!width.isAuto() && !marginLeft.isAuto() && !marginRight.isAuto()) {
+            ASSERT(computedMarginLeftValue);
+            ASSERT(computedMarginRightValue);
+
+            if (containingBlock->style().isLeftToRightDirection())
+                computedMarginRightValue = containingBlockWidth - (*computedMarginLeftValue + borderLeft + paddingLeft  + computedWidthValue + paddingRight + borderRight);
+            else
+                computedMarginLeftValue = containingBlockWidth - (borderLeft + paddingLeft  + computedWidthValue + paddingRight + borderRight + *computedMarginRightValue);
+        }
+
+        // #3
+        if (!computedMarginLeftValue && !marginRight.isAuto() && !width.isAuto()) {
+            ASSERT(computedMarginRightValue);
+            computedMarginLeftValue = containingBlockWidth - (borderLeft + paddingLeft  + computedWidthValue + paddingRight + borderRight + *computedMarginRightValue);
+        } else if (!computedMarginRightValue && !marginLeft.isAuto() && !width.isAuto()) {
+            ASSERT(computedMarginLeftValue);
+            computedMarginRightValue = containingBlockWidth - (*computedMarginLeftValue + borderLeft + paddingLeft  + computedWidthValue + paddingRight + borderRight);
+        }
+
+        // #4
+        if (width.isAuto())
+            computedWidthValue = containingBlockWidth - (computedMarginLeftValue.value_or(0) + borderLeft + paddingLeft + paddingRight + borderRight + computedMarginRightValue.value_or(0));
+
+        // #5
+        if (!computedMarginLeftValue && !computedMarginRightValue) {
+            auto horizontalSpaceForMargin = containingBlockWidth - (borderLeft + paddingLeft  + computedWidthValue + paddingRight + borderRight);
+            computedMarginLeftValue = computedMarginRightValue = horizontalSpaceForMargin / 2;
+        }
+
+        ASSERT(computedMarginLeftValue);
+        ASSERT(computedMarginRightValue);
 
 
-        return computedWidthValue;
+        return FormattingContext::Geometry::WidthAndMargin { computedWidthValue, { *computedMarginLeftValue, *computedMarginRightValue } };
     };
 
     };
 
-    auto computedWidth = compute();
-    if (!isStretchedToViewport(layoutBox))
-        return computedWidth;
+    auto widthAndMargin = compute();
+    if (!isStretchedToViewport(layoutContext, layoutBox)) {
+        LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow non-replaced -> width(" << widthAndMargin.width << "px) margin(" << widthAndMargin.margin.left << "px, " << widthAndMargin.margin.right << "px) -> layoutBox(" << &layoutBox << ")");
+        return widthAndMargin;
+    }
+
     auto initialContainingBlockWidth = layoutContext.displayBoxForLayoutBox(initialContainingBlock(layoutBox))->contentBox().width();
     auto initialContainingBlockWidth = layoutContext.displayBoxForLayoutBox(initialContainingBlock(layoutBox))->contentBox().width();
-    return std::max(computedWidth, initialContainingBlockWidth);
+    auto horizontalMargins = widthAndMargin.margin.left + widthAndMargin.margin.right;
+    // Stretch but never overstretch with the margins.
+    if (widthAndMargin.width + horizontalMargins < initialContainingBlockWidth)
+        widthAndMargin.width = initialContainingBlockWidth - horizontalMargins;
+
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow non-replaced -> streched to viewport-> width(" << widthAndMargin.width << "px) margin(" << widthAndMargin.margin.left << "px, " << widthAndMargin.margin.right << "px) -> layoutBox(" << &layoutBox << ")");
+    return widthAndMargin;
+}
+
+FormattingContext::Geometry::WidthAndMargin BlockFormattingContext::Geometry::inFlowReplacedWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
+{
+    ASSERT(layoutBox.isInFlow() && layoutBox.replaced());
+
+    // 10.3.4 Block-level, replaced elements in normal flow
+    //
+    // 1. The used value of 'width' is determined as for inline replaced elements.
+    // 2. Then the rules for non-replaced block-level elements are applied to determine the margins.
+
+    // #1
+    auto width = FormattingContext::Geometry::inlineReplacedWidthAndMargin(layoutContext, layoutBox).width;
+    // #2
+    auto margin = inFlowNonReplacedWidthAndMargin(layoutContext, layoutBox, width).margin;
+
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow replaced -> width(" << width << "px) margin(" << margin.left << "px, " << margin.left << "px) -> layoutBox(" << &layoutBox << ")");
+    return { width, margin };
 }
 
 LayoutPoint BlockFormattingContext::Geometry::staticPosition(LayoutContext& layoutContext, const Box& layoutBox)
 }
 
 LayoutPoint BlockFormattingContext::Geometry::staticPosition(LayoutContext& layoutContext, const Box& layoutBox)
@@ -160,6 +269,7 @@ LayoutPoint BlockFormattingContext::Geometry::staticPosition(LayoutContext& layo
     // 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.
     // In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch).
     // 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.
     // In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch).
+
     auto containingBlockContentBox = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->contentBox();
     // Start from the top of the container's content box.
     auto top = containingBlockContentBox.top();
     auto containingBlockContentBox = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->contentBox();
     // Start from the top of the container's content box.
     auto top = containingBlockContentBox.top();
@@ -168,15 +278,14 @@ LayoutPoint BlockFormattingContext::Geometry::staticPosition(LayoutContext& layo
         auto& previousInFlowDisplayBox = *layoutContext.displayBoxForLayoutBox(*previousInFlowSibling);
         top = previousInFlowDisplayBox.bottom() + previousInFlowDisplayBox.marginBottom();
     }
         auto& previousInFlowDisplayBox = *layoutContext.displayBoxForLayoutBox(*previousInFlowSibling);
         top = previousInFlowDisplayBox.bottom() + previousInFlowDisplayBox.marginBottom();
     }
-    auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
-    LayoutPoint topLeft = { top, left };
-    topLeft.moveBy({ displayBox.marginLeft(), displayBox.marginTop() });
-    return topLeft;
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position] -> static -> top(" << top << "px) left(" << left << "px) layoutBox(" << &layoutBox << ")");
+    return { top, left };
 }
 
 LayoutPoint BlockFormattingContext::Geometry::inFlowPositionedPosition(LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isInFlowPositioned());
 }
 
 LayoutPoint BlockFormattingContext::Geometry::inFlowPositionedPosition(LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isInFlowPositioned());
+
     // 9.4.3 Relative positioning
     //
     // The 'top' and 'bottom' properties move relatively positioned element(s) up or down without changing their size.
     // 9.4.3 Relative positioning
     //
     // The 'top' and 'bottom' properties move relatively positioned element(s) up or down without changing their size.
@@ -185,22 +294,28 @@ LayoutPoint BlockFormattingContext::Geometry::inFlowPositionedPosition(LayoutCon
     // 1. If both are 'auto', their used values are both '0'.
     // 2. If one of them is 'auto', it becomes the negative of the other.
     // 3. If neither is 'auto', 'bottom' is ignored (i.e., the used value of 'bottom' will be minus the value of 'top').
     // 1. If both are 'auto', their used values are both '0'.
     // 2. If one of them is 'auto', it becomes the negative of the other.
     // 3. If neither is 'auto', 'bottom' is ignored (i.e., the used value of 'bottom' will be minus the value of 'top').
-    auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
+
     auto& style = layoutBox.style();
     auto& style = layoutBox.style();
+    auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
+    auto& containingBlock = *layoutBox.containingBlock();
+    auto& containingBlockDisplayBox = *layoutContext.displayBoxForLayoutBox(containingBlock);
+    auto containingBlockWidth = containingBlockDisplayBox.width();
 
 
-    auto top = style.logicalTop();
-    auto bottom = style.logicalBottom();
-    LayoutUnit topDelta;
+    auto top = FormattingContext::Geometry::computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
+    auto bottom = FormattingContext::Geometry::computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
 
 
-    if (top.isAuto() && bottom.isAuto()) {
+    if (!top && !bottom) {
         // #1
         // #1
-        topDelta = 0;
-    } else if (top.isAuto()) {
+        top = bottom = { 0 };
+    } else if (!top) {
         // #2
         // #2
-        topDelta = -bottom.value();
-    } else if (bottom.isAuto()) {
-        // #3 #4
-        topDelta = top.value();
+        top = -*bottom;
+    } else if (!bottom) {
+        // #3
+        bottom = -*top;
+    } else {
+        // #4
+        bottom = std::nullopt;
     }
 
     // For relatively positioned elements, 'left' and 'right' move the box(es) horizontally, without changing their size.
     }
 
     // For relatively positioned elements, 'left' and 'right' move the box(es) horizontally, without changing their size.
@@ -213,40 +328,56 @@ LayoutPoint BlockFormattingContext::Geometry::inFlowPositionedPosition(LayoutCon
     // 4. If neither 'left' nor 'right' is 'auto', the position is over-constrained, and one of them has to be ignored.
     //    If the 'direction' property of the containing block is 'ltr', the value of 'left' wins and 'right' becomes -'left'.
     //    If 'direction' of the containing block is 'rtl', 'right' wins and 'left' is ignored.
     // 4. If neither 'left' nor 'right' is 'auto', the position is over-constrained, and one of them has to be ignored.
     //    If the 'direction' property of the containing block is 'ltr', the value of 'left' wins and 'right' becomes -'left'.
     //    If 'direction' of the containing block is 'rtl', 'right' wins and 'left' is ignored.
-    //
-    auto left = style.logicalLeft();
-    auto right = style.logicalRight();
-    LayoutUnit leftDelta;
 
 
-    if (left.isAuto() && right.isAuto()) {
+    auto left = FormattingContext::Geometry::computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
+    auto right = FormattingContext::Geometry::computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
+
+    if (!left && !right) {
         // #1
         // #1
-        leftDelta = 0;
-    } else if (left.isAuto()) {
+        left = right = { 0 };
+    } else if (!left) {
         // #2
         // #2
-        leftDelta = -right.value();
-    } else if (right.isAuto()) {
+        left = -*right;
+    } else if (!right) {
         // #3
         // #3
-        leftDelta = left.value();
+        right = -*left;
     } else {
         // #4
     } else {
         // #4
-        // FIXME: take direction into account
-        leftDelta = left.value();
+        auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
+        if (isLeftToRightDirection)
+            right = -*left;
+        else
+            left = std::nullopt;
     }
 
     }
 
-    return { displayBox.left() + leftDelta, displayBox.top() + topDelta };
+    ASSERT(!bottom || *top == -*bottom);
+    ASSERT(!left || *left == -*right);
+
+    auto newTopPosition = displayBox.top() + *top;
+    auto newLeftPosition = displayBox.left() + left.value_or(-*right);
+
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position] -> positioned inflow -> top(" << newTopPosition << "px) left(" << newLeftPosition << "px) layoutBox(" << &layoutBox << ")");
+    return { newTopPosition, newLeftPosition };
 }
 
 }
 
-Display::Box::Edges BlockFormattingContext::Geometry::computedMargin(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::HeightAndMargin BlockFormattingContext::Geometry::inFlowHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
 {
 {
-    auto& style = layoutBox.style();
-    auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->width();
-
-    return Display::Box::Edges(
-        BlockFormattingContext::MarginCollapse::marginTop(layoutBox),
-        valueForLength(style.marginLeft(), containingBlockWidth),
-        BlockFormattingContext::MarginCollapse::marginBottom(layoutBox),
-        valueForLength(style.marginRight(), containingBlockWidth)
-    );
+    ASSERT(layoutBox.isInFlow());
+
+    if (!layoutBox.replaced())
+        return inFlowNonReplacedHeightAndMargin(layoutContext, layoutBox);
+    // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block'
+    // replaced elements in normal flow and floating replaced elements
+    return FormattingContext::Geometry::inlineReplacedHeightAndMargin(layoutContext, layoutBox);
+}
+
+FormattingContext::Geometry::WidthAndMargin BlockFormattingContext::Geometry::inFlowWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
+{
+    ASSERT(layoutBox.isInFlow());
+
+    if (!layoutBox.replaced())
+        return inFlowNonReplacedWidthAndMargin(layoutContext, layoutBox);
+    return inFlowReplacedWidthAndMargin(layoutContext, layoutBox);
 }
 
 }
 }
 
 }