[LFC][BFC][MarginCollapsing] Add support for peculiar cases.
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Jan 2019 16:08:05 +0000 (16:08 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Jan 2019 16:08:05 +0000 (16:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=192625

Reviewed by Antti Koivisto.

Source/WebCore:

Implement some of the more peculiar cases like margin collpasing through multiple boxes etc.
Add ~100 new passing cases.

* layout/FormattingContextGeometry.cpp:
(WebCore::Layout::FormattingContext::Geometry::inlineReplacedHeightAndMargin):
* layout/LayoutState.h:
(WebCore::Layout::LayoutState::hasFormattingState const):
* layout/MarginTypes.h:
* layout/blockformatting/BlockFormattingContext.cpp:
(WebCore::Layout::BlockFormattingContext::computeEstimatedMarginBefore const):
(WebCore::Layout::BlockFormattingContext::computeEstimatedMarginBeforeForAncestors const):
(WebCore::Layout::hasPrecomputedMarginBefore):
(WebCore::Layout::BlockFormattingContext::computeFloatingPosition const):
(WebCore::Layout::BlockFormattingContext::computePositionToAvoidFloats const):
(WebCore::Layout::BlockFormattingContext::computeVerticalPositionForFloatClear const):
(WebCore::Layout::BlockFormattingContext::computeHeightAndMargin const):
(WebCore::Layout::BlockFormattingContext::adjustedVerticalPositionAfterMarginCollapsing const):
* layout/blockformatting/BlockFormattingContext.h:
(WebCore::Layout::BlockFormattingContext::blockFormattingState const):
* layout/blockformatting/BlockFormattingContextGeometry.cpp:
(WebCore::Layout::BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMargin):
(WebCore::Layout::BlockFormattingContext::Geometry::inFlowHeightAndMargin):
(WebCore::Layout::BlockFormattingContext::Geometry::estimatedMarginBefore): Deleted.
(WebCore::Layout::BlockFormattingContext::Geometry::estimatedMarginAfter): Deleted.
* layout/blockformatting/BlockFormattingContextQuirks.cpp:
(WebCore::Layout::BlockFormattingContext::Quirks::stretchedInFlowHeight):
(WebCore::Layout::BlockFormattingContext::Quirks::shouldIgnoreMarginAfter):
(WebCore::Layout::BlockFormattingContext::Quirks::stretchedHeight): Deleted.
* layout/blockformatting/BlockFormattingState.h:
(WebCore::Layout::BlockFormattingState::setPositiveAndNegativeVerticalMargin):
(WebCore::Layout::BlockFormattingState::hasPositiveAndNegativeVerticalMargin const):
(WebCore::Layout::BlockFormattingState::positiveAndNegativeVerticalMargin const):
(WebCore::Layout::BlockFormattingState::setHasEstimatedMarginBefore):
(WebCore::Layout::BlockFormattingState::clearHasEstimatedMarginBefore):
(WebCore::Layout::BlockFormattingState::hasEstimatedMarginBefore const):
* layout/blockformatting/BlockMarginCollapse.cpp:
(WebCore::Layout::hasClearance):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarginAfter):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarginBefore):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithPreviousSiblingMarginAfter):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithFirstInFlowChildMarginBefore):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithSiblingMarginBeforeWithClearance):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithParentMarginBefore):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithLastInFlowChildMarginAfter):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithNextSiblingMarginBefore):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginsCollapseThrough):
(WebCore::Layout::computedPositiveAndNegativeMargin):
(WebCore::Layout::marginValue):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::updateCollapsedMarginAfter):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::positiveNegativeValues):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::positiveNegativeMarginBefore):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::positiveNegativeMarginAfter):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::estimatedMarginBefore):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::collapsedVerticalValues):
(WebCore::Layout::BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginBefore): Deleted.
(WebCore::Layout::BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginAfter): Deleted.
(WebCore::Layout::BlockFormattingContext::MarginCollapse::nonCollapsedMarginBefore): Deleted.
(WebCore::Layout::BlockFormattingContext::MarginCollapse::nonCollapsedMarginAfter): Deleted.
(WebCore::Layout::BlockFormattingContext::MarginCollapse::collapsedMarginBeforeFromFirstChild): Deleted.
(WebCore::Layout::BlockFormattingContext::MarginCollapse::collapsedMarginAfterFromLastChild): Deleted.
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithPreviousSibling): Deleted.
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithNextSibling): Deleted.
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBefore): Deleted.
(WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfter): Deleted.
* layout/displaytree/DisplayBox.cpp:
(WebCore::Display::Box::Box):
* layout/displaytree/DisplayBox.h:
(WebCore::Display::Box::hasClearance const):
(WebCore::Display::Box::setEstimatedMarginBefore):
(WebCore::Display::Box::estimatedMarginBefore const):
(WebCore::Display::Box::setHasClearance):
(WebCore::Display::Box::invalidateEstimatedMarginBefore):
(WebCore::Display::Box::setVerticalMargin):
(WebCore::Display::Box::rectWithMargin const):
* layout/floats/FloatingContext.cpp:
(WebCore::Layout::FloatingContext::verticalPositionWithClearance const):
* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::collectInlineContentForSubtree const):

Tools:

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

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

15 files changed:
Source/WebCore/ChangeLog
Source/WebCore/layout/LayoutState.h
Source/WebCore/layout/MarginTypes.h
Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp
Source/WebCore/layout/blockformatting/BlockFormattingContext.h
Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp
Source/WebCore/layout/blockformatting/BlockFormattingContextQuirks.cpp
Source/WebCore/layout/blockformatting/BlockFormattingState.h
Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp
Source/WebCore/layout/displaytree/DisplayBox.cpp
Source/WebCore/layout/displaytree/DisplayBox.h
Source/WebCore/layout/floats/FloatingContext.cpp
Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
Tools/ChangeLog
Tools/LayoutReloaded/misc/LFC-passing-tests.txt

index d1d06c5..63394d7 100644 (file)
@@ -1,3 +1,89 @@
+2019-01-09  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][BFC][MarginCollapsing] Add support for peculiar cases.
+        https://bugs.webkit.org/show_bug.cgi?id=192625
+
+        Reviewed by Antti Koivisto.
+
+        Implement some of the more peculiar cases like margin collpasing through multiple boxes etc.
+        Add ~100 new passing cases.
+
+        * layout/FormattingContextGeometry.cpp:
+        (WebCore::Layout::FormattingContext::Geometry::inlineReplacedHeightAndMargin):
+        * layout/LayoutState.h:
+        (WebCore::Layout::LayoutState::hasFormattingState const):
+        * layout/MarginTypes.h:
+        * layout/blockformatting/BlockFormattingContext.cpp:
+        (WebCore::Layout::BlockFormattingContext::computeEstimatedMarginBefore const):
+        (WebCore::Layout::BlockFormattingContext::computeEstimatedMarginBeforeForAncestors const):
+        (WebCore::Layout::hasPrecomputedMarginBefore):
+        (WebCore::Layout::BlockFormattingContext::computeFloatingPosition const):
+        (WebCore::Layout::BlockFormattingContext::computePositionToAvoidFloats const):
+        (WebCore::Layout::BlockFormattingContext::computeVerticalPositionForFloatClear const):
+        (WebCore::Layout::BlockFormattingContext::computeHeightAndMargin const):
+        (WebCore::Layout::BlockFormattingContext::adjustedVerticalPositionAfterMarginCollapsing const):
+        * layout/blockformatting/BlockFormattingContext.h:
+        (WebCore::Layout::BlockFormattingContext::blockFormattingState const):
+        * layout/blockformatting/BlockFormattingContextGeometry.cpp:
+        (WebCore::Layout::BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMargin):
+        (WebCore::Layout::BlockFormattingContext::Geometry::inFlowHeightAndMargin):
+        (WebCore::Layout::BlockFormattingContext::Geometry::estimatedMarginBefore): Deleted.
+        (WebCore::Layout::BlockFormattingContext::Geometry::estimatedMarginAfter): Deleted.
+        * layout/blockformatting/BlockFormattingContextQuirks.cpp:
+        (WebCore::Layout::BlockFormattingContext::Quirks::stretchedInFlowHeight):
+        (WebCore::Layout::BlockFormattingContext::Quirks::shouldIgnoreMarginAfter):
+        (WebCore::Layout::BlockFormattingContext::Quirks::stretchedHeight): Deleted.
+        * layout/blockformatting/BlockFormattingState.h:
+        (WebCore::Layout::BlockFormattingState::setPositiveAndNegativeVerticalMargin):
+        (WebCore::Layout::BlockFormattingState::hasPositiveAndNegativeVerticalMargin const):
+        (WebCore::Layout::BlockFormattingState::positiveAndNegativeVerticalMargin const):
+        (WebCore::Layout::BlockFormattingState::setHasEstimatedMarginBefore):
+        (WebCore::Layout::BlockFormattingState::clearHasEstimatedMarginBefore):
+        (WebCore::Layout::BlockFormattingState::hasEstimatedMarginBefore const):
+        * layout/blockformatting/BlockMarginCollapse.cpp:
+        (WebCore::Layout::hasClearance):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarginAfter):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarginBefore):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithPreviousSiblingMarginAfter):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithFirstInFlowChildMarginBefore):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithSiblingMarginBeforeWithClearance):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithParentMarginBefore):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithLastInFlowChildMarginAfter):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithNextSiblingMarginBefore):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginsCollapseThrough):
+        (WebCore::Layout::computedPositiveAndNegativeMargin):
+        (WebCore::Layout::marginValue):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::updateCollapsedMarginAfter):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::positiveNegativeValues):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::positiveNegativeMarginBefore):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::positiveNegativeMarginAfter):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::estimatedMarginBefore):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::collapsedVerticalValues):
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginBefore): Deleted.
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginAfter): Deleted.
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::nonCollapsedMarginBefore): Deleted.
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::nonCollapsedMarginAfter): Deleted.
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::collapsedMarginBeforeFromFirstChild): Deleted.
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::collapsedMarginAfterFromLastChild): Deleted.
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithPreviousSibling): Deleted.
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithNextSibling): Deleted.
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginBefore): Deleted.
+        (WebCore::Layout::BlockFormattingContext::MarginCollapse::marginAfter): Deleted.
+        * layout/displaytree/DisplayBox.cpp:
+        (WebCore::Display::Box::Box):
+        * layout/displaytree/DisplayBox.h:
+        (WebCore::Display::Box::hasClearance const):
+        (WebCore::Display::Box::setEstimatedMarginBefore):
+        (WebCore::Display::Box::estimatedMarginBefore const):
+        (WebCore::Display::Box::setHasClearance):
+        (WebCore::Display::Box::invalidateEstimatedMarginBefore):
+        (WebCore::Display::Box::setVerticalMargin):
+        (WebCore::Display::Box::rectWithMargin const):
+        * layout/floats/FloatingContext.cpp:
+        (WebCore::Layout::FloatingContext::verticalPositionWithClearance const):
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::collectInlineContentForSubtree const):
+
 2019-01-09  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         REGRESSION(r239156): [FreeType] fixed width, and synthetic bold/italic not correctly applied since r239156
index 04775ab..28446cf 100644 (file)
@@ -75,6 +75,7 @@ public:
 
     FormattingState& formattingStateForBox(const Box&) const;
     FormattingState& establishedFormattingState(const Box& formattingRoot) const;
+    bool hasFormattingState(const Box& formattingRoot) const { return m_formattingStates.contains(&formattingRoot); }
     FormattingState& createFormattingStateForFormattingRootIfNeeded(const Box& formattingRoot);
 
     Display::Box& displayBoxForLayoutBox(const Box& layoutBox) const;
index 4e1fc56..ac4aa8b 100644 (file)
@@ -50,15 +50,15 @@ struct UsedVerticalMargin {
     struct CollapsedValues {
         Optional<LayoutUnit> before;
         Optional<LayoutUnit> after;
+        bool isCollapsedThrough { false };
     };
     CollapsedValues collapsedValues() const { return m_collapsedValues; }
     bool hasCollapsedValues() const { return m_collapsedValues.before || m_collapsedValues.after; }
     void setCollapsedValues(CollapsedValues collapsedValues) { m_collapsedValues = collapsedValues; }
 
     UsedVerticalMargin(NonCollapsedValues, CollapsedValues);
-
     UsedVerticalMargin() = default;
-    ~UsedVerticalMargin() = default;
+
 private:
     NonCollapsedValues m_nonCollapsedValues;
     CollapsedValues m_collapsedValues;
@@ -74,6 +74,10 @@ struct UsedHorizontalMargin {
     LayoutUnit end;
 };
 
+struct EstimatedMarginBefore {
+    LayoutUnit usedValue;
+};
+
 struct PositiveAndNegativeVerticalMargin {
     struct Values {
         Optional<LayoutUnit> positive;
index 2481116..b24de9f 100644 (file)
@@ -187,11 +187,14 @@ void BlockFormattingContext::computeStaticPosition(const Box& layoutBox) const
 void BlockFormattingContext::computeEstimatedMarginBefore(const Box& layoutBox) const
 {
     auto& layoutState = this->layoutState();
-    auto estimatedMarginBefore = Geometry::estimatedMarginBefore(layoutState, layoutBox);
+    auto estimatedMarginBefore = MarginCollapse::estimatedMarginBefore(layoutState, layoutBox);
+    blockFormattingState().setHasEstimatedMarginBefore(layoutBox);
 
     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
+    displayBox.setTop(adjustedVerticalPositionAfterMarginCollapsing(layoutBox, estimatedMarginBefore.usedValue));
+#if !ASSERT_DISABLED
     displayBox.setEstimatedMarginBefore(estimatedMarginBefore);
-    displayBox.moveVertically(estimatedMarginBefore);
+#endif
 }
 
 void BlockFormattingContext::computeEstimatedMarginBeforeForAncestors(const Box& layoutBox) const
@@ -208,12 +211,10 @@ void BlockFormattingContext::computeEstimatedMarginBeforeForAncestors(const Box&
     // So when we get to the point where we intersect the box with the float to decide if the box needs to move, we don't yet have the final vertical position.
     //
     // The idea here is that as long as we don't cross the block formatting context boundary, we should be able to pre-compute the final top margin.
-    auto& layoutState = this->layoutState();
-
+    auto& formattingState = blockFormattingState();
     for (auto* ancestor = layoutBox.containingBlock(); ancestor && !ancestor->establishesBlockFormattingContext(); ancestor = ancestor->containingBlock()) {
-        auto& displayBox = layoutState.displayBoxForLayoutBox(*ancestor);
         // FIXME: with incremental layout, we might actually have a valid (non-estimated) margin top as well.
-        if (displayBox.estimatedMarginBefore())
+        if (formattingState.hasEstimatedMarginBefore(*ancestor))
             return;
 
         computeEstimatedMarginBefore(*ancestor);
@@ -238,10 +239,10 @@ void BlockFormattingContext::precomputeVerticalPositionForFormattingRootIfNeeded
 }
 
 #ifndef NDEBUG
-static bool hasPrecomputedMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
+static bool hasPrecomputedMarginBefore(const BlockFormattingState& formattingState, const Box& layoutBox)
 {
     for (auto* ancestor = layoutBox.containingBlock(); ancestor && !ancestor->establishesBlockFormattingContext(); ancestor = ancestor->containingBlock()) {
-        if (layoutState.displayBoxForLayoutBox(*ancestor).estimatedMarginBefore())
+        if (formattingState.hasEstimatedMarginBefore(*ancestor))
             continue;
         return false;
     }
@@ -253,7 +254,7 @@ void BlockFormattingContext::computeFloatingPosition(const FloatingContext& floa
 {
     auto& layoutState = this->layoutState();
     ASSERT(layoutBox.isFloatingPositioned());
-    ASSERT(hasPrecomputedMarginBefore(layoutState, layoutBox));
+    ASSERT(hasPrecomputedMarginBefore(blockFormattingState(), layoutBox));
 
     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
     // 8.3.1 Collapsing margins
@@ -273,7 +274,7 @@ void BlockFormattingContext::computePositionToAvoidFloats(const FloatingContext&
     ASSERT(layoutBox.establishesBlockFormattingContext());
     ASSERT(!layoutBox.isFloatingPositioned());
     ASSERT(!layoutBox.hasFloatClear());
-    ASSERT(hasPrecomputedMarginBefore(layoutState, layoutBox));
+    ASSERT(hasPrecomputedMarginBefore(blockFormattingState(), layoutBox));
 
     if (floatingContext.floatingState().isEmpty())
         return;
@@ -292,7 +293,7 @@ void BlockFormattingContext::computeVerticalPositionForFloatClear(const Floating
     // For formatting roots, we already precomputed final position.
     if (!layoutBox.establishesFormattingContext())
         computeEstimatedMarginBeforeForAncestors(layoutBox);
-    ASSERT(hasPrecomputedMarginBefore(layoutState, layoutBox));
+    ASSERT(hasPrecomputedMarginBefore(blockFormattingState(), layoutBox));
 
     if (auto verticalPositionWithClearance = floatingContext.verticalPositionWithClearance(layoutBox))
         layoutState.displayBoxForLayoutBox(layoutBox).setTop(*verticalPositionWithClearance);
@@ -371,15 +372,21 @@ void BlockFormattingContext::computeHeightAndMargin(const Box& layoutBox) const
         }
     }
 
-    auto collapsedMargin = UsedVerticalMargin::CollapsedValues { MarginCollapse::marginBefore(layoutState, layoutBox), MarginCollapse::marginAfter(layoutState, layoutBox) };
+    auto collapsedMargin = MarginCollapse::collapsedVerticalValues(layoutState, layoutBox, heightAndMargin.nonCollapsedMargin);
     auto verticalMargin = UsedVerticalMargin { heightAndMargin.nonCollapsedMargin, collapsedMargin };
     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
+    auto& formattingState = this->blockFormattingState();
+    if (formattingState.hasEstimatedMarginBefore(layoutBox)) {
+        formattingState.clearHasEstimatedMarginBefore(layoutBox);
+        ASSERT(displayBox.estimatedMarginBefore()->usedValue == verticalMargin.before());
+    } else
+        displayBox.setTop(adjustedVerticalPositionAfterMarginCollapsing(layoutBox, verticalMargin.before()));
+
     displayBox.setContentBoxHeight(heightAndMargin.height);
     displayBox.setVerticalMargin(verticalMargin);
-
-    // If this box has already been moved by the estimated vertical margin, no need to move it again.
-    if (layoutBox.isFloatingPositioned() || !displayBox.estimatedMarginBefore())
-        displayBox.moveVertically(verticalMargin.before());
+    // Adjust the previous sibling's margin bottom now that this box's vertical margin is computed.
+    if (MarginCollapse::marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, layoutBox))
+        MarginCollapse::updateCollapsedMarginAfter(layoutState, *layoutBox.previousInFlowSibling(), verticalMargin);
 }
 
 FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::instrinsicWidthConstraints() const
@@ -446,6 +453,37 @@ FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::instrinsic
     return instrinsicWidthConstraints;
 }
 
+LayoutUnit BlockFormattingContext::adjustedVerticalPositionAfterMarginCollapsing(const Box& layoutBox, LayoutUnit marginBefore) const
+{
+    // Now that we've computed the final margin before, let's shift the box's vertical position.
+    // 1. Check if the margin before collapses with the previous box's margin after. if not -> return previous box's bottom inlcuding margin after + marginBefore
+    // 2. Check if the previous box's margins collapse through. If not -> return previous box' bottom excluding margin after + marginBefore (they are supposed to be equal)
+    // 3. Go to previous box and start from step #1 until we hit the parent box.
+    auto& layoutState = this->layoutState();
+    auto* currentLayoutBox = &layoutBox;
+    while (currentLayoutBox) {
+        if (!currentLayoutBox->previousInFlowSibling())
+            break;
+        auto& previousInFlowSibling = *currentLayoutBox->previousInFlowSibling();
+        if (!MarginCollapse::marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, *currentLayoutBox)) {
+            auto& previousDisplayBox = layoutState.displayBoxForLayoutBox(previousInFlowSibling);
+            return previousDisplayBox.rectWithMargin().bottom() + marginBefore;
+        }
+
+        if (!MarginCollapse::marginsCollapseThrough(layoutState, previousInFlowSibling)) {
+            auto& previousDisplayBox = layoutState.displayBoxForLayoutBox(previousInFlowSibling);
+            return previousDisplayBox.bottom() + marginBefore;
+        }
+        currentLayoutBox = &previousInFlowSibling;
+    }
+
+    auto containingBlockContentBoxTop = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxTop();
+    if (MarginCollapse::marginBeforeCollapsesWithParentMarginBefore(layoutState, layoutBox) || MarginCollapse::marginBeforeCollapsesWithParentMarginAfter(layoutState, layoutBox))
+        return containingBlockContentBoxTop;
+
+    return containingBlockContentBoxTop + marginBefore;
+}
+
 }
 }
 
index 21de3b4..500d064 100644 (file)
@@ -50,6 +50,8 @@ public:
     void layout() const override;
 
 private:
+    BlockFormattingState& blockFormattingState() const { return downcast<BlockFormattingState>(formattingState()); }
+
     void layoutFormattingContextRoot(FloatingContext&, const Box&) const;
     void placeInFlowPositionedChildren(const Container&) const;
 
@@ -67,6 +69,7 @@ private:
     void precomputeVerticalPositionForFormattingRootIfNeeded(const Box&) const;
 
     InstrinsicWidthConstraints instrinsicWidthConstraints() const override;
+    LayoutUnit adjustedVerticalPositionAfterMarginCollapsing(const Box&, LayoutUnit marginBefore) const;
 
     // This class implements positioning and sizing for boxes participating in a block formatting context.
     class Geometry : public FormattingContext::Geometry {
@@ -79,9 +82,6 @@ private:
         static bool instrinsicWidthConstraintsNeedChildrenWidth(const Box&);
         static InstrinsicWidthConstraints instrinsicWidthConstraints(const LayoutState&, const Box&);
 
-        static LayoutUnit estimatedMarginBefore(const LayoutState&, const Box&);
-        static LayoutUnit estimatedMarginAfter(const LayoutState&, const Box&);
-
     private:
         static HeightAndMargin inFlowNonReplacedHeightAndMargin(const LayoutState&, const Box&, Optional<LayoutUnit> usedHeight = { });
         static WidthAndMargin inFlowNonReplacedWidthAndMargin(const LayoutState&, const Box&, Optional<LayoutUnit> usedWidth = { });
@@ -92,36 +92,38 @@ private:
     // This class implements margin collapsing for block formatting context.
     class MarginCollapse {
     public:
-        static LayoutUnit marginBefore(const LayoutState&, const Box&);
-        static LayoutUnit marginAfter(const LayoutState&, const Box&);
-
-        static bool marginBeforeCollapsesWithParentMarginAfter(const LayoutState&, const Box&);
-        static bool marginAfterCollapsesWithParentMarginAfter(const LayoutState&, const Box&);
-
-    private:
-        static LayoutUnit collapsedMarginAfterFromLastChild(const LayoutState&, const Box&);
-        static LayoutUnit nonCollapsedMarginAfter(const LayoutState&, const Box&);
-
-        static LayoutUnit computedNonCollapsedMarginBefore(const LayoutState&, const Box&);
-        static LayoutUnit computedNonCollapsedMarginAfter(const LayoutState&, const Box&);
+        static UsedVerticalMargin::CollapsedValues collapsedVerticalValues(const LayoutState&, const Box&, const UsedVerticalMargin::NonCollapsedValues&);
 
-        static LayoutUnit collapsedMarginBeforeFromFirstChild(const LayoutState&, const Box&);
-        static LayoutUnit nonCollapsedMarginBefore(const LayoutState&, const Box&);
+        static EstimatedMarginBefore estimatedMarginBefore(const LayoutState&, const Box&);
+        static void updateCollapsedMarginAfter(const LayoutState&, const Box&, const UsedVerticalMargin& nextSiblingVerticalMargin);
 
         static bool marginBeforeCollapsesWithParentMarginBefore(const LayoutState&, const Box&);
-        static bool marginBeforeCollapsesWithPreviousSibling(const Box&);
-        static bool marginAfterCollapsesWithNextSibling(const Box&);
-        static bool marginAfterCollapsesWithSiblingMarginBeforeWithClearance(const LayoutState&, const Box&);
+        static bool marginBeforeCollapsesWithFirstInFlowChildMarginBefore(const LayoutState&, const Box&);
+        static bool marginBeforeCollapsesWithParentMarginAfter(const LayoutState&, const Box&);
+        static bool marginBeforeCollapsesWithPreviousSiblingMarginAfter(const LayoutState&, const Box&);
+
+        static bool marginAfterCollapsesWithParentMarginAfter(const LayoutState&, const Box&);
+        static bool marginAfterCollapsesWithLastInFlowChildMarginAfter(const LayoutState&, const Box&);
         static bool marginAfterCollapsesWithParentMarginBefore(const LayoutState&, const Box&);
+        static bool marginAfterCollapsesWithNextSiblingMarginBefore(const LayoutState&, const Box&);
+        static bool marginAfterCollapsesWithSiblingMarginBeforeWithClearance(const LayoutState&, const Box&);
+
         static bool marginsCollapseThrough(const LayoutState&, const Box&);
+
+    private:
+        enum class MarginType { Before, After };
+        static PositiveAndNegativeVerticalMargin::Values positiveNegativeValues(const LayoutState&, const Box&, MarginType);
+        static PositiveAndNegativeVerticalMargin::Values positiveNegativeMarginBefore(const LayoutState&, const Box&, const UsedVerticalMargin::NonCollapsedValues&);
+        static PositiveAndNegativeVerticalMargin::Values positiveNegativeMarginAfter(const LayoutState&, const Box&, const UsedVerticalMargin::NonCollapsedValues&);
     };
 
     class Quirks {
     public:
         static bool needsStretching(const LayoutState&, const Box&);
-        static HeightAndMargin stretchedHeight(const LayoutState&, const Box&, HeightAndMargin);
+        static HeightAndMargin stretchedInFlowHeight(const LayoutState&, const Box&, HeightAndMargin);
 
         static bool shouldIgnoreMarginBefore(const LayoutState&, const Box&);
+        static bool shouldIgnoreMarginAfter(const LayoutState&, const Box&);
     };
 };
 
index 4cbbc63..4170eac 100644 (file)
@@ -99,7 +99,6 @@ HeightAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMarg
     };
 
     auto heightAndMargin = compute();
-
     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> height(" << heightAndMargin.height << "px) margin(" << heightAndMargin.nonCollapsedMargin.before << "px, " << heightAndMargin.nonCollapsedMargin.after << "px) -> layoutBox(" << &layoutBox << ")");
     return heightAndMargin;
 }
@@ -257,7 +256,7 @@ HeightAndMargin BlockFormattingContext::Geometry::inFlowHeightAndMargin(const La
     if (!Quirks::needsStretching(layoutState, layoutBox))
         return heightAndMargin;
 
-    heightAndMargin = Quirks::stretchedHeight(layoutState, layoutBox, heightAndMargin);
+    heightAndMargin = Quirks::stretchedInFlowHeight(layoutState, layoutBox, heightAndMargin);
 
     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> streched to viewport -> height(" << heightAndMargin.height << "px) margin(" << heightAndMargin.nonCollapsedMargin.before << "px, " << heightAndMargin.nonCollapsedMargin.after << "px) -> layoutBox(" << &layoutBox << ")");
     return heightAndMargin;
@@ -318,30 +317,6 @@ FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::Geometry::
     return { minimumIntrinsicWidth, maximumIntrinsicWidth };
 }
 
-LayoutUnit BlockFormattingContext::Geometry::estimatedMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
-{
-    ASSERT(layoutBox.isBlockLevelBox());
-    // Can't estimate vertical margins for out of flow boxes (and we shouldn't need to do it for float boxes).
-    ASSERT(layoutBox.isInFlow());
-    // Can't cross block formatting context boundary.
-    ASSERT(!layoutBox.establishesBlockFormattingContext());
-
-    // Let's just use the normal path for now.
-    return MarginCollapse::marginBefore(layoutState, layoutBox);
-}
-
-LayoutUnit BlockFormattingContext::Geometry::estimatedMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
-{
-    ASSERT(layoutBox.isBlockLevelBox());
-    // Can't estimate vertical margins for out of flow boxes (and we shouldn't need to do it for float boxes).
-    ASSERT(layoutBox.isInFlow());
-    // Can't cross block formatting context boundary.
-    ASSERT(!layoutBox.establishesBlockFormattingContext());
-
-    // Let's just use the normal path for now.
-    return MarginCollapse::marginAfter(layoutState, layoutBox);
-}
-
 }
 }
 
index a107974..71a1a79 100644 (file)
@@ -55,7 +55,6 @@ static bool hasMarginBeforeQuirkValue(const Box& layoutBox)
 
 bool BlockFormattingContext::Quirks::needsStretching(const LayoutState& layoutState, const Box& layoutBox)
 {
-    ASSERT(layoutBox.isInFlow());
     // In quirks mode, body stretches to html and html to the initial containing block (height: auto only).
     if (!layoutState.inQuirksMode())
         return false;
@@ -66,8 +65,9 @@ bool BlockFormattingContext::Quirks::needsStretching(const LayoutState& layoutSt
     return layoutBox.style().logicalHeight().isAuto();
 }
 
-HeightAndMargin BlockFormattingContext::Quirks::stretchedHeight(const LayoutState& layoutState, const Box& layoutBox, HeightAndMargin heightAndMargin)
+HeightAndMargin BlockFormattingContext::Quirks::stretchedInFlowHeight(const LayoutState& layoutState, const Box& layoutBox, HeightAndMargin heightAndMargin)
 {
+    ASSERT(layoutBox.isInFlow());
     ASSERT(layoutBox.isDocumentBox() || layoutBox.isBodyBox());
 
     auto& documentBox = layoutBox.isDocumentBox() ? layoutBox : *layoutBox.parent();
@@ -87,11 +87,12 @@ HeightAndMargin BlockFormattingContext::Quirks::stretchedHeight(const LayoutStat
         // Here is the quirky part for body box:
         // Stretch the body using the initial containing block's height and shrink it with document box's margin/border/padding.
         // This looks extremely odd when html has non-auto height.
-        auto verticalMargin = Geometry::estimatedMarginBefore(layoutState, documentBox) + Geometry::estimatedMarginAfter(layoutState, documentBox);
-        strechedHeight -= verticalMargin;
+        auto documentBoxVerticalMargin = Geometry::computedVerticalMargin(layoutState, documentBox);
+        strechedHeight -= (documentBoxVerticalMargin.before.valueOr(0) + documentBoxVerticalMargin.after.valueOr(0));
 
-        auto nonCollapsedValues = heightAndMargin.nonCollapsedMargin;
-        totalVerticalMargin = nonCollapsedValues.before + nonCollapsedValues.after;
+        auto nonCollapsedMargin = heightAndMargin.nonCollapsedMargin;
+        auto collapsedMargin = MarginCollapse::collapsedVerticalValues(layoutState, layoutBox, nonCollapsedMargin);
+        totalVerticalMargin = collapsedMargin.before.valueOr(nonCollapsedMargin.before) + collapsedMargin.after.valueOr(nonCollapsedMargin.after);
     }
 
     // Stretch but never overstretch with the margins.
@@ -109,6 +110,20 @@ bool BlockFormattingContext::Quirks::shouldIgnoreMarginBefore(const LayoutState&
     return layoutState.inQuirksMode() && isQuirkContainer(*layoutBox.parent()) && hasMarginBeforeQuirkValue(layoutBox);
 }
 
+bool BlockFormattingContext::Quirks::shouldIgnoreMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
+{
+    // Ignore maring after when the ignored margin before collapses through.
+    if (!shouldIgnoreMarginBefore(layoutState, layoutBox))
+        return false;
+
+    ASSERT(layoutBox.parent());
+    auto& parent = *layoutBox.parent();
+    if (parent.firstInFlowOrFloatingChild() != &layoutBox || parent.firstInFlowOrFloatingChild() != parent.lastInFlowOrFloatingChild())
+        return false;
+
+    return MarginCollapse::marginsCollapseThrough(layoutState, layoutBox);
+}
+
 }
 }
 
index bb96693..bb456b5 100644 (file)
@@ -28,6 +28,7 @@
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "FormattingState.h"
+#include "MarginTypes.h"
 #include <wtf/IsoMalloc.h>
 
 namespace WebCore {
@@ -42,6 +43,18 @@ public:
     virtual ~BlockFormattingState();
 
     std::unique_ptr<FormattingContext> createFormattingContext(const Box& formattingContextRoot) override;
+
+    void setPositiveAndNegativeVerticalMargin(const Box& layoutBox, PositiveAndNegativeVerticalMargin verticalMargin) { m_positiveAndNegativeVerticalMargin.set(&layoutBox, verticalMargin); }
+    bool hasPositiveAndNegativeVerticalMargin(const Box& layoutBox) const { return m_positiveAndNegativeVerticalMargin.contains(&layoutBox); }
+    PositiveAndNegativeVerticalMargin positiveAndNegativeVerticalMargin(const Box& layoutBox) const { return m_positiveAndNegativeVerticalMargin.get(&layoutBox); }
+
+    void setHasEstimatedMarginBefore(const Box& layoutBox) { m_estimatedMarginBeforeList.add(&layoutBox); }
+    void clearHasEstimatedMarginBefore(const Box& layoutBox) { m_estimatedMarginBeforeList.remove(&layoutBox); }
+    bool hasEstimatedMarginBefore(const Box& layoutBox) const { return m_estimatedMarginBeforeList.contains(&layoutBox); }
+
+private:
+    HashMap<const Box*, PositiveAndNegativeVerticalMargin> m_positiveAndNegativeVerticalMargin;
+    HashSet<const Box*> m_estimatedMarginBeforeList;
 };
 
 }
index c71d392..f9ab127 100644 (file)
@@ -70,12 +70,11 @@ static bool hasPaddingAfter(const Box& layoutBox)
     return hasPadding(layoutBox.style().paddingAfter());
 }
 
-static bool hasClearance(const Box& layoutBox)
+static bool hasClearance(const LayoutState& layoutState, const Box& layoutBox)
 {
     if (!layoutBox.hasFloatClear())
         return false;
-    // FIXME
-    return false;
+    return layoutState.displayBoxForLayoutBox(layoutBox).hasClearance();
 }
 
 static bool establishesBlockFormattingContext(const Box& layoutBox)
@@ -87,93 +86,6 @@ static bool establishesBlockFormattingContext(const Box& layoutBox)
     return layoutBox.establishesBlockFormattingContext();
 }
 
-static LayoutUnit marginValue(LayoutUnit currentMarginValue, LayoutUnit candidateMarginValue)
-{
-    if (!candidateMarginValue)
-        return currentMarginValue;
-    if (!currentMarginValue)
-        return candidateMarginValue;
-    // Both margins are positive.
-    if (candidateMarginValue > 0 && currentMarginValue > 0)
-        return std::max(candidateMarginValue, currentMarginValue);
-    // Both margins are negative.
-    if (candidateMarginValue < 0 && currentMarginValue < 0)
-        return 0 - std::max(std::abs(candidateMarginValue.toFloat()), std::abs(currentMarginValue.toFloat()));
-    // One of the margins is negative.
-    return currentMarginValue + candidateMarginValue;
-}
-
-LayoutUnit BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
-{
-    ASSERT(layoutBox.isBlockLevelBox());
-
-    return Geometry::computedVerticalMargin(layoutState, layoutBox).before.valueOr(0);
-}
-
-LayoutUnit BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
-{
-    ASSERT(layoutBox.isBlockLevelBox());
-
-    return Geometry::computedVerticalMargin(layoutState, layoutBox).after.valueOr(0);
-}
-
-LayoutUnit BlockFormattingContext::MarginCollapse::nonCollapsedMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
-{
-    ASSERT(layoutBox.isBlockLevelBox());
-
-    // Non collapsed margin top includes collapsed margin from inflow first child.
-    return marginValue(computedNonCollapsedMarginBefore(layoutState, layoutBox), collapsedMarginBeforeFromFirstChild(layoutState, layoutBox));
-}
-
-LayoutUnit BlockFormattingContext::MarginCollapse::nonCollapsedMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
-{
-    ASSERT(layoutBox.isBlockLevelBox());
-
-    // Non collapsed margin bottom includes collapsed margin from inflow last child.
-    return marginValue(computedNonCollapsedMarginAfter(layoutState, layoutBox), collapsedMarginAfterFromLastChild(layoutState, layoutBox));
-}
-
-LayoutUnit BlockFormattingContext::MarginCollapse::collapsedMarginBeforeFromFirstChild(const LayoutState& layoutState, const Box& layoutBox)
-{
-    ASSERT(layoutBox.isBlockLevelBox());
-
-    // Check if the first child collapses its margin top.
-    if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
-        return 0;
-
-    // Do not collapse margin with a box from a non-block formatting context <div><span>foobar</span></div>.
-    if (layoutBox.establishesFormattingContext() && !layoutBox.establishesBlockFormattingContextOnly())
-        return 0;
-
-    // FIXME: Take collapsed through margin into account.
-    auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild();
-    if (!marginBeforeCollapsesWithParentMarginBefore(layoutState, firstInFlowChild))
-        return 0;
-    // Collect collapsed margin top recursively.
-    return marginValue(computedNonCollapsedMarginBefore(layoutState, firstInFlowChild), collapsedMarginBeforeFromFirstChild(layoutState, firstInFlowChild));
-}
-
-LayoutUnit BlockFormattingContext::MarginCollapse::collapsedMarginAfterFromLastChild(const LayoutState& layoutState, const Box& layoutBox)
-{
-    ASSERT(layoutBox.isBlockLevelBox());
-
-    // Check if the last child propagates its margin bottom.
-    if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
-        return 0;
-
-    // Do not collapse margin with a box from a non-block formatting context <div><span>foobar</span></div>.
-    if (layoutBox.establishesFormattingContext() && !layoutBox.establishesBlockFormattingContextOnly())
-        return 0;
-
-    // FIXME: Check for collapsed through margin.
-    auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild();
-    if (!marginAfterCollapsesWithParentMarginAfter(layoutState, lastInFlowChild))
-        return 0;
-
-    // Collect collapsed margin bottom recursively.
-    return marginValue(computedNonCollapsedMarginAfter(layoutState, lastInFlowChild), collapsedMarginAfterFromLastChild(layoutState, lastInFlowChild));
-}
-
 bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
 {
     // 1. This is the last in-flow child and its margins collapse through and the margin after collapses with parent's margin after or
@@ -185,7 +97,7 @@ bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarg
             return false;
         if (currentBox == lastInFlowChild)
             return marginAfterCollapsesWithParentMarginAfter(layoutState, *currentBox); 
-        if (!marginAfterCollapsesWithNextSibling(*currentBox))
+        if (!marginAfterCollapsesWithNextSiblingMarginBefore(layoutState, *currentBox))
             return false;
     }
     ASSERT_NOT_REACHED();
@@ -229,10 +141,86 @@ bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarg
         return false;
 
     // ...and the child has no clearance.
-    if (hasClearance(layoutBox))
+    if (hasClearance(layoutState, layoutBox))
         return false;
 
-    if (BlockFormattingContext::Quirks::shouldIgnoreMarginBefore(layoutState, layoutBox))
+    return true;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithPreviousSiblingMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
+{
+    ASSERT(layoutBox.isBlockLevelBox());
+
+    if (!layoutBox.previousInFlowSibling())
+        return false;
+
+    auto& previousInFlowSibling = *layoutBox.previousInFlowSibling();
+
+    // Margins between a floated box and any other box do not collapse.
+    if (layoutBox.isFloatingPositioned() || previousInFlowSibling.isFloatingPositioned())
+        return false;
+
+    // Margins of absolutely positioned boxes do not collapse.
+    if ((layoutBox.isOutOfFlowPositioned() && !layoutBox.style().top().isAuto())
+        || (previousInFlowSibling.isOutOfFlowPositioned() && !previousInFlowSibling.style().bottom().isAuto()))
+        return false;
+
+    // Margins of inline-block boxes do not collapse.
+    if (layoutBox.isInlineBlockBox() || previousInFlowSibling.isInlineBlockBox())
+        return false;
+
+    // The bottom margin of an in-flow block-level element always collapses with the top margin of
+    // its next in-flow block-level sibling, unless that sibling has clearance.
+    if (hasClearance(layoutState, layoutBox))
+        return false;
+
+    return true;
+}
+
+bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithFirstInFlowChildMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
+{
+    if (layoutBox.isAnonymous())
+        return false;
+
+    ASSERT(layoutBox.isBlockLevelBox());
+    // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children.
+    if (establishesBlockFormattingContext(layoutBox))
+        return false;
+
+    // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children.
+    if (establishesBlockFormattingContext(layoutBox))
+        return false;
+
+    // The top margin of an in-flow block element collapses with its first in-flow block-level
+    // child's top margin if the element has no top border...
+    if (hasBorderBefore(layoutBox))
+        return false;
+
+    // ...no top padding
+    if (hasPaddingBefore(layoutBox))
+        return false;
+
+    if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
+        return false;
+
+    auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild();
+    if (!firstInFlowChild.isBlockLevelBox())
+        return false;
+
+    // ...and the child has no clearance.
+    if (hasClearance(layoutState, firstInFlowChild))
+        return false;
+
+    // Margins between a floated box and any other box do not collapse.
+    if (firstInFlowChild.isFloatingPositioned())
+        return false;
+
+    // Margins of absolutely positioned boxes do not collapse.
+    if (firstInFlowChild.isOutOfFlowPositioned())
+        return false;
+
+    // Margins of inline-block boxes do not collapse.
+    if (firstInFlowChild.isInlineBlockBox())
         return false;
 
     return true;
@@ -248,7 +236,7 @@ bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithSiblingMarg
     for (auto* previousSibling = layoutBox.previousInFlowSibling(); previousSibling; previousSibling = previousSibling->previousInFlowSibling()) {
         if (!marginsCollapseThrough(layoutState, *previousSibling))
             return false;
-        if (hasClearance(*previousSibling))
+        if (hasClearance(layoutState, *previousSibling))
             return true;
     }
     return false;
@@ -265,7 +253,7 @@ bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithParentMargi
             return false;
         if (currentBox == firstInFlowChild)
             return marginBeforeCollapsesWithParentMarginBefore(layoutState, *currentBox); 
-        if (!marginBeforeCollapsesWithPreviousSibling(*currentBox))
+        if (!marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, *currentBox))
             return false;
     }
     ASSERT_NOT_REACHED();
@@ -324,44 +312,66 @@ bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithParentMargi
     return true;
 }
 
-bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithPreviousSibling(const Box& layoutBox)
+bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithLastInFlowChildMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
 {
     ASSERT(layoutBox.isBlockLevelBox());
 
-    if (!layoutBox.previousInFlowSibling())
+    // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children.
+    if (establishesBlockFormattingContext(layoutBox))
         return false;
 
-    auto& previousInFlowSibling = *layoutBox.previousInFlowSibling();
+    if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
+        return false;
+
+    auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild();
+    if (!lastInFlowChild.isBlockLevelBox())
+        return false;
+
+    // The bottom margin of an in-flow block box with a 'height' of 'auto' collapses with its last in-flow block-level child's bottom margin, if:
+    if (!layoutBox.style().height().isAuto())
+        return false;
+
+    // the box has no bottom padding, and
+    if (hasPaddingBefore(layoutBox))
+        return false;
+
+    // the box has no bottom border, and
+    if (hasBorderBefore(layoutBox))
+        return false;
+
+    // the child's bottom margin neither collapses with a top margin that has clearance...
+    if (marginAfterCollapsesWithSiblingMarginBeforeWithClearance(layoutState, lastInFlowChild))
+        return false;
+
+    // nor (if the box's min-height is non-zero) with the box's top margin.
+    auto computedMinHeight = layoutBox.style().logicalMinHeight();
+    if (!computedMinHeight.isAuto() && computedMinHeight.value()
+        && (marginAfterCollapsesWithParentMarginBefore(layoutState, lastInFlowChild) || hasClearance(layoutState, lastInFlowChild)))
+        return false;
 
     // Margins between a floated box and any other box do not collapse.
-    if (layoutBox.isFloatingPositioned() || previousInFlowSibling.isFloatingPositioned())
+    if (lastInFlowChild.isFloatingPositioned())
         return false;
 
     // Margins of absolutely positioned boxes do not collapse.
-    if ((layoutBox.isOutOfFlowPositioned() && !layoutBox.style().top().isAuto())
-        || (previousInFlowSibling.isOutOfFlowPositioned() && !previousInFlowSibling.style().bottom().isAuto()))
+    if (lastInFlowChild.isOutOfFlowPositioned())
         return false;
 
     // Margins of inline-block boxes do not collapse.
-    if (layoutBox.isInlineBlockBox() || previousInFlowSibling.isInlineBlockBox())
-        return false;
-
-    // The bottom margin of an in-flow block-level element always collapses with the top margin of
-    // its next in-flow block-level sibling, unless that sibling has clearance.
-    if (hasClearance(layoutBox))
+    if (lastInFlowChild.isInlineBlockBox())
         return false;
 
     return true;
 }
 
-bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithNextSibling(const Box& layoutBox)
+bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithNextSiblingMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
 {
     ASSERT(layoutBox.isBlockLevelBox());
 
     if (!layoutBox.nextInFlowSibling())
         return false;
 
-    return marginBeforeCollapsesWithPreviousSibling(*layoutBox.nextInFlowSibling());
+    return marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, *layoutBox.nextInFlowSibling());
 }
 
 bool BlockFormattingContext::MarginCollapse::marginsCollapseThrough(const LayoutState& layoutState, const Box& layoutBox)
@@ -387,77 +397,234 @@ bool BlockFormattingContext::MarginCollapse::marginsCollapseThrough(const Layout
     if (!is<Container>(layoutBox))
         return true;
 
-    if (layoutBox.establishesInlineFormattingContext()) {
-        if (downcast<InlineFormattingState>(layoutState.establishedFormattingState(layoutBox)).inlineRuns().isEmpty())
-            return false;
-    } else {
-        for (auto* inflowChild = downcast<Container>(layoutBox).firstInFlowChild(); inflowChild; inflowChild = inflowChild->nextInFlowSibling()) {
-            if (!marginsCollapseThrough(layoutState, *inflowChild))
-                return true;
+    if (!downcast<Container>(layoutBox).hasInFlowChild())
+        return !establishesBlockFormattingContext(layoutBox);
+
+    if (Quirks::needsStretching(layoutState, layoutBox))
+        return false;
+
+    if (layoutBox.establishesFormattingContext()) {
+        if (layoutBox.establishesInlineFormattingContext()) {
+            // If we get here through margin estimation, we don't necessarily have an actual state for this layout box since
+            // we haven't started laying it out yet.
+            if (!layoutState.hasFormattingState(layoutBox))
+                return false;
+            return downcast<InlineFormattingState>(layoutState.establishedFormattingState(layoutBox)).inlineRuns().isEmpty();
         }
+
+        if (establishesBlockFormattingContext(layoutBox))
+            return false;
     }
 
+    for (auto* inflowChild = downcast<Container>(layoutBox).firstInFlowChild(); inflowChild; inflowChild = inflowChild->nextInFlowSibling()) {
+        if (establishesBlockFormattingContext(*inflowChild))
+            return false;
+        if (!marginsCollapseThrough(layoutState, *inflowChild))
+            return false;
+    }
     return true;
 }
 
-LayoutUnit BlockFormattingContext::MarginCollapse::marginBefore(const LayoutState& layoutState, const Box& layoutBox)
+static PositiveAndNegativeVerticalMargin::Values computedPositiveAndNegativeMargin(PositiveAndNegativeVerticalMargin::Values a, PositiveAndNegativeVerticalMargin::Values b)
 {
-    if (layoutBox.isAnonymous())
-        return 0;
+    PositiveAndNegativeVerticalMargin::Values computedValues;
+    if (a.positive && b.positive)
+        computedValues.positive = std::max(*a.positive, *b.positive);
+    else
+        computedValues.positive = a.positive ? a.positive : b.positive;
+
+    if (a.negative && b.negative)
+        computedValues.negative = std::min(*a.negative, *b.negative);
+    else
+        computedValues.negative = a.negative ? a.negative : b.negative;
+
+    return computedValues;
+}
 
-    ASSERT(layoutBox.isBlockLevelBox());
+static PositiveAndNegativeVerticalMargin::Values computedPositiveAndNegativeMargin(PositiveAndNegativeVerticalMargin::Values a, Optional<LayoutUnit> marginValue)
+{
+    if (!marginValue || !marginValue.value())
+        return a;
+    if (*marginValue > 0)
+        return computedPositiveAndNegativeMargin(a, { marginValue, { } });
+    if (*marginValue < 0)
+        return computedPositiveAndNegativeMargin(a, { { }, marginValue });
+    ASSERT_NOT_REACHED();
+    return { };
+}
+
+static Optional<LayoutUnit> marginValue(PositiveAndNegativeVerticalMargin::Values marginValues)
+{
+    // When two or more margins collapse, the resulting margin width is the maximum of the collapsing margins' widths.
+    // In the case of negative margins, the maximum of the absolute values of the negative adjoining margins is deducted from the maximum
+    // of the positive adjoining margins. If there are no positive margins, the maximum of the absolute values of the adjoining margins is deducted from zero.
+    if (!marginValues.negative)
+        return marginValues.positive;
+
+    if (!marginValues.positive)
+        return marginValues.negative;
 
-    // TODO: take _hasAdjoiningMarginBeforeAndAfter() into account.
-    if (marginBeforeCollapsesWithParentMarginBefore(layoutState, layoutBox))
-        return 0;
-
-    // FIXME: Find out the logic behind this.
-    if (BlockFormattingContext::Quirks::shouldIgnoreMarginBefore(layoutState, layoutBox))
-        return 0;
-
-    if (!marginBeforeCollapsesWithPreviousSibling(layoutBox)) {
-        if (!marginsCollapseThrough(layoutState, layoutBox))
-            return nonCollapsedMarginBefore(layoutState, layoutBox);
-        // Compute the collapsed through value.
-        auto marginBefore = nonCollapsedMarginBefore(layoutState, layoutBox);
-        auto marginAfter = nonCollapsedMarginAfter(layoutState, layoutBox); 
-        return marginValue(marginBefore, marginAfter);
+    return *marginValues.positive + *marginValues.negative;
+}
+
+void BlockFormattingContext::MarginCollapse::updateCollapsedMarginAfter(const LayoutState& layoutState, const Box& layoutBox, const UsedVerticalMargin& nextSiblingVerticalMargin)
+{
+    // 1. Get the margin before value from the next in-flow sibling. This is the same as this box's margin after value now since they are collapsed.
+    // 2. Update the collapsed margin after value as well as the positive/negative cache.
+    // 3. Check if the box's margins collapse through.
+    // 4. If so, update the collapsed margin before value as well as the positive/negative cache.
+    // 5. In case of collapsed through margins check if the before margin collapes with the previous inflow sibling's after margin.
+    // 6. If so, jump to #2.
+    if (!marginAfterCollapsesWithNextSiblingMarginBefore(layoutState, layoutBox))
+        return;
+
+    auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
+    auto verticalMargin = displayBox.verticalMargin();
+    auto verticalMarginAfter = nextSiblingVerticalMargin.before();
+    Optional<LayoutUnit> verticalMarginBefore;
+
+    auto marginsCollapseThrough = MarginCollapse::marginsCollapseThrough(layoutState, layoutBox);
+    if (marginsCollapseThrough)
+        verticalMarginBefore = verticalMarginAfter; 
+    else if (verticalMargin.hasCollapsedValues())
+        verticalMarginBefore = verticalMargin.collapsedValues().before;
+
+    // Update vertical margin values.
+    verticalMargin.setCollapsedValues({ verticalMarginBefore, verticalMarginAfter });
+    displayBox.setVerticalMargin(verticalMargin);
+
+    // Update positive/negative cache.
+    auto& blockFormattingState = downcast<BlockFormattingState>(layoutState.formattingStateForBox(layoutBox));
+    auto positiveNegativeMargin = blockFormattingState.positiveAndNegativeVerticalMargin(layoutBox);
+    auto nextSiblingPositiveNegativeMarginBefore = blockFormattingState.positiveAndNegativeVerticalMargin(*layoutBox.nextInFlowSibling()).before;
+
+    positiveNegativeMargin.after = computedPositiveAndNegativeMargin(nextSiblingPositiveNegativeMarginBefore, positiveNegativeMargin.after);
+    if (marginsCollapseThrough) {
+        positiveNegativeMargin.before = computedPositiveAndNegativeMargin(positiveNegativeMargin.before, positiveNegativeMargin.after);
+        positiveNegativeMargin.after = positiveNegativeMargin.before; 
     }
+    blockFormattingState.setPositiveAndNegativeVerticalMargin(layoutBox, positiveNegativeMargin);
+
+    if (!marginsCollapseThrough || !marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, layoutBox))
+        return;
+
+    updateCollapsedMarginAfter(layoutState, *layoutBox.previousInFlowSibling(), verticalMargin);
+}
+
+PositiveAndNegativeVerticalMargin::Values BlockFormattingContext::MarginCollapse::positiveNegativeValues(const LayoutState& layoutState, const Box& layoutBox, MarginType marginType)
+{
+    auto& blockFormattingState = downcast<BlockFormattingState>(layoutState.formattingStateForBox(layoutBox));
+    if (blockFormattingState.hasPositiveAndNegativeVerticalMargin(layoutBox)) {
+        auto positiveAndNegativeVerticalMargin = blockFormattingState.positiveAndNegativeVerticalMargin(layoutBox);
+        return marginType == MarginType::Before ? positiveAndNegativeVerticalMargin.before : positiveAndNegativeVerticalMargin.after; 
+    }
+    // This is the estimate path. We don't yet have positive/negative margin computed.
+    auto computedVerticalMargin = Geometry::computedVerticalMargin(layoutState, layoutBox);
+    auto nonCollapsedMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) }; 
+
+    if (marginType == MarginType::Before)
+        return positiveNegativeMarginBefore(layoutState, layoutBox, nonCollapsedMargin);
+    return positiveNegativeMarginAfter(layoutState, layoutBox, nonCollapsedMargin);
+}
 
-    // The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling,
-    // unless that sibling has clearance.
-    auto* previousInFlowSibling = layoutBox.previousInFlowSibling();
-    if (!previousInFlowSibling)
-        return nonCollapsedMarginBefore(layoutState, layoutBox);
+PositiveAndNegativeVerticalMargin::Values BlockFormattingContext::MarginCollapse::positiveNegativeMarginBefore(const LayoutState& layoutState, const Box& layoutBox, const UsedVerticalMargin::NonCollapsedValues& nonCollapsedValues)
+{
+    auto firstChildCollapsedMarginBefore = [&]() -> PositiveAndNegativeVerticalMargin::Values {
+        if (!is<Container>(layoutBox))
+            return { };
+        auto* firstInFlowChild = downcast<Container>(layoutBox).firstInFlowChild();
+        if (!firstInFlowChild)
+            return { };
+        if (!marginBeforeCollapsesWithFirstInFlowChildMarginBefore(layoutState, layoutBox))
+            return { };
+        if (Quirks::shouldIgnoreMarginBefore(layoutState, *firstInFlowChild))
+            return { };
+        return positiveNegativeValues(layoutState, *firstInFlowChild, MarginType::Before);
+    };
+
+    auto previouSiblingCollapsedMarginAfter = [&]() -> PositiveAndNegativeVerticalMargin::Values {
+        auto* previousInFlowSibling = layoutBox.previousInFlowSibling();
+        if (!previousInFlowSibling)
+            return { };
+        if (!marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, layoutBox))
+            return { };
+        if (Quirks::shouldIgnoreMarginAfter(layoutState, *previousInFlowSibling))
+            return { };
+        return positiveNegativeValues(layoutState, *previousInFlowSibling, MarginType::After);
+    };
+
+    // 1. Gather positive and negative margin values from first child if margins are adjoining.
+    // 2. Gather positive and negative margin values from previous inflow sibling if margins are adjoining.
+    // 3. Compute min/max positive and negative collapsed margin values using non-collpased computed margin before.
+    auto collapsedMarginBefore = computedPositiveAndNegativeMargin(firstChildCollapsedMarginBefore(), previouSiblingCollapsedMarginAfter());
+    return computedPositiveAndNegativeMargin(collapsedMarginBefore, nonCollapsedValues.before);
+}
 
-    auto previousSiblingMarginAfter = nonCollapsedMarginAfter(layoutState, *previousInFlowSibling);
-    auto marginBefore = nonCollapsedMarginBefore(layoutState, layoutBox);
-    return marginValue(marginBefore, previousSiblingMarginAfter);
+PositiveAndNegativeVerticalMargin::Values BlockFormattingContext::MarginCollapse::positiveNegativeMarginAfter(const LayoutState& layoutState, const Box& layoutBox, const UsedVerticalMargin::NonCollapsedValues& nonCollapsedValues)
+{
+    auto lastChildCollapsedMarginAfter = [&]() -> PositiveAndNegativeVerticalMargin::Values {
+        if (!is<Container>(layoutBox))
+            return { };
+        auto* lastInFlowChild = downcast<Container>(layoutBox).lastInFlowChild();
+        if (!lastInFlowChild)
+            return { };
+        if (!marginAfterCollapsesWithLastInFlowChildMarginAfter(layoutState, layoutBox))
+            return { };
+        if (Quirks::shouldIgnoreMarginAfter(layoutState, *lastInFlowChild))
+            return { };
+        return positiveNegativeValues(layoutState, *lastInFlowChild, MarginType::After);
+    };
+
+    // We don't know yet the margin before value of the next sibling. Let's just pretend it does not have one and 
+    // update it later when we compute the next sibling's margin before. See updateCollapsedMarginAfter.
+    return computedPositiveAndNegativeMargin(lastChildCollapsedMarginAfter(), nonCollapsedValues.after);
 }
 
-LayoutUnit BlockFormattingContext::MarginCollapse::marginAfter(const LayoutState& layoutState, const Box& layoutBox)
+EstimatedMarginBefore BlockFormattingContext::MarginCollapse::estimatedMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
 {
     if (layoutBox.isAnonymous())
-        return 0;
+        return { };
 
     ASSERT(layoutBox.isBlockLevelBox());
+    // Can't estimate vertical margins for out of flow boxes (and we shouldn't need to do it for float boxes).
+    ASSERT(layoutBox.isInFlow());
+    ASSERT(!layoutBox.replaced());
+    // Can't cross block formatting context boundary.
+    ASSERT(!layoutBox.establishesBlockFormattingContext());
+    
+    auto computedVerticalMargin = Geometry::computedVerticalMargin(layoutState, layoutBox);
+    auto nonCollapsedMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) }; 
 
-    // TODO: take _hasAdjoiningMarginBeforeAndBottom() into account.
-    if (marginAfterCollapsesWithParentMarginAfter(layoutState, layoutBox))
-        return 0;
+    if (!marginsCollapseThrough(layoutState, layoutBox))
+        return { marginValue(positiveNegativeMarginBefore(layoutState, layoutBox, nonCollapsedMargin)).valueOr(0) };
+    
+    return { marginValue(computedPositiveAndNegativeMargin(positiveNegativeMarginBefore(layoutState, layoutBox, nonCollapsedMargin),
+        positiveNegativeMarginAfter(layoutState, layoutBox, nonCollapsedMargin))).valueOr(0) };
+}
 
-    if (marginsCollapseThrough(layoutState, layoutBox))
-        return 0;
+UsedVerticalMargin::CollapsedValues BlockFormattingContext::MarginCollapse::collapsedVerticalValues(const LayoutState& layoutState, const Box& layoutBox, const UsedVerticalMargin::NonCollapsedValues& nonCollapsedValues)
+{
+    if (layoutBox.isAnonymous())
+        return { };
 
-    // Floats and out of flow positioned boxes do not collapse their margins.
-    if (!marginAfterCollapsesWithNextSibling(layoutBox))
-        return nonCollapsedMarginAfter(layoutState, layoutBox);
+    ASSERT(layoutBox.isBlockLevelBox());
+    // 1. Get min/max margin top values from the first in-flow child if we are collapsing margin top with it.
+    // 2. Get min/max margin top values from the previous in-flow sibling, if we are collapsing margin top with it.
+    // 3. Get this layout box's computed margin top value.
+    // 4. Update the min/max value and compute the final margin. 
+    auto positiveNegativeMarginBefore = MarginCollapse::positiveNegativeMarginBefore(layoutState, layoutBox, nonCollapsedValues);
+    auto positiveNegativeMarginAfter = MarginCollapse::positiveNegativeMarginAfter(layoutState, layoutBox, nonCollapsedValues);
+
+    auto marginsCollapseThrough = MarginCollapse::marginsCollapseThrough(layoutState, layoutBox); 
+    if (marginsCollapseThrough) {
+        positiveNegativeMarginBefore = computedPositiveAndNegativeMargin(positiveNegativeMarginBefore, positiveNegativeMarginAfter);
+        positiveNegativeMarginAfter = positiveNegativeMarginBefore;         
+    }
 
-    // The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling,
-    // unless that sibling has clearance.
-    if (layoutBox.nextInFlowSibling())
-        return 0;
-    return nonCollapsedMarginAfter(layoutState, layoutBox);
+    auto& blockFormattingState = downcast<BlockFormattingState>(layoutState.formattingStateForBox(layoutBox));
+    blockFormattingState.setPositiveAndNegativeVerticalMargin(layoutBox, { positiveNegativeMarginBefore, positiveNegativeMarginAfter });
+
+    return { marginValue(positiveNegativeMarginBefore), marginValue(positiveNegativeMarginAfter), marginsCollapseThrough }; 
 }
 
 }
index 3140a55..631ef71 100644 (file)
@@ -60,7 +60,7 @@ Box::Box(const Box& other)
     , m_horizontalMargin(other.m_horizontalMargin)
     , m_verticalMargin(other.m_verticalMargin)
     , m_horizontalComputedMargin(other.m_horizontalComputedMargin)
-    , m_estimatedMarginBefore(other.m_estimatedMarginBefore)
+    , m_hasClearance(other.m_hasClearance)
     , m_border(other.m_border)
     , m_padding(other.m_padding)
 #if !ASSERT_DISABLED
@@ -74,6 +74,7 @@ Box::Box(const Box& other)
     , m_hasValidPadding(other.m_hasValidPadding)
     , m_hasValidContentHeight(other.m_hasValidContentHeight)
     , m_hasValidContentWidth(other.m_hasValidContentWidth)
+    , m_estimatedMarginBefore(other.m_estimatedMarginBefore)
 #endif
 {
 }
index 31d86a6..9aefb18 100644 (file)
@@ -136,21 +136,20 @@ public:
     LayoutUnit width() const { return borderLeft() + paddingLeft().valueOr(0) + contentBoxWidth() + paddingRight().valueOr(0) + borderRight(); }
     LayoutUnit height() const { return borderTop() + paddingTop().valueOr(0) + contentBoxHeight() + paddingBottom().valueOr(0) + borderBottom(); }
     Rect rect() const { return { top(), left(), width(), height() }; }
-    Rect rectWithMargin() const { return { top() - marginBefore(), left() - marginStart(), marginStart() + width() + marginEnd(), marginBefore() + height() + marginAfter() }; }
+    Rect rectWithMargin() const;
 
     Layout::UsedVerticalMargin verticalMargin() const;
     LayoutUnit marginBefore() const;
     LayoutUnit marginStart() const;
     LayoutUnit marginAfter() const;
     LayoutUnit marginEnd() const;
+    bool hasClearance() const { return m_hasClearance; }
 
     LayoutUnit nonCollapsedMarginBefore() const;
     LayoutUnit nonCollapsedMarginAfter() const;
     Optional<LayoutUnit> computedMarginStart() const;
     Optional<LayoutUnit> computedMarginEnd() const;
 
-    Optional<LayoutUnit> estimatedMarginBefore() const { return m_estimatedMarginBefore; }
-
     LayoutUnit borderTop() const;
     LayoutUnit borderLeft() const;
     LayoutUnit borderBottom() const;
@@ -175,6 +174,11 @@ public:
     Rect paddingBox() const;
     Rect contentBox() const;
 
+#if !ASSERT_DISABLED
+    void setEstimatedMarginBefore(Layout::EstimatedMarginBefore marginBefore) { m_estimatedMarginBefore = marginBefore; }
+    Optional<Layout::EstimatedMarginBefore> estimatedMarginBefore() const { return m_estimatedMarginBefore; }
+#endif
+
 private:
     struct Style {
         Style(const RenderStyle&);
@@ -194,7 +198,7 @@ private:
     void setHorizontalMargin(Layout::UsedHorizontalMargin);
     void setVerticalMargin(Layout::UsedVerticalMargin);
     void setHorizontalComputedMargin(Layout::ComputedHorizontalMargin);
-    void setEstimatedMarginBefore(LayoutUnit marginBefore) { m_estimatedMarginBefore = marginBefore; }
+    void setHasClearance() { m_hasClearance = true; }
 
     void setBorder(Layout::Edges);
     void setPadding(Optional<Layout::Edges>);
@@ -203,6 +207,7 @@ private:
     void invalidateMargin();
     void invalidateBorder() { m_hasValidBorder = false; }
     void invalidatePadding() { m_hasValidPadding = false; }
+    void invalidateEstimatedMarginBefore() { m_estimatedMarginBefore = { }; }
 
     void setHasValidTop() { m_hasValidTop = true; }
     void setHasValidLeft() { m_hasValidLeft = true; }
@@ -227,7 +232,7 @@ private:
     Layout::UsedHorizontalMargin m_horizontalMargin;
     Layout::UsedVerticalMargin m_verticalMargin;
     Layout::ComputedHorizontalMargin m_horizontalComputedMargin;
-    Optional<LayoutUnit> m_estimatedMarginBefore;
+    bool m_hasClearance;
 
     Layout::Edges m_border;
     Optional<Layout::Edges> m_padding;
@@ -243,6 +248,7 @@ private:
     bool m_hasValidPadding { false };
     bool m_hasValidContentHeight { false };
     bool m_hasValidContentWidth { false };
+    Optional<Layout::EstimatedMarginBefore> m_estimatedMarginBefore;
 #endif
 };
 
@@ -520,8 +526,8 @@ inline void Box::setVerticalMargin(Layout::UsedVerticalMargin margin)
 #if !ASSERT_DISABLED
     setHasValidVerticalMargin();
     setHasValidVerticalNonCollapsedMargin();
+    invalidateEstimatedMarginBefore();
 #endif
-    ASSERT(!m_estimatedMarginBefore || *m_estimatedMarginBefore == margin.before());
     m_verticalMargin = margin;
 }
 
@@ -549,6 +555,14 @@ inline void Box::setPadding(Optional<Layout::Edges> padding)
     m_padding = padding;
 }
 
+inline Box::Rect Box::rectWithMargin() const
+{
+    auto marginAfter = this->marginAfter();
+    if (m_verticalMargin.collapsedValues().isCollapsedThrough)
+        marginAfter = 0;
+    return { top() - marginBefore(), left() - marginStart(), marginStart() + width() + marginEnd(), marginBefore() + height() + marginAfter };
+}
+
 inline Layout::UsedVerticalMargin Box::verticalMargin() const
 {
     ASSERT(m_hasValidVerticalMargin);
index 71d0539..4e9f2cc 100644 (file)
@@ -183,34 +183,32 @@ Optional<Position> FloatingContext::verticalPositionWithClearance(const Box& lay
         if (clearance <= 0)
             return { };
 
+        displayBox.setHasClearance();
         // Clearance inhibits margin collapsing. Let's reset the relevant adjoining margins.
         if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) {
             auto& previousInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*previousInFlowSibling);
+            // Does this box with clearance actually collapse its margin before with the previous inflow box's margin after? 
+            auto verticalMargin = displayBox.verticalMargin();
+            if (verticalMargin.hasCollapsedValues() && verticalMargin.collapsedValues().before) {
+                // Reset previous bottom after and current margin before to non-collapsing.
+                auto previousVerticalMargin = previousInFlowDisplayBox.verticalMargin();
+                ASSERT(previousVerticalMargin.hasCollapsedValues() && previousVerticalMargin.collapsedValues().after);
 
-            // Since the previous inflow sibling has already been laid out, its margin is collapsed by now.
-            ASSERT(!previousInFlowDisplayBox.marginAfter());
-            auto collapsedMargin = displayBox.marginBefore();
-
-            // Reset previous bottom and current top margins to non-collapsing.
-            auto previousVerticalMargin = previousInFlowDisplayBox.verticalMargin();
-            if (previousVerticalMargin.collapsedValues().after) {
+                auto collapsedMargin = *verticalMargin.collapsedValues().before;
                 previousVerticalMargin.setCollapsedValues({ previousVerticalMargin.collapsedValues().before, { } });
                 previousInFlowDisplayBox.setVerticalMargin(previousVerticalMargin);
-            }
-            // FIXME: check if collapsing through has anything to do with this.
-            auto verticalMargin = displayBox.verticalMargin();
-            if (verticalMargin.collapsedValues().before) {
+
                 verticalMargin.setCollapsedValues({ { }, verticalMargin.collapsedValues().after });
                 displayBox.setVerticalMargin(verticalMargin);
-            }
 
-            auto nonCollapsedMargin = previousInFlowDisplayBox.marginAfter() + displayBox.marginBefore();
-            auto marginOffset = nonCollapsedMargin - collapsedMargin;
-            // Move the box to the position where it would be with non-collapsed margins.
-            rootRelativeTop += marginOffset;
+                auto nonCollapsedMargin = previousVerticalMargin.after() + verticalMargin.before();
+                auto marginDifference = nonCollapsedMargin - collapsedMargin;
+                // Move the box to the position where it would be with non-collapsed margins.
+                rootRelativeTop += marginDifference;
 
-            // Having negative clearance is also normal. It just means that the box with the non-collapsed margins is now lower than it needs to be.
-            clearance -= marginOffset;
+                // Having negative clearance is also normal. It just means that the box with the non-collapsed margins is now lower than it needs to be.
+                clearance -= marginDifference;
+            }
         }
         // Now adjust the box's position with the clearance.
         rootRelativeTop += clearance;
index 0c3f5c8..e7e72b7 100644 (file)
@@ -157,7 +157,6 @@ void InlineFormattingContext::splitInlineRunIfNeeded(const InlineRun& inlineRun,
         splitRuns.append(run);
 
         contentStart += runWidth + uncommitted->lastInlineItem->nonBreakableEnd();
-        remaningLength -= uncommitted->length;
 
         startPosition = 0;
         uncommitted = { };
@@ -183,7 +182,9 @@ void InlineFormattingContext::splitInlineRunIfNeeded(const InlineRun& inlineRun,
         // #1
         if (detachingRules.containsAll({ InlineItem::DetachingRule::BreakAtStart, InlineItem::DetachingRule::BreakAtEnd })) {
             commit();
-            uncommitted = Uncommitted { &inlineItem, &inlineItem, currentLength() };
+            auto contentLength = currentLength();
+            uncommitted = Uncommitted { &inlineItem, &inlineItem, contentLength };
+            remaningLength -= contentLength;
             commit();
             continue;
         }
@@ -194,10 +195,12 @@ void InlineFormattingContext::splitInlineRunIfNeeded(const InlineRun& inlineRun,
 
         // Add current inline item to uncommitted.
         // #3 and #4
+        auto contentLength = currentLength();
         if (!uncommitted)
             uncommitted = Uncommitted { &inlineItem, &inlineItem, 0 };
-        uncommitted->length += currentLength();
+        uncommitted->length += contentLength;
         uncommitted->lastInlineItem = &inlineItem;
+        remaningLength -= contentLength;
 
         // #3
         if (detachingRules.contains(InlineItem::DetachingRule::BreakAtEnd))
@@ -441,11 +444,12 @@ void InlineFormattingContext::collectInlineContentForSubtree(const Box& root, In
     if (root.establishesFormattingContext() && &root != &(this->root())) {
         createAndAppendInlineItem();
         auto& inlineRun = *inlineFormattingState.inlineContent().last();
+        auto computedHorizontalMargin = Geometry::computedHorizontalMargin(layoutState(), root);
+        auto horizontalMargin = UsedHorizontalMargin { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
 
-        auto horizontalMargin = Geometry::computedHorizontalMargin(layoutState(), root);
         inlineRun.addDetachingRule({ InlineItem::DetachingRule::BreakAtStart, InlineItem::DetachingRule::BreakAtEnd });
-        inlineRun.addNonBreakableStart(horizontalMargin.start.valueOr(0));
-        inlineRun.addNonBreakableEnd(horizontalMargin.end.valueOr(0));
+        inlineRun.addNonBreakableStart(horizontalMargin.start);
+        inlineRun.addNonBreakableEnd(horizontalMargin.end);
         // Skip formatting root subtree. They are not part of this inline formatting context.
         return;
     }
@@ -465,7 +469,9 @@ void InlineFormattingContext::collectInlineContentForSubtree(const Box& root, In
     // FIXME: Revisit this when we figured out how inline boxes fit the display tree.
     auto padding = Geometry::computedPadding(layoutState(), root);
     auto border = Geometry::computedBorder(layoutState(), root);
-    auto horizontalMargin = Geometry::computedHorizontalMargin(layoutState(), root);
+    auto computedHorizontalMargin = Geometry::computedHorizontalMargin(layoutState(), root);
+    auto horizontalMargin = UsedHorizontalMargin { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
+
     // Setup breaking boundaries for this subtree.
     auto* lastDescendantInlineBox = inlineFormattingState.lastInlineItem();
     // Empty container?
@@ -496,7 +502,7 @@ void InlineFormattingContext::collectInlineContentForSubtree(const Box& root, In
 
         ASSERT(firstDescendantInlineBox);
         firstDescendantInlineBox->addDetachingRule(InlineItem::DetachingRule::BreakAtStart);
-        auto startOffset = border.horizontal.left + horizontalMargin.start.valueOr(0);
+        auto startOffset = border.horizontal.left + horizontalMargin.start;
         if (padding)
             startOffset += padding->horizontal.left;
         firstDescendantInlineBox->addNonBreakableStart(startOffset);
@@ -504,7 +510,7 @@ void InlineFormattingContext::collectInlineContentForSubtree(const Box& root, In
 
     if (rootBreaksAtEnd()) {
         lastDescendantInlineBox->addDetachingRule(InlineItem::DetachingRule::BreakAtEnd);
-        auto endOffset = border.horizontal.right + horizontalMargin.end.valueOr(0);
+        auto endOffset = border.horizontal.right + horizontalMargin.end;
         if (padding)
             endOffset += padding->horizontal.right;
         lastDescendantInlineBox->addNonBreakableEnd(endOffset);
index 993b3b7..f49bfa1 100644 (file)
@@ -1,3 +1,12 @@
+2019-01-09  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][BFC][MarginCollapsing] Add support for peculiar cases.
+        https://bugs.webkit.org/show_bug.cgi?id=192625
+
+        Reviewed by Antti Koivisto.
+
+        * LayoutReloaded/misc/LFC-passing-tests.txt:
+
 2019-01-09  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         [WPE][GTK] Purge use of g_assert() under TestWebKitAPI
index ff5eb38..225b747 100644 (file)
@@ -4,23 +4,66 @@ fast/block/block-only/absolute-left-auto.html
 fast/block/block-only/absolute-left-right-top-bottom-auto.html
 fast/block/block-only/absolute-nested2.html
 fast/block/block-only/absolute-nested.html
+fast/block/block-only/absolute-position-min-max-height.html
+fast/block/block-only/absolute-position-min-max-width.html
+fast/block/block-only/absolute-position-when-containing-block-is-not-in-the-formatting-context2.html
 fast/block/block-only/absolute-position-when-containing-block-is-not-in-the-formatting-context.html
 fast/block/block-only/absolute-simple.html
 fast/block/block-only/absolute-width-shrink-to-fit.html
 fast/block/block-only/absolute-width-stretch.html
 fast/block/block-only/absolute-with-static-block-position-nested.html
+fast/block/block-only/almost-intruding-left-float-simple.html
+fast/block/block-only/body-height-with-auto-html-height-quirk2.html
+fast/block/block-only/body-height-with-auto-html-height-quirk.html
+fast/block/block-only/body-height-with-non-auto-html-height-quirk2.html
+fast/block/block-only/body-height-with-non-auto-html-height-quirk.html
 fast/block/block-only/border-simple.html
+fast/block/block-only/collapsed-margin-with-min-height.html
 fast/block/block-only/fixed-nested.html
+fast/block/block-only/float-and-siblings-with-margins.html
+fast/block/block-only/float-avoider-multiple-roots.html
+fast/block/block-only/float-avoider-simple-left.html
+fast/block/block-only/float-avoider-simple-right.html
+fast/block/block-only/float-avoider-with-margins.html
+fast/block/block-only/float-left-when-container-has-padding-margin.html
+fast/block/block-only/float-min-max-height.html
+fast/block/block-only/float-min-max-width.html
+fast/block/block-only/floating-and-next-previous-inflow-with-margin-with-no-border.html
+fast/block/block-only/floating-and-next-previous-inflow-with-margin.html
+fast/block/block-only/floating-box-clear-both-simple.html
+fast/block/block-only/floating-box-clear-right-simple.html
+fast/block/block-only/floating-box-left-and-right-multiple-with-top-offset.html
+fast/block/block-only/floating-box-left-and-right-multiple.html
+fast/block/block-only/floating-box-right-simple.html
+fast/block/block-only/floating-box-with-clear-siblings.html
+fast/block/block-only/floating-box-with-clear-simple.html
+fast/block/block-only/floating-box-with-new-formatting-context.html
+fast/block/block-only/floating-box-with-relative-positioned-sibling.html
+fast/block/block-only/floating-left-and-right-with-clearance.html
+fast/block/block-only/floating-left-right-simple.html
+fast/block/block-only/floating-left-right-with-all-margins.html
+fast/block/block-only/floating-lefts-and-rights-simple.html
+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-with-new-block-formatting-context.html
+fast/block/block-only/inflow-min-max-height.html
+fast/block/block-only/inflow-min-max-width.html
 fast/block/block-only/margin-collapse-bottom-bottom.html
 fast/block/block-only/margin-collapse-bottom-nested.html
+fast/block/block-only/margin-collapse-first-last-are-floating.html
 fast/block/block-only/margin-collapse-simple.html
 fast/block/block-only/margin-collapse-top-nested.html
 fast/block/block-only/margin-collapse-when-child-has-padding-border.html
+fast/block/block-only/margin-collapse-with-block-formatting-context2.html
+fast/block/block-only/margin-collapse-with-block-formatting-context.html
+fast/block/block-only/margin-collapse-with-clearance.html
 fast/block/block-only/margin-left-right-sizing-out-of-flow.html
 fast/block/block-only/margin-left-right-sizing.html
 fast/block/block-only/margin-propagation-simple-content-height.html
 fast/block/block-only/margin-sibling-collapse-propagated.html
 fast/block/block-only/margin-simple.html
+fast/block/block-only/min-max-height-percentage.html
 fast/block/block-only/negative-margin-simple.html
 fast/block/block-only/padding-nested.html
 fast/block/block-only/padding-simple.html
@@ -31,80 +74,49 @@ fast/block/block-only/relative-position-when-containing-block-is-not-in-the-form
 fast/block/block-only/relative-right.html
 fast/block/block-only/relative-siblings.html
 fast/block/block-only/relative-simple.html
-fast/block/block-only/absolute-position-when-containing-block-is-not-in-the-formatting-context2.html
-fast/block/block-only/margin-collapse-with-block-formatting-context.html
-fast/block/block-only/margin-collapse-with-block-formatting-context2.html
-fast/block/block-only/float-left-when-container-has-padding-margin.html
-fast/block/block-only/floating-box-left-and-right-multiple-with-top-offset.html
-fast/block/block-only/floating-box-left-and-right-multiple.html
-fast/block/block-only/floating-box-right-simple.html
-fast/block/block-only/floating-box-with-new-formatting-context.html
-fast/block/block-only/floating-box-with-relative-positioned-sibling.html
-fast/block/block-only/floating-left-right-simple.html
-fast/block/block-only/floating-left-right-with-all-margins.html
-fast/block/block-only/floating-lefts-and-rights-simple.html
-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-and-next-previous-inflow-with-margin-with-no-border.html
-fast/block/block-only/floating-left-and-right-with-clearance.html
-fast/block/block-only/float-and-siblings-with-margins.html
-fast/block/block-only/margin-collapse-with-clearance.html
-fast/block/block-only/float-avoider-simple-left.html
-fast/block/block-only/float-avoider-simple-right.html
-fast/block/block-only/float-avoider-multiple-roots.html
-fast/block/block-only/float-avoider-with-margins.html
-fast/block/block-only/inflow-min-max-width.html
-fast/block/block-only/absolute-position-min-max-width.html
-fast/block/block-only/float-min-max-width.html
-fast/block/block-only/inflow-min-max-height.html
-fast/block/block-only/absolute-position-min-max-height.html
-fast/block/block-only/float-min-max-height.html
-fast/block/block-only/min-max-height-percentage.html
-fast/block/block-only/body-height-with-non-auto-html-height-quirk.html
-fast/block/block-only/body-height-with-non-auto-html-height-quirk2.html
-fast/block/block-only/body-height-with-auto-html-height-quirk.html
-fast/block/block-only/body-height-with-auto-html-height-quirk2.html
-fast/block/block-only/collapsed-margin-with-min-height.html
+fast/block/basic/002.html
+fast/block/basic/003.html
+fast/block/basic/006.html
+fast/block/basic/007.html
+fast/block/basic/008.html
+fast/block/basic/009.html
+fast/block/basic/012.html
+fast/block/basic/child-block-level-box-with-height-percent.html
+fast/block/basic/height-percentage-simple.html
 fast/block/basic/inline-content-with-floating-image.html
 fast/block/basic/inline-content-with-floating-images2.html
-fast/inline/simple-intruding-float1.html
-fast/inline/simple-intruding-floats2.html
-fast/inline/simple-intruding-floats3.html
-fast/inline/simple-inline-block.html
-fast/inline/simple-shrink-to-fit-inline-block.html
-fast/inline/simple-inline-inflow-positioned.html
-fast/inline/simple-inline-with-out-of-flow-descendant.html
-fast/inline/simple-inline-with-out-of-flow-descendant2.html
-fast/inline/inline-content-with-padding-left-right.html
-fast/inline/inline-content-with-border-left-right.html
-fast/inline/inline-content-with-margin-left-right.html
-fast/inline/inline-content-and-nested-formatting-root-with-margin-left-right.html
-fast/inline/inline-content-with-image-simple.html
-fast/inline/inline-content-with-float-and-margin.html
-fast/block/basic/height-percentage-simple.html
-fast/block/basic/child-block-level-box-with-height-percent.html
+fast/block/basic/percent-height-inside-anonymous-block.html
 fast/block/basic/quirk-mode-percent-height.html
+fast/block/basic/quirk-percent-height-grandchild.html
 fast/block/float/001.html
 fast/block/float/007.html
 fast/block/float/008.html
 fast/block/float/009.html
 fast/block/float/013.html
 fast/block/float/019.html
-fast/block/basic/002.html
-fast/block/basic/003.html
-fast/block/basic/006.html
-fast/block/basic/007.html
-fast/block/basic/008.html
-fast/block/basic/009.html
-fast/block/basic/012.html
+fast/block/float/negative-margin-clear.html
+fast/block/float/overhanging-after-height-decrease-offsets.html
+fast/block/float/overhanging-after-height-decrease.html
+fast/block/float/overlapping-floats-with-overflow-hidden.html
+fast/block/float/previous-sibling-abspos-002.html
+fast/block/float/relative-painted-twice.html
+fast/block/float/avoidance-rtl.html
+fast/block/float/br-with-clear.html
+fast/block/float/clamped-right-float.html
+fast/block/float/float-on-zero-height-line.html
+fast/block/float/float-overhangs-root.html
+fast/block/float/float-with-anonymous-previous-sibling.html
+fast/block/float/floats-not-cleared-crash.html
 fast/block/margin-collapse/002.html
 fast/block/margin-collapse/003.html
 fast/block/margin-collapse/026.html
 fast/block/margin-collapse/027.html
+fast/block/margin-collapse/028.html
+fast/block/margin-collapse/029.html
+fast/block/margin-collapse/035.html
 fast/block/margin-collapse/039.html
 fast/block/margin-collapse/040.html
+fast/block/positioning/003.html
 fast/block/positioning/004.html
 fast/block/positioning/005.html
 fast/block/positioning/006.html
@@ -145,8 +157,29 @@ fast/block/positioning/046.html
 fast/block/positioning/049.html
 fast/block/positioning/052.html
 fast/block/positioning/054.html
+fast/block/positioning/060.html
+fast/block/positioning/absolute-positioning-no-scrollbar.html
+fast/block/positioning/change-containing-block-for-absolute-positioned.html
+fast/block/positioning/change-containing-block-for-fixed-positioned.html
+fast/block/positioning/complex-positioned-movement.html
+fast/block/positioning/fixed-position-detached-frame.html
+fast/block/positioning/fixed-position-stacking-context2.html
+fast/block/positioning/fixed-position-stacking-context.html
+fast/block/positioning/hiding-inside-relpositioned-inline.html
+fast/block/positioning/insert-positioned-in-anonymous-crash.html
+fast/block/positioning/leftmargin-topmargin.html
+fast/block/positioning/negative-rel-position.html
+fast/block/positioning/abs-inside-inline-rel.html
+fast/block/positioning/pref-width-change.html
+fast/block/positioning/relative-overflow-replaced-float.html
+fast/block/positioning/static-inline-position-dynamic.html
 fast/block/inside-inlines/basic-float-intrusion.html
 fast/block/inside-inlines/crash-on-first-line-change.html
+fast/block/lineboxcontain/replaced.html
+fast/block/collapse-anon-block-with-float-siblings-only.html
+fast/block/crash-when-anonymous-blocks-are-merged-with-simple-line-layout.html
+fast/block/crash-when-subtree-is-still-attached.html
+fast/block/geometry-map-assertion-with-tall-content.html
 fast/borders/0px-borders-no-line-height.html
 fast/borders/0px-borders.html
 fast/borders/block-mask-overlay-image-outset.html
@@ -156,11 +189,8 @@ fast/borders/border-image-scaled-gradient.html
 fast/borders/border-image-slice-omission.html
 fast/borders/border-left-right-same-bottom-different-color.html
 fast/borders/border-painting-dashed-at-all.html
-fast/borders/border-painting-dashed.html
 fast/borders/border-painting-dotted-at-all.html
-fast/borders/border-painting-dotted.html
 fast/borders/border-painting-double-at-all.html
-fast/borders/border-painting-double.html
 fast/borders/border-painting-groove-at-all.html
 fast/borders/border-painting-inset-at-all.html
 fast/borders/border-painting-inset.html
@@ -169,4 +199,86 @@ fast/borders/border-painting-outset.html
 fast/borders/border-painting-ridge-at-all.html
 fast/borders/border-painting-solid-at-all.html
 fast/borders/border-painting-solid.html
-fast/inline-block/004.html
+fast/borders/border-radius-inset-outset.html
+fast/borders/border-radius-on-html.html
+fast/borders/border-radius-on-subpixel-position-non-hidpi.html
+fast/borders/border-radius-percent.html
+fast/borders/border-radius-valid-border-clipping.html
+fast/borders/border-radius-wide-border-01.html
+fast/borders/border-radius-with-box-shadow-01.html
+fast/borders/border-radius-with-box-shadow.html
+fast/borders/border-shadow-large-radius.html
+fast/borders/borderRadiusAllStylesAllCorners.html
+fast/borders/borderRadiusArcs01.html
+fast/borders/borderRadiusDashed01.html
+fast/borders/borderRadiusDashed02.html
+fast/borders/borderRadiusDashed03.html
+fast/borders/borderRadiusDotted01.html
+fast/borders/borderRadiusDotted02.html
+fast/borders/borderRadiusDotted03.html
+fast/borders/borderRadiusDouble01.html
+fast/borders/borderRadiusDouble02.html
+fast/borders/borderRadiusDouble03.html
+fast/borders/borderRadiusGroove01.html
+fast/borders/borderRadiusGroove02.html
+fast/borders/borderRadiusInset01.html
+fast/borders/borderRadiusInvalidColor.html
+fast/borders/borderRadiusOutset01.html
+fast/borders/borderRadiusRidge01.html
+fast/borders/borderRadiusSolid01.html
+fast/borders/borderRadiusSolid02.html
+fast/borders/borderRadiusSolid03.html
+fast/borders/borderRadiusSolid04.html
+fast/borders/dashed-border-on-subpixel-position.html
+fast/borders/dotted-border-on-subpixel-position.html
+fast/borders/double-1px-border-assert.html
+fast/borders/empty-drawrect-assert-after-pixelsnap.html
+fast/borders/empty-outline-border-assert.html
+fast/borders/hidpi-3x-input-hairline-border.html
+fast/borders/hidpi-border-clipping-right-after-move.html
+fast/borders/hidpi-border-painting-groove.html
+fast/borders/hidpi-border-painting-ridge.html
+fast/borders/hidpi-border-radius-outer-border-goes-rectangle.html
+fast/borders/hidpi-border-radius-with-subpixel-margin-not-renderable.html
+fast/borders/hidpi-border-width-flooring.html
+fast/borders/hidpi-outline-hairline-painting.html
+fast/borders/hidpi-outline-on-subpixel-position.html
+fast/borders/hidpi-rounded-border-on-subpixel-position.html
+fast/borders/hidpi-simple-hairline-border-painting.html
+fast/borders/mixed-border-style2.html
+fast/borders/negative-border-width.html
+fast/borders/outline-offset-min-assert.html
+fast/borders/outline-offset-overflow.html
+fast/borders/webkit-border-radius.html
+fast/inline/absolute-positioned-block-in-centred-block.html
+fast/inline/absolute-positioned-inline-in-centred-block.html
+fast/inline/anonymous-block-with-empty-inline.html
+fast/inline/hidpi-outline-auto-negative-offset-with-border-radius.html
+fast/inline/hidpi-outline-auto-with-fractional-radius.html
+fast/inline/hidpi-outline-auto-with-one-focusring-rect.html
+fast/inline/hidpi-pixel-gap-between-adjacent-selection-inlines.html
+fast/inline/hidpi-select-inline-on-subpixel-position.html
+fast/inline/hidpi-selection-gap-on-subpixel-position.html
+fast/inline/hidpi-selection-gap-overlaps-inline-selection.html
+fast/inline/inline-body-with-scrollbar-crash.html
+fast/inline/inline-child-height-width-calc-crash.html
+fast/inline/inline-content-with-border-left-right.html
+fast/inline/inline-content-with-float-and-margin.html
+fast/inline/inline-content-with-image-simple.html
+fast/inline/inline-content-with-margin-left-right.html
+fast/inline/inline-content-with-padding-left-right.html
+fast/inline/inline-marquee-crash.html
+fast/inline/inline-padding-disables-text-quirk.html
+fast/inline/new-float-needs-layout-when-line-is-dirty.html
+fast/inline/percentage-margins.html
+fast/inline/simple-inline-block.html
+fast/inline/simple-inline-inflow-positioned.html
+fast/inline/simple-inline-with-out-of-flow-descendant2.html
+fast/inline/simple-inline-with-out-of-flow-descendant.html
+fast/inline/simple-intruding-float1.html
+fast/inline/simple-intruding-floats2.html
+fast/inline/simple-intruding-floats3.html
+fast/inline/simple-line-layout-16bit-content.html
+fast/inline/simple-shrink-to-fit-inline-block.html
+fast/inline/skipped-whitespace-boundingBox.html
+fast/inline/skipped-whitespace-client-rect.html