[LFC] Align compute functions styles.
[WebKit-https.git] / Source / WebCore / layout / blockformatting / BlockFormattingContextGeometry.cpp
index 552829b..58fa0aa 100644 (file)
@@ -29,6 +29,8 @@
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "FormattingContext.h"
+#include "Logging.h"
+#include <wtf/text/TextStream.h>
 
 namespace WebCore {
 namespace Layout {
@@ -40,7 +42,7 @@ static bool isStretchedToViewport(const LayoutContext& layoutContext, const Box&
     if (!layoutContext.inQuirksMode())
         return false;
 
-    if (!layoutBox.isDocumentBox() || !layoutBox.isBodyBox())
+    if (!layoutBox.isDocumentBox() && !layoutBox.isBodyBox())
         return false;
 
     return layoutBox.style().logicalHeight().isAuto();
@@ -54,12 +56,14 @@ static const Container& initialContainingBlock(const Box& layoutBox)
     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 {
-        // 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
@@ -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.
+
         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);
-        if (!BlockFormattingContext::MarginCollapse::isMarginBottomCollapsedWithParent(*lastInFlowChild)) {
+        if (!MarginCollapse::isMarginBottomCollapsedWithParent(*lastInFlowChild)) {
             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;
-        while (inFlowChild && BlockFormattingContext::MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(*inFlowChild))
+        while (inFlowChild && MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(*inFlowChild))
             inFlowChild = inFlowChild->previousInFlowSibling();
         if (inFlowChild) {
             auto* inFlowDisplayBox = layoutContext.displayBoxForLayoutBox(*inFlowChild);
@@ -106,11 +111,22 @@ LayoutUnit BlockFormattingContext::Geometry::inFlowNonReplacedHeight(LayoutConte
         return 0;
     };
 
-    auto computedHeight = compute();
-    if (!isStretchedToViewport(layoutContext, 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();
-    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 } };
 }
 
 FormattingContext::Geometry::WidthAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox,
@@ -212,11 +228,38 @@ FormattingContext::Geometry::WidthAndMargin BlockFormattingContext::Geometry::in
         return FormattingContext::Geometry::WidthAndMargin { computedWidthValue, { *computedMarginLeftValue, *computedMarginRightValue } };
     };
 
-    auto computedWidthAndMarginValue = compute();
-    if (!isStretchedToViewport(layoutContext, layoutBox))
-        return computedWidthAndMarginValue;
+    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();
-    return FormattingContext::Geometry::WidthAndMargin { std::max(computedWidthAndMarginValue.width, 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)
@@ -226,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).
+
     auto containingBlockContentBox = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->contentBox();
     // Start from the top of the container's content box.
     auto top = containingBlockContentBox.top();
@@ -234,15 +278,14 @@ LayoutPoint BlockFormattingContext::Geometry::staticPosition(LayoutContext& layo
         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());
+
     // 9.4.3 Relative positioning
     //
     // The 'top' and 'bottom' properties move relatively positioned element(s) up or down without changing their size.
@@ -251,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').
-    auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
+
     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
-        topDelta = 0;
-    } else if (top.isAuto()) {
+        top = bottom = { 0 };
+    } else if (!top) {
         // #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.
@@ -279,38 +328,47 @@ 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.
-    //
-    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
-        leftDelta = 0;
-    } else if (left.isAuto()) {
+        left = right = { 0 };
+    } else if (!left) {
         // #2
-        leftDelta = -right.value();
-    } else if (right.isAuto()) {
+        left = -*right;
+    } else if (!right) {
         // #3
-        leftDelta = left.value();
+        right = -*left;
     } 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 };
 }
 
-LayoutUnit BlockFormattingContext::Geometry::inFlowHeight(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::HeightAndMargin BlockFormattingContext::Geometry::inFlowHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isInFlow());
 
     if (!layoutBox.replaced())
-        return inFlowNonReplacedHeight(layoutContext, layoutBox);
+        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::inlineReplacedHeight(layoutContext, layoutBox);
+    return FormattingContext::Geometry::inlineReplacedHeightAndMargin(layoutContext, layoutBox);
 }
 
 FormattingContext::Geometry::WidthAndMargin BlockFormattingContext::Geometry::inFlowWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
@@ -319,17 +377,7 @@ FormattingContext::Geometry::WidthAndMargin BlockFormattingContext::Geometry::in
 
     if (!layoutBox.replaced())
         return inFlowNonReplacedWidthAndMargin(layoutContext, layoutBox);
-    // 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 inlineReplacedWidthAndMargin = FormattingContext::Geometry::inlineReplacedWidthAndMargin(layoutContext, layoutBox);
-    // #2
-    auto inlineReplacedWidthAndBlockNonReplacedMargin = Geometry::inFlowNonReplacedWidthAndMargin(layoutContext, layoutBox, inlineReplacedWidthAndMargin.width);
-    ASSERT(inlineReplacedWidthAndMargin.width == inlineReplacedWidthAndBlockNonReplacedMargin.width);
-    return inlineReplacedWidthAndBlockNonReplacedMargin;
+    return inFlowReplacedWidthAndMargin(layoutContext, layoutBox);
 }
 
 }