[LFC] Merge left, right, width and horizontal margin computation for out-of-flow...
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Jun 2018 14:37:08 +0000 (14:37 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Jun 2018 14:37:08 +0000 (14:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186474

Reviewed by Antti Koivisto.

Implement https://www.w3.org/TR/CSS22/visudet.html#abs-non-replaced-width
(10.3.7 Absolutely positioned, non-replaced elements)

* layout/FormattingContext.cpp:
(WebCore::Layout::FormattingContext::computeOutOfFlowHorizontalGeometry const):
(WebCore::Layout::FormattingContext::layoutOutOfFlowDescendants const):
(WebCore::Layout::FormattingContext::computeOutOfFlowWidthAndMargin const): Deleted.
* layout/FormattingContext.h:
* layout/FormattingContextGeometry.cpp:
(WebCore::Layout::FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry):
(WebCore::Layout::FormattingContext::Geometry::outOfFlowReplacedHorizontalGeometry):
(WebCore::Layout::FormattingContext::Geometry::outOfFlowHorizontalGeometry):
(WebCore::Layout::FormattingContext::Geometry::outOfFlowNonReplacedWidthAndMargin): Deleted.
(WebCore::Layout::FormattingContext::Geometry::outOfFlowReplacedWidthAndMargin): Deleted.
(WebCore::Layout::FormattingContext::Geometry::outOfFlowWidthAndMargin): Deleted.

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

Source/WebCore/ChangeLog
Source/WebCore/layout/FormattingContext.cpp
Source/WebCore/layout/FormattingContext.h
Source/WebCore/layout/FormattingContextGeometry.cpp

index 2b81791..9519e64 100644 (file)
@@ -1,3 +1,26 @@
+2018-06-11  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC] Merge left, right, width and horizontal margin computation for out-of-flow non-replaced elements
+        https://bugs.webkit.org/show_bug.cgi?id=186474
+
+        Reviewed by Antti Koivisto.
+
+        Implement https://www.w3.org/TR/CSS22/visudet.html#abs-non-replaced-width 
+        (10.3.7 Absolutely positioned, non-replaced elements)
+
+        * layout/FormattingContext.cpp:
+        (WebCore::Layout::FormattingContext::computeOutOfFlowHorizontalGeometry const):
+        (WebCore::Layout::FormattingContext::layoutOutOfFlowDescendants const):
+        (WebCore::Layout::FormattingContext::computeOutOfFlowWidthAndMargin const): Deleted.
+        * layout/FormattingContext.h:
+        * layout/FormattingContextGeometry.cpp:
+        (WebCore::Layout::FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry):
+        (WebCore::Layout::FormattingContext::Geometry::outOfFlowReplacedHorizontalGeometry):
+        (WebCore::Layout::FormattingContext::Geometry::outOfFlowHorizontalGeometry):
+        (WebCore::Layout::FormattingContext::Geometry::outOfFlowNonReplacedWidthAndMargin): Deleted.
+        (WebCore::Layout::FormattingContext::Geometry::outOfFlowReplacedWidthAndMargin): Deleted.
+        (WebCore::Layout::FormattingContext::Geometry::outOfFlowWidthAndMargin): Deleted.
+
 2018-06-10  Zalan Bujtas  <zalan@apple.com>
 
         [LFC] Merge top, bottom, height and vertical margin computation for out-of-flow non-replaced elements
index 92c39d8..6aeb4c2 100644 (file)
@@ -68,11 +68,13 @@ void FormattingContext::computeFloatingWidthAndMargin(LayoutContext& layoutConte
     displayBox.setHorizontalMargin(widthAndMargin.margin);
 }
 
-void FormattingContext::computeOutOfFlowWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
+void FormattingContext::computeOutOfFlowHorizontalGeometry(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
 {
-    auto widthAndMargin = Geometry::outOfFlowWidthAndMargin(layoutContext, layoutBox);
-    displayBox.setWidth(widthAndMargin.width);
-    displayBox.setHorizontalMargin(widthAndMargin.margin);
+    auto horizontalGeometry = Geometry::outOfFlowHorizontalGeometry(layoutContext, layoutBox);
+    displayBox.setLeft(horizontalGeometry.left);
+    displayBox.setWidth(horizontalGeometry.width);
+    ASSERT(horizontalGeometry.left + horizontalGeometry.width == horizontalGeometry.right);
+    displayBox.setHorizontalMargin(horizontalGeometry.margin);
 }
 
 void FormattingContext::computeOutOfFlowVerticalGeometry(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
@@ -117,7 +119,7 @@ void FormattingContext::layoutOutOfFlowDescendants(LayoutContext& layoutContext)
         // of a hypothetical box that would have been the first box of the element if its specified 'position' value had been 'static' and
         // its specified 'float' had been 'none' and its specified 'clear' had been 'none'.
         computeStaticPosition(layoutContext, layoutBox, displayBox);
-        computeOutOfFlowWidthAndMargin(layoutContext, layoutBox, displayBox);
+        computeOutOfFlowHorizontalGeometry(layoutContext, layoutBox, displayBox);
 
         ASSERT(layoutBox.establishesFormattingContext());
         auto formattingContext = layoutContext.formattingContext(layoutBox);
index 3c6acf4..2f9afa6 100644 (file)
@@ -91,6 +91,13 @@ protected:
             Display::Box::VerticalEdges margin;
         };
 
+         struct HorizontalGeometry {
+             LayoutUnit left;
+             LayoutUnit right;
+             LayoutUnit width;
+             Display::Box::HorizontalEdges margin;
+         };
+
         struct VerticalGeometry {
             LayoutUnit top;
             LayoutUnit bottom;
@@ -99,7 +106,7 @@ protected:
         };
 
         static VerticalGeometry outOfFlowVerticalGeometry(LayoutContext&, const Box&);
-        static WidthAndMargin outOfFlowWidthAndMargin(LayoutContext&, const Box&);
+        static HorizontalGeometry outOfFlowHorizontalGeometry(LayoutContext&, const Box&);
 
         static HeightAndMargin floatingHeightAndMargin(LayoutContext&, const Box&);
         static WidthAndMargin floatingWidthAndMargin(LayoutContext&, const Box&);
@@ -118,10 +125,10 @@ protected:
 
     private:
         static VerticalGeometry outOfFlowReplacedVerticalGeometry(LayoutContext&, const Box&);
-        static WidthAndMargin outOfFlowReplacedWidthAndMargin(LayoutContext&, const Box&);
+        static HorizontalGeometry outOfFlowReplacedHorizontalGeometry(LayoutContext&, const Box&);
 
         static VerticalGeometry outOfFlowNonReplacedVerticalGeometry(LayoutContext&, const Box&);
-        static WidthAndMargin outOfFlowNonReplacedWidthAndMargin(LayoutContext&, const Box&);
+        static HorizontalGeometry outOfFlowNonReplacedHorizontalGeometry(LayoutContext&, const Box&);
 
         static HeightAndMargin floatingReplacedHeightAndMargin(LayoutContext&, const Box&);
         static WidthAndMargin floatingReplacedWidthAndMargin(LayoutContext&, const Box&);
@@ -133,7 +140,7 @@ protected:
 private:
     void computeOutOfFlowPosition(LayoutContext&, const Box&, Display::Box&) const;
     void computeOutOfFlowVerticalGeometry(LayoutContext&, const Box&, Display::Box&) const;
-    void computeOutOfFlowWidthAndMargin(LayoutContext&, const Box&, Display::Box&) const;
+    void computeOutOfFlowHorizontalGeometry(LayoutContext&, const Box&, Display::Box&) const;
 
     WeakPtr<Box> m_root;
 };
index 0b66168..a69c016 100644 (file)
@@ -186,7 +186,7 @@ FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOf
     return { *top, *bottom, *height, { *marginTop, *marginBottom} };
 }
 
-FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::outOfFlowNonReplacedWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
     
@@ -198,7 +198,15 @@ FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::outOfFl
     // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
     // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
     // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
-
+    //
+    // If none of the three is 'auto': If both 'margin-left' and 'margin-right' are 'auto', solve the equation under the extra constraint that the two margins get equal values,
+    // unless this would make them negative, in which case when direction of the containing block is 'ltr' ('rtl'), set 'margin-left' ('margin-right') to zero and
+    // solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value.
+    // If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right'
+    // (in case 'direction' is 'ltr') and solve for that value.
+    //
+    // Otherwise, set 'auto' values for 'margin-left' and 'margin-right' to 0, and pick the one of the following six rules that applies.
+    //
     // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'
     // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing the static-position 
     //    containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position.
@@ -207,41 +215,116 @@ FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::outOfFl
     // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
     // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
     // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
-    auto& style = layoutBox.style();
-    auto left = style.logicalLeft();
-    auto right = style.logicalRight();
-    auto width = style.logicalWidth();
 
-    auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->width();
-    LayoutUnit computedWidthValue;
-
-    if (!width.isAuto())
-        computedWidthValue = valueForLength(width, containingBlockWidth);
-    else if ((left.isAuto() && right.isAuto())
-        || (left.isAuto() && !right.isAuto())
-        || (!left.isAuto() && right.isAuto())) {
-        // All auto (#1), #1 and #3
-        computedWidthValue = shrinkToFitWidth(layoutContext, layoutBox);
-    } else if (!left.isAuto() && !right.isAuto()) {
-        // #5
-        auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
-
-        auto marginLeft = displayBox.marginLeft();
-        auto marginRight = displayBox.marginRight();
+    auto& style = layoutBox.style();
+    auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
+    auto& containingBlock = *layoutBox.containingBlock();
+    auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(containingBlock)->width();
+    auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
     
-        auto paddingLeft = displayBox.paddingLeft();
-        auto paddingRight = displayBox.paddingRight();
+    auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
+    auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
+    auto width = computedValueIfNotAuto(style.logicalWidth(), containingBlockWidth);
+    auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth);
+    auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth);
+    auto paddingLeft = displayBox.paddingLeft();
+    auto paddingRight = displayBox.paddingRight();
+    auto borderLeft = displayBox.borderLeft();
+    auto borderRight = displayBox.borderRight();
+
+    if (!left && !width && !right) {
+        // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
+        // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
+        // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
+        marginLeft = marginLeft.value_or(0);
+        marginRight = marginRight.value_or(0);
+
+        if (isLeftToRightDirection)
+            left = displayBox.left();
+        else
+            right = displayBox.right();
+    } else if (left && width && right) {
+        // If none of the three is 'auto': If both 'margin-left' and 'margin-right' are 'auto', solve the equation under the extra constraint that the two margins get equal values,
+        // unless this would make them negative, in which case when direction of the containing block is 'ltr' ('rtl'), set 'margin-left' ('margin-right') to zero and
+        // solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value.
+        // If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right'
+        // (in case 'direction' is 'ltr') and solve for that value.
+        if (!marginLeft && !marginRight) {
+            auto marginLeftAndRight = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
+            if (marginLeftAndRight >= 0)
+                marginLeft = marginRight = marginLeftAndRight / 2;  
+            else {
+                if (isLeftToRightDirection) {
+                    marginLeft = LayoutUnit { 0 };
+                    marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
+                } else {
+                    marginRight = LayoutUnit { 0 };
+                    marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
+                }
+            }
+        } else if (!marginLeft) {
+            marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
+            // Overconstrained? Ignore right (left).
+            if (*marginLeft < 0) {
+                if (isLeftToRightDirection)
+                    marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
+                else
+                    marginLeft = containingBlockWidth - (borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
+            }
+        } else if (!marginRight) {
+            marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
+            // Overconstrained? Ignore right (left).
+            if (*marginRight < 0) {
+                if (isLeftToRightDirection)
+                    marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight);
+                else
+                    marginRight = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
+            }
+        }
+    } else {
+        // Otherwise, set 'auto' values for 'margin-left' and 'margin-right' to 0, and pick the one of the following six rules that applies.
+        marginLeft = marginLeft.value_or(0);
+        marginRight = marginRight.value_or(0);
+    }
 
-        auto borderLeft = displayBox.borderLeft();
-        auto borderRight = displayBox.borderRight();
+    ASSERT(marginLeft);
+    ASSERT(marginRight);
 
-        computedWidthValue = containingBlockWidth - (left.value() + marginLeft + borderLeft + paddingLeft + paddingRight + borderRight + marginRight + right.value());
-    } else {
-        // #2 #4 #6 have width != auto
-        ASSERT_NOT_REACHED();
+    if (!left && !width && right) {
+        // #1
+        width = shrinkToFitWidth(layoutContext, layoutBox);
+        left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight  + borderRight + *marginRight + *right);
+    } else if (!left && !right && width) {
+        // #2
+        if (isLeftToRightDirection) {
+            left = displayBox.left();
+            right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
+        } else {
+            right = displayBox.right();
+            left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
+        }
+    } else if (!width && !right && left) {
+        // #3
+        width = shrinkToFitWidth(layoutContext, layoutBox);
+        right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
+    } else if (!left && width && right) {
+        // #4
+        left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
+    } else if (!width && left && right) {
+        // #5
+        width = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + paddingRight  + borderRight + *marginRight + *right);
+    } else if (!right && left && width) {
+        // #6
+        right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
     }
 
-    return WidthAndMargin { computedWidthValue, { } };
+    ASSERT(left);
+    ASSERT(right);
+    ASSERT(width);
+    ASSERT(marginLeft);
+    ASSERT(marginRight);
+
+    return HorizontalGeometry { *left, *right, *width, { *marginLeft, *marginRight } };
 }
 
 FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOfFlowReplacedVerticalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
@@ -254,13 +337,14 @@ FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOf
     return { { }, { }, heightAndMargin.height, heightAndMargin.margin };
 }
 
-FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::outOfFlowReplacedWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowReplacedHorizontalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isOutOfFlowPositioned() && layoutBox.replaced());
     // 10.3.8 Absolutely positioned, replaced elements
     //
     // The used value of 'width' is determined as for inline replaced elements.
-    return inlineReplacedWidthAndMargin(layoutContext, layoutBox);
+    auto widthAndMargin = inlineReplacedWidthAndMargin(layoutContext, layoutBox);
+    return { { }, { }, widthAndMargin.width, widthAndMargin.margin };
 }
 
 FormattingContext::Geometry::HeightAndMargin FormattingContext::Geometry::floatingNonReplacedHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
@@ -508,13 +592,13 @@ FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOf
     return outOfFlowReplacedVerticalGeometry(layoutContext, layoutBox);
 }
 
-FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::outOfFlowWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowHorizontalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isOutOfFlowPositioned());
 
     if (!layoutBox.replaced())
-        return outOfFlowNonReplacedWidthAndMargin(layoutContext, layoutBox);
-    return outOfFlowReplacedWidthAndMargin(layoutContext, layoutBox);
+        return outOfFlowNonReplacedHorizontalGeometry(layoutContext, layoutBox);
+    return outOfFlowReplacedHorizontalGeometry(layoutContext, layoutBox);
 }
 
 FormattingContext::Geometry::HeightAndMargin FormattingContext::Geometry::floatingHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox)