WebKit Bugzilla
Attachment 357130 Details for
Bug 192625
: [LFC][BFC][MarginCollapsing] Add support for peculiar cases.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
Patch.txt (text/plain), 79.47 KB, created by
zalan
on 2018-12-12 08:00:20 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
zalan
Created:
2018-12-12 08:00:20 PST
Size:
79.47 KB
patch
obsolete
>diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContext.h b/Source/WebCore/layout/blockformatting/BlockFormattingContext.h >index e677e0174f0ed58c162fb6b713de096b11f0fd30..40e7b21c532a5d8c911a8feb324b9250e64db8d9 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContext.h >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContext.h >@@ -89,8 +89,8 @@ private: > static LayoutUnit marginTop(const LayoutState&, const Box&); > static LayoutUnit marginBottom(const LayoutState&, const Box&); > >- static bool isMarginBottomCollapsedWithParent(const Box&); >- static bool isMarginTopCollapsedWithParentMarginBottom(const Box&); >+ static bool marginBottomCollapsesWithParent(const Box&); >+ static bool marginTopCollapsesWithParentMarginBottom(const Box&); > > private: > static LayoutUnit collapsedMarginBottomFromLastChild(const LayoutState&, const Box&); >@@ -102,7 +102,10 @@ private: > static LayoutUnit collapsedMarginTopFromFirstChild(const LayoutState&, const Box&); > static LayoutUnit nonCollapsedMarginTop(const LayoutState&, const Box&); > >- static bool isMarginTopCollapsedWithParent(const LayoutState&, const Box&); >+ static bool marginTopCollapsesWithParent(const LayoutState&, const Box&); >+ static bool marginTopCollapsesWithSibling(const Box&); >+ static bool marginBottomCollapsesWithSibling(const Box&); >+ static bool marginsCollapseThrough(const Box&); > }; > > static HeightAndMargin inFlowNonReplacedHeightAndMargin(const LayoutState&, const Box&, std::optional<LayoutUnit> usedHeight = { }); >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp b/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp >index 3b09f30cf1b69c837adbe3b8183802eca86408c4..815f33258aefce822b5a1982a4de6ced91f6055c 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp >@@ -84,14 +84,14 @@ HeightAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMarg > // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin... > auto* lastInFlowChild = downcast<Container>(layoutBox).lastInFlowChild(); > ASSERT(lastInFlowChild); >- if (!MarginCollapse::isMarginBottomCollapsedWithParent(*lastInFlowChild)) { >+ if (!MarginCollapse::marginBottomCollapsesWithParent(*lastInFlowChild)) { > auto& lastInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*lastInFlowChild); > return { lastInFlowDisplayBox.bottom() + lastInFlowDisplayBox.marginBottom() - borderAndPaddingTop, nonCollapsedMargin, collapsedMargin }; > } > > // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin > auto* inFlowChild = lastInFlowChild; >- while (inFlowChild && MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(*inFlowChild)) >+ while (inFlowChild && MarginCollapse::marginTopCollapsesWithParentMarginBottom(*inFlowChild)) > inFlowChild = inFlowChild->previousInFlowSibling(); > if (inFlowChild) { > auto& inFlowDisplayBox = layoutState.displayBoxForLayoutBox(*inFlowChild); >diff --git a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp >index bf1a08ccb659e5735937c76e822e87dba889e486..6f8020bce8ec83f54f6d331b71a9178cc531cd57 100644 >--- a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp >@@ -78,53 +78,73 @@ static bool establishesBlockFormattingContext(const Box& layoutBox) > return layoutBox.establishesBlockFormattingContext(); > } > >-static LayoutUnit marginValue(LayoutUnit currentMarginValue, LayoutUnit candidateMarginValue) >+static bool hasClearance(const Box&) > { >- 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; >+ // FIXME: Use computed clearance for float avoiding block boxes. >+ return false; > } > >-static bool isMarginTopCollapsedWithSibling(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginTopCollapsesWithSibling(const Box& layoutBox) > { > ASSERT(layoutBox.isBlockLevelBox()); > >- if (layoutBox.isFloatingPositioned()) >+ if (!layoutBox.previousInFlowSibling()) > return false; > >- if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned()) >- return true; >+ auto& previousInFlowSibling = *layoutBox.previousInFlowSibling(); > >- // Out of flow positioned. >- ASSERT(layoutBox.isOutOfFlowPositioned()); >- return layoutBox.style().top().isAuto(); >+ // 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(layoutBox)) >+ return false; >+ >+ return true; > } > >-static bool isMarginBottomCollapsedWithSibling(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginBottomCollapsesWithSibling(const Box& layoutBox) > { > ASSERT(layoutBox.isBlockLevelBox()); > >- if (layoutBox.isFloatingPositioned()) >+ if (!layoutBox.nextInFlowSibling()) > return false; > >- if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned()) >- return true; >+ auto& nextInFlowSibling = *layoutBox.nextInFlowSibling(); >+ >+ // Margins between a floated box and any other box do not collapse. >+ if (layoutBox.isFloatingPositioned() || nextInFlowSibling.isFloatingPositioned()) >+ return false; >+ >+ // Margins of absolutely positioned boxes do not collapse. >+ if ((layoutBox.isOutOfFlowPositioned() && !layoutBox.style().bottom().isAuto()) >+ || (nextInFlowSibling.isOutOfFlowPositioned() && !nextInFlowSibling.style().top().isAuto())) >+ return false; >+ >+ // Margins of inline-block boxes do not collapse. >+ if (layoutBox.isInlineBlockBox() || nextInFlowSibling.isInlineBlockBox()) >+ return false; > >- // Out of flow positioned. >- ASSERT(layoutBox.isOutOfFlowPositioned()); >- return layoutBox.style().bottom().isAuto(); >+ // 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(nextInFlowSibling)) >+ return false; >+ >+ return true; > } > >-bool BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithParent(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginTopCollapsesWithParent(const LayoutState& layoutState, const Box& layoutBox) > { > // The first inflow child could propagate its top margin to parent. > // https://www.w3.org/TR/CSS21/box.html#collapsing-margins >@@ -133,7 +153,16 @@ bool BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithP > > ASSERT(layoutBox.isBlockLevelBox()); > >- if (layoutBox.isFloatingOrOutOfFlowPositioned()) >+ // Margins between a floated box and any other box do not collapse. >+ if (layoutBox.isFloatingPositioned()) >+ return false; >+ >+ // Margins of absolutely positioned boxes do not collapse. >+ if (layoutBox.isOutOfFlowPositioned()) >+ return false; >+ >+ // Margins of inline-block boxes do not collapse. >+ if (layoutBox.isInlineBlockBox()) > return false; > > // Only the first inlflow child collapses with parent. >@@ -141,46 +170,126 @@ bool BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithP > return false; > > auto& parent = *layoutBox.parent(); >- // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children >+ // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children. > if (establishesBlockFormattingContext(parent)) > 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(parent)) > return false; > >+ // ...no top padding > if (hasPaddingBefore(parent)) > return false; > >+ // ...and the child has no clearance. >+ if (hasClearance(layoutBox)) >+ return false; >+ > if (BlockFormattingContext::Quirks::shouldIgnoreMarginTop(layoutState, layoutBox)) > return false; > > return true; > } > >-static bool isMarginBottomCollapsedThrough(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginBottomCollapsesWithParent(const Box& layoutBox) > { >+ // last inflow box to parent. >+ // https://www.w3.org/TR/CSS21/box.html#collapsing-margins >+ if (layoutBox.isAnonymous()) >+ return false; >+ > ASSERT(layoutBox.isBlockLevelBox()); > >- // If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it. >+ // Margins between a floated box and any other box do not collapse. >+ if (layoutBox.isFloatingPositioned()) >+ return false; >+ >+ // Margins of absolutely positioned boxes do not collapse. >+ if (layoutBox.isOutOfFlowPositioned()) >+ return false; >+ >+ // Margins of inline-block boxes do not collapse. >+ if (layoutBox.isInlineBlockBox()) >+ return false; >+ >+ // Only the last inlflow child collapses with parent. >+ if (layoutBox.nextInFlowSibling()) >+ return false; >+ >+ auto& parent = *layoutBox.parent(); >+ // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children. >+ if (establishesBlockFormattingContext(parent)) >+ 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 (!parent.style().height().isAuto()) >+ return false; >+ >+ // the box has no bottom padding, and >+ if (hasPaddingBefore(parent)) >+ return false; >+ >+ // the box has no bottom border, and >+ if (hasBorderBefore(parent)) >+ return false; >+ >+ // 1. If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins >+ // of following siblings but that resulting margin does not collapse with the bottom margin of the parent block. >+ >+ // 2. If the top margin of a box with non-zero computed 'min-height' and 'auto' computed 'height' collapses with the bottom >+ // margin of its last in-flow child, then the child's bottom margin does not collapse with the parent's bottom margin >+ >+ // FIXME: ^^ >+ >+ return true; >+} >+ >+bool BlockFormattingContext::Geometry::MarginCollapse::marginsCollapseThrough(const Box& layoutBox) >+{ >+ ASSERT(layoutBox.isBlockLevelBox()); >+ >+ // A box's own margins collapse if the 'min-height' property is zero, and it has neither top or bottom borders nor top or bottom padding, >+ // and it has a 'height' of either 0 or 'auto', and it does not contain a line box, and all of its in-flow children's margins (if any) collapse. > if (hasBorderBefore(layoutBox) || hasBorderAfter(layoutBox)) > return false; > > if (hasPaddingBefore(layoutBox) || hasPaddingAfter(layoutBox)) > return false; > >+ // FIXME: Check for computed 0 height. > if (!layoutBox.style().height().isAuto() || !layoutBox.style().minHeight().isAuto()) > return false; > > if (!is<Container>(layoutBox)) > return true; > >+ // FIXME: Check if the inflow block children have collapsed margins > auto& container = downcast<Container>(layoutBox); > if (container.hasInFlowOrFloatingChild()) > return false; > >+ // FIXME: Check if there's no line box (inline formatting context). > return true; > } > >+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::Geometry::MarginCollapse::collapsedMarginTopFromFirstChild(const LayoutState& layoutState, const Box& layoutBox) > { > ASSERT(layoutBox.isBlockLevelBox()); >@@ -195,7 +304,7 @@ LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::collapsedMarginTopF > > // FIXME: Take collapsed through margin into account. > auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild(); >- if (!isMarginTopCollapsedWithParent(layoutState, firstInFlowChild)) >+ if (!marginTopCollapsesWithParent(layoutState, firstInFlowChild)) > return 0; > // Collect collapsed margin top recursively. > return marginValue(computedNonCollapsedMarginTop(layoutState, firstInFlowChild), collapsedMarginTopFromFirstChild(layoutState, firstInFlowChild)); >@@ -209,20 +318,6 @@ LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::nonCollapsedMarginT > return marginValue(computedNonCollapsedMarginTop(layoutState, layoutBox), collapsedMarginTopFromFirstChild(layoutState, layoutBox)); > } > >-/*static bool hasAdjoiningMarginTopAndBottom(const Box&) >-{ >- // Two margins are adjoining if and only if: >- // 1. both belong to in-flow block-level boxes that participate in the same block formatting context >- // 2. no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.) >- // 3. both belong to vertically-adjacent box edges, i.e. form one of the following pairs: >- // top margin of a box and top margin of its first in-flow child >- // bottom margin of box and top margin of its next in-flow following sibling >- // bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height >- // top and bottom margins of a box that does not establish a new block formatting context and that has zero computed 'min-height', >- // zero or 'auto' computed 'height', and no in-flow children >- // A collapsed margin is considered adjoining to another margin if any of its component margins is adjoining to that margin. >- return false; >-}*/ > LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::computedNonCollapsedMarginTop(const LayoutState& layoutState, const Box& layoutBox) > { > ASSERT(layoutBox.isBlockLevelBox()); >@@ -245,15 +340,15 @@ LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::marginTop(const Lay > ASSERT(layoutBox.isBlockLevelBox()); > > // TODO: take _hasAdjoiningMarginTopAndBottom() into account. >- if (isMarginTopCollapsedWithParent(layoutState, layoutBox)) >+ if (marginTopCollapsesWithParent(layoutState, layoutBox)) > return 0; > > // FIXME: Find out the logic behind this. > if (BlockFormattingContext::Quirks::shouldIgnoreMarginTop(layoutState, layoutBox)) > return 0; > >- if (!isMarginTopCollapsedWithSibling(layoutBox)) { >- if (!isMarginBottomCollapsedThrough(layoutBox)) >+ if (!marginTopCollapsesWithSibling(layoutBox)) { >+ if (!marginsCollapseThrough(layoutBox)) > return nonCollapsedMarginTop(layoutState, layoutBox); > // Compute the collapsed through value. > auto marginTop = nonCollapsedMarginTop(layoutState, layoutBox); >@@ -280,14 +375,14 @@ LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::marginBottom(const > ASSERT(layoutBox.isBlockLevelBox()); > > // TODO: take _hasAdjoiningMarginTopAndBottom() into account. >- if (isMarginBottomCollapsedWithParent(layoutBox)) >+ if (marginBottomCollapsesWithParent(layoutBox)) > return 0; > >- if (isMarginBottomCollapsedThrough(layoutBox)) >+ if (marginsCollapseThrough(layoutBox)) > return 0; > > // Floats and out of flow positioned boxes do not collapse their margins. >- if (!isMarginBottomCollapsedWithSibling(layoutBox)) >+ if (!marginBottomCollapsesWithSibling(layoutBox)) > return nonCollapsedMarginBottom(layoutState, layoutBox); > > // The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling, >@@ -297,43 +392,7 @@ LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::marginBottom(const > return nonCollapsedMarginBottom(layoutState, layoutBox); > } > >-bool BlockFormattingContext::Geometry::MarginCollapse::isMarginBottomCollapsedWithParent(const Box& layoutBox) >-{ >- // last inflow box to parent. >- // https://www.w3.org/TR/CSS21/box.html#collapsing-margins >- if (layoutBox.isAnonymous()) >- return false; >- >- ASSERT(layoutBox.isBlockLevelBox()); >- >- if (layoutBox.isFloatingOrOutOfFlowPositioned()) >- return false; >- >- if (isMarginBottomCollapsedThrough(layoutBox)) >- return false; >- >- // Only the last inlflow child collapses with parent. >- if (layoutBox.nextInFlowSibling()) >- return false; >- >- auto& parent = *layoutBox.parent(); >- // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children >- if (establishesBlockFormattingContext(parent)) >- return false; >- >- if (hasBorderBefore(parent)) >- return false; >- >- if (hasPaddingBefore(parent)) >- return false; >- >- if (!parent.style().height().isAuto()) >- return false; >- >- return true; >-} >- >-bool BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(const Box&) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginTopCollapsesWithParentMarginBottom(const Box&) > { > return false; > } >@@ -352,7 +411,7 @@ LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::collapsedMarginBott > > // FIXME: Check for collapsed through margin. > auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild(); >- if (!isMarginBottomCollapsedWithParent(lastInFlowChild)) >+ if (!marginBottomCollapsesWithParent(lastInFlowChild)) > return 0; > > // Collect collapsed margin bottom recursively. >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index ee7eb659e63..3f540a538aa 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,72 @@ >+2018-12-12 Zalan Bujtas <zalan@apple.com> >+ >+ [LFC][BFC][MarginCollapsing] Add support for peculiar cases. >+ https://bugs.webkit.org/show_bug.cgi?id=192625 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * layout/FormattingContext.cpp: >+ (WebCore::Layout::FormattingContext::computeOutOfFlowVerticalGeometry const): >+ * layout/FormattingContext.h: >+ * layout/FormattingContextGeometry.cpp: >+ (WebCore::Layout::FormattingContext::Geometry::inlineReplacedHeightAndMargin): >+ (WebCore::Layout::FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue): >+ * layout/FormattingContextQuirks.cpp: >+ (WebCore::Layout::FormattingContext::Quirks::heightValueOfNearestContainingBlockWithFixedHeight): >+ * layout/LayoutUnits.h: >+ (WebCore::Layout::HeightAndMargin::usedMarginValues const): >+ * layout/blockformatting/BlockFormattingContext.cpp: >+ (WebCore::Layout::BlockFormattingContext::computeVerticalPositionForFloatClear const): >+ (WebCore::Layout::BlockFormattingContext::computeHeightAndMargin const): >+ * layout/blockformatting/BlockFormattingContext.h: >+ * layout/blockformatting/BlockFormattingContextGeometry.cpp: >+ (WebCore::Layout::BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMargin): >+ (WebCore::Layout::BlockFormattingContext::Geometry::inFlowHeightAndMargin): >+ (WebCore::Layout::BlockFormattingContext::Geometry::estimatedMarginTop): >+ (WebCore::Layout::BlockFormattingContext::Geometry::estimatedMarginBottom): >+ * layout/blockformatting/BlockFormattingContextQuirks.cpp: >+ (WebCore::Layout::BlockFormattingContext::Quirks::stretchedHeight): >+ (WebCore::Layout::BlockFormattingContext::Quirks::shouldIgnoreMarginBefore): >+ (WebCore::Layout::BlockFormattingContext::Quirks::shouldIgnoreMarginTop): Deleted. >+ * layout/blockformatting/BlockFormattingState.h: >+ (WebCore::Layout::BlockFormattingState::setPositiveAndNegativeMargins): >+ (WebCore::Layout::BlockFormattingState::positiveAndNegativeMargins const): >+ * layout/blockformatting/BlockMarginCollapse.cpp: >+ (WebCore::Layout::hasClearance): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginBeforeCollapsesWithPreviousSibling): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithNextSibling): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginBeforeCollapsesWithParent): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginBeforeCollapsesWithFirstInFlowChild): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithSiblingMarginBeforeWithClearance): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithParentMarginBefore): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithParent): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithLastInFlowChild): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginsCollapseThrough): >+ (WebCore::Layout::computedPositiveAndNegativeMargin): >+ (WebCore::Layout::marginValue): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::computedNonCollapsedMargins): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::updateCollapsedMarginAfter): >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::verticalMargins): >+ (WebCore::Layout::isMarginTopCollapsedWithSibling): Deleted. >+ (WebCore::Layout::isMarginBottomCollapsedWithSibling): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithParent): Deleted. >+ (WebCore::Layout::isMarginBottomCollapsedThrough): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::collapsedMarginTopFromFirstChild): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::nonCollapsedMarginTop): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::computedNonCollapsedMarginTop): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::computedNonCollapsedMarginBottom): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginTop): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::marginBottom): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::isMarginBottomCollapsedWithParent): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithParentMarginBottom): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::collapsedMarginBottomFromLastChild): Deleted. >+ (WebCore::Layout::BlockFormattingContext::Geometry::MarginCollapse::nonCollapsedMarginBottom): Deleted. >+ * layout/displaytree/DisplayBox.h: >+ (WebCore::Display::Box::setVerticalMargin): >+ (WebCore::Display::Box::setVerticalNonCollapsedMargin): >+ (WebCore::Display::Box::setHasClearance): >+ (WebCore::Display::Box::hasClearance const): >+ > 2018-12-10 Truitt Savell <tsavell@apple.com> > > Unreviewed, rolling out r238965. >diff --git a/Source/WebCore/layout/FormattingContext.cpp b/Source/WebCore/layout/FormattingContext.cpp >index d33d37e5cd7..7e973a9cb4b 100644 >--- a/Source/WebCore/layout/FormattingContext.cpp >+++ b/Source/WebCore/layout/FormattingContext.cpp >@@ -116,9 +116,10 @@ void FormattingContext::computeOutOfFlowVerticalGeometry(const Box& layoutBox) c > > auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox); > // Margins of absolutely positioned boxes do not collapse >- ASSERT(!verticalGeometry.heightAndMargin.collapsedMargin); >+ ASSERT(!verticalGeometry.heightAndMargin.collapsedMargin.before && !verticalGeometry.heightAndMargin.collapsedMargin.after); > auto nonCollapsedVerticalMargins = verticalGeometry.heightAndMargin.usedMarginValues(); >- displayBox.setTop(verticalGeometry.top + nonCollapsedVerticalMargins.top); >+ ASSERT(nonCollapsedVerticalMargins.before); >+ displayBox.setTop(verticalGeometry.top + *nonCollapsedVerticalMargins.before); > displayBox.setContentBoxHeight(verticalGeometry.heightAndMargin.height); > displayBox.setVerticalMargin(nonCollapsedVerticalMargins); > displayBox.setVerticalNonCollapsedMargin(nonCollapsedVerticalMargins); >diff --git a/Source/WebCore/layout/FormattingContext.h b/Source/WebCore/layout/FormattingContext.h >index 4f65e56e918..7729f44315a 100644 >--- a/Source/WebCore/layout/FormattingContext.h >+++ b/Source/WebCore/layout/FormattingContext.h >@@ -97,7 +97,7 @@ protected: > static std::optional<Edges> computedPadding(const LayoutState&, const Box&); > > static HorizontalEdges computedNonCollapsedHorizontalMarginValue(const LayoutState&, const Box&); >- static VerticalEdges computedNonCollapsedVerticalMarginValue(const LayoutState&, const Box&); >+ static MarginValues computedNonCollapsedVerticalMarginValue(const LayoutState&, const Box&); > > static std::optional<LayoutUnit> computedValueIfNotAuto(const Length& geometryProperty, LayoutUnit containingBlockWidth); > static std::optional<LayoutUnit> fixedValue(const Length& geometryProperty); >diff --git a/Source/WebCore/layout/FormattingContextGeometry.cpp b/Source/WebCore/layout/FormattingContextGeometry.cpp >index c99071ebb85..2f8f1e746aa 100644 >--- a/Source/WebCore/layout/FormattingContextGeometry.cpp >+++ b/Source/WebCore/layout/FormattingContextGeometry.cpp >@@ -837,7 +837,7 @@ HeightAndMargin FormattingContext::Geometry::inlineReplacedHeightAndMargin(const > > ASSERT(height); > >- LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow replaced -> height(" << *height << "px) margin(" << margin.top << "px, " << margin.bottom << "px) -> layoutBox(" << &layoutBox << ")"); >+ LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow replaced -> height(" << *height << "px) margin(" << *margin.before << "px, " << *margin.after << "px) -> layoutBox(" << &layoutBox << ")"); > return { *height, margin, { } }; > } > >@@ -1032,7 +1032,7 @@ HorizontalEdges FormattingContext::Geometry::computedNonCollapsedHorizontalMargi > return { marginLeft, marginRight }; > } > >-VerticalEdges FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(const LayoutState& layoutState, const Box& layoutBox) >+MarginValues FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(const LayoutState& layoutState, const Box& layoutBox) > { > auto& style = layoutBox.style(); > auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth(); >diff --git a/Source/WebCore/layout/FormattingContextQuirks.cpp b/Source/WebCore/layout/FormattingContextQuirks.cpp >index a3c5b6e8975..18bc23b0e4f 100644 >--- a/Source/WebCore/layout/FormattingContextQuirks.cpp >+++ b/Source/WebCore/layout/FormattingContextQuirks.cpp >@@ -54,7 +54,8 @@ LayoutUnit FormattingContext::Quirks::heightValueOfNearestContainingBlockWithFix > auto verticalMargins = FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(layoutState, *containingBlock); > auto verticalPaddings = displayBox.paddingTop().value_or(0) + displayBox.paddingBottom().value_or(0); > auto verticalBorders = displayBox.borderTop() + displayBox.borderBottom(); >- bodyAndDocumentVerticalMarginsPaddingsAndBorders += verticalMargins.top + verticalMargins.bottom + verticalPaddings + verticalBorders; >+ ASSERT(verticalMargins.before && verticalMargins.after); >+ bodyAndDocumentVerticalMarginsPaddingsAndBorders += *verticalMargins.before + *verticalMargins.after + verticalPaddings + verticalBorders; > } > > containingBlock = containingBlock->containingBlock(); >diff --git a/Source/WebCore/layout/LayoutUnits.h b/Source/WebCore/layout/LayoutUnits.h >index 3e1d8446f75..eea3e08f0b0 100644 >--- a/Source/WebCore/layout/LayoutUnits.h >+++ b/Source/WebCore/layout/LayoutUnits.h >@@ -95,6 +95,11 @@ struct VerticalEdges { > LayoutUnit bottom; > }; > >+struct MarginValues { >+ std::optional<LayoutUnit> before; >+ std::optional<LayoutUnit> after; >+}; >+ > struct Edges { > HorizontalEdges horizontal; > VerticalEdges vertical; >@@ -107,11 +112,11 @@ struct WidthAndMargin { > }; > > struct HeightAndMargin { >- VerticalEdges usedMarginValues() const { return collapsedMargin.value_or(nonCollapsedMargin); } >+ MarginValues usedMarginValues() const; > > LayoutUnit height; >- VerticalEdges nonCollapsedMargin; >- std::optional<VerticalEdges> collapsedMargin; >+ MarginValues nonCollapsedMargin; >+ MarginValues collapsedMargin; > }; > > struct HorizontalGeometry { >@@ -126,6 +131,12 @@ struct VerticalGeometry { > HeightAndMargin heightAndMargin; > }; > >+inline MarginValues HeightAndMargin::usedMarginValues() const >+{ >+ return { collapsedMargin.before ? collapsedMargin.before : nonCollapsedMargin.before, >+ collapsedMargin.after ? collapsedMargin.after : nonCollapsedMargin.after }; >+} >+ > } > } > #endif >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp b/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp >index a12d10f65c8..9ed26cdb7d5 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp >@@ -294,7 +294,9 @@ void BlockFormattingContext::computeVerticalPositionForFloatClear(const Floating > computeEstimatedMarginTopForAncestors(layoutBox); > ASSERT(hasPrecomputedMarginTop(layoutState, layoutBox)); > >- if (auto verticalPositionWithClearance = floatingContext.verticalPositionWithClearance(layoutBox)) >+ auto verticalPositionWithClearance = floatingContext.verticalPositionWithClearance(layoutBox); >+ layoutState.displayBoxForLayoutBox(layoutBox).setHasClearance(verticalPositionWithClearance.has_value()); >+ if (verticalPositionWithClearance) > layoutState.displayBoxForLayoutBox(layoutBox).setTop(*verticalPositionWithClearance); > } > >@@ -377,8 +379,10 @@ void BlockFormattingContext::computeHeightAndMargin(const Box& layoutBox) const > displayBox.setVerticalMargin(heightAndMargin.usedMarginValues()); > > // If this box has already been moved by the estimated vertical margin, no need to move it again. >- if (layoutBox.isFloatingPositioned() || !displayBox.estimatedMarginTop()) >- displayBox.moveVertically(heightAndMargin.usedMarginValues().top); >+ if (layoutBox.isFloatingPositioned() || !displayBox.estimatedMarginTop()) { >+ ASSERT(heightAndMargin.usedMarginValues().before); >+ displayBox.moveVertically(*heightAndMargin.usedMarginValues().before); >+ } > } > > FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::instrinsicWidthConstraints() const >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContext.h b/Source/WebCore/layout/blockformatting/BlockFormattingContext.h >index e677e0174f0..447cd23a319 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContext.h >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContext.h >@@ -86,23 +86,25 @@ private: > // This class implements margin collapsing for block formatting context. > class MarginCollapse { > public: >- static LayoutUnit marginTop(const LayoutState&, const Box&); >- static LayoutUnit marginBottom(const LayoutState&, const Box&); >- >- static bool isMarginBottomCollapsedWithParent(const Box&); >- static bool isMarginTopCollapsedWithParentMarginBottom(const Box&); >+ struct VerticalValues { >+ MarginValues computed; >+ MarginValues collapsed; >+ }; >+ static VerticalValues verticalMargins(const LayoutState&, const Box&); >+ static void updateCollapsedMarginAfter(const LayoutState&, const Box&); >+ >+ static bool marginBeforeCollapsesWithPreviousSibling(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithNextSibling(const LayoutState&, const Box&); >+ static bool marginBeforeCollapsesWithParent(const LayoutState&, const Box&); >+ static bool marginBeforeCollapsesWithFirstInFlowChild(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithParent(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithLastInFlowChild(const LayoutState&, const Box&); >+ static bool marginsCollapseThrough(const Box&); > > private: >- static LayoutUnit collapsedMarginBottomFromLastChild(const LayoutState&, const Box&); >- static LayoutUnit nonCollapsedMarginBottom(const LayoutState&, const Box&); >- >- static LayoutUnit computedNonCollapsedMarginTop(const LayoutState&, const Box&); >- static LayoutUnit computedNonCollapsedMarginBottom(const LayoutState&, const Box&); >- >- static LayoutUnit collapsedMarginTopFromFirstChild(const LayoutState&, const Box&); >- static LayoutUnit nonCollapsedMarginTop(const LayoutState&, const Box&); >- >- static bool isMarginTopCollapsedWithParent(const LayoutState&, const Box&); >+ static MarginValues computedNonCollapsedMargins(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithParentMarginBefore(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithSiblingMarginBeforeWithClearance(const LayoutState&, const Box&); > }; > > static HeightAndMargin inFlowNonReplacedHeightAndMargin(const LayoutState&, const Box&, std::optional<LayoutUnit> usedHeight = { }); >@@ -116,7 +118,7 @@ private: > static bool needsStretching(const LayoutState&, const Box&); > static HeightAndMargin stretchedHeight(const LayoutState&, const Box&, HeightAndMargin); > >- static bool shouldIgnoreMarginTop(const LayoutState&, const Box&); >+ static bool shouldIgnoreMarginBefore(const LayoutState&, const Box&); > }; > }; > >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp b/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp >index 3b09f30cf1b..48918baedae 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp >@@ -56,55 +56,56 @@ HeightAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMarg > // 4. zero, otherwise > // Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored, > // and relatively positioned boxes are considered without their offset). Note that the child box may be an anonymous block box. >- >- auto& style = layoutBox.style(); >- auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth(); > auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox); > >- VerticalEdges nonCollapsedMargin = { computedValueIfNotAuto(style.marginTop(), containingBlockWidth).value_or(0), >- computedValueIfNotAuto(style.marginBottom(), containingBlockWidth).value_or(0) }; >- VerticalEdges collapsedMargin = { MarginCollapse::marginTop(layoutState, layoutBox), MarginCollapse::marginBottom(layoutState, layoutBox) }; >+ auto verticalMargins = MarginCollapse::verticalMargins(layoutState, layoutBox); >+ MarginValues nonCollapsedMargins = { verticalMargins.computed.before.value_or(0), verticalMargins.computed.after.value_or(0) }; >+ MarginValues collapsedMargins = { verticalMargins.collapsed.before, verticalMargins.collapsed.after }; > auto borderAndPaddingTop = displayBox.borderTop() + displayBox.paddingTop().value_or(0); > > auto height = usedHeight ? usedHeight.value() : computedHeightValue(layoutState, layoutBox, HeightType::Normal); > if (height) >- return { height.value(), nonCollapsedMargin, collapsedMargin }; >+ return { height.value(), nonCollapsedMargins, collapsedMargins }; > > if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild()) >- return { 0, nonCollapsedMargin, collapsedMargin }; >+ return { 0, nonCollapsedMargins, collapsedMargins }; > > // 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines > if (layoutBox.establishesInlineFormattingContext()) { > // This is temp and will be replaced by the correct display box once inline runs move over to the display tree. > auto& inlineRuns = downcast<InlineFormattingState>(layoutState.establishedFormattingState(layoutBox)).inlineRuns(); > auto bottomEdge = inlineRuns.isEmpty() ? LayoutUnit() : inlineRuns.last().logicalBottom(); >- return { bottomEdge, nonCollapsedMargin, collapsedMargin }; >+ return { bottomEdge, nonCollapsedMargins, collapsedMargins }; > } > > // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin... > auto* lastInFlowChild = downcast<Container>(layoutBox).lastInFlowChild(); > ASSERT(lastInFlowChild); >- if (!MarginCollapse::isMarginBottomCollapsedWithParent(*lastInFlowChild)) { >+ if (!MarginCollapse::marginAfterCollapsesWithParent(layoutState, *lastInFlowChild)) { > auto& lastInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*lastInFlowChild); >- return { lastInFlowDisplayBox.bottom() + lastInFlowDisplayBox.marginBottom() - borderAndPaddingTop, nonCollapsedMargin, collapsedMargin }; >+ return { lastInFlowDisplayBox.bottom() + lastInFlowDisplayBox.marginBottom() - borderAndPaddingTop, nonCollapsedMargins, collapsedMargins }; > } > > // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin > auto* inFlowChild = lastInFlowChild; >- while (inFlowChild && MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(*inFlowChild)) >+ while (inFlowChild && MarginCollapse::marginBeforeCollapsesWithParent(layoutState, *inFlowChild)) > inFlowChild = inFlowChild->previousInFlowSibling(); > if (inFlowChild) { > auto& inFlowDisplayBox = layoutState.displayBoxForLayoutBox(*inFlowChild); >- return { inFlowDisplayBox.top() + inFlowDisplayBox.borderBox().height() - borderAndPaddingTop, nonCollapsedMargin, collapsedMargin }; >+ return { inFlowDisplayBox.top() + inFlowDisplayBox.borderBox().height() - borderAndPaddingTop, nonCollapsedMargins, collapsedMargins }; > } > > // 4. zero, otherwise >- return { 0, nonCollapsedMargin, collapsedMargin }; >+ return { 0, nonCollapsedMargins, collapsedMargins }; > }; > > auto heightAndMargin = compute(); > >- LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> height(" << heightAndMargin.height << "px) margin(" << heightAndMargin.usedMarginValues().top << "px, " << heightAndMargin.usedMarginValues().bottom << "px) -> layoutBox(" << &layoutBox << ")"); >+ // Adjust the previous sibling's margin bottom (and check for collapsing through margins as well. >+ if (MarginCollapse::marginBeforeCollapsesWithPreviousSibling(layoutState, layoutBox)) >+ MarginCollapse::updateCollapsedMarginAfter(layoutState, *layoutBox.previousInFlowSibling()); >+ >+ LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> height(" << heightAndMargin.height << "px) margin(" << *heightAndMargin.usedMarginValues().before << "px, " << *heightAndMargin.usedMarginValues().after << "px) -> layoutBox(" << &layoutBox << ")"); > return heightAndMargin; > } > >@@ -263,7 +264,7 @@ HeightAndMargin BlockFormattingContext::Geometry::inFlowHeightAndMargin(const La > > heightAndMargin = Quirks::stretchedHeight(layoutState, layoutBox, heightAndMargin); > >- LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> streched to viewport -> height(" << heightAndMargin.height << "px) margin(" << heightAndMargin.usedMarginValues().top << "px, " << heightAndMargin.usedMarginValues().bottom << "px) -> layoutBox(" << &layoutBox << ")"); >+ LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> streched to viewport -> height(" << heightAndMargin.height << "px) margin(" << *heightAndMargin.usedMarginValues().before << "px, " << *heightAndMargin.usedMarginValues().after << "px) -> layoutBox(" << &layoutBox << ")"); > return heightAndMargin; > } > >@@ -322,7 +323,7 @@ FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::Geometry:: > return { minimumIntrinsicWidth, maximumIntrinsicWidth }; > } > >-LayoutUnit BlockFormattingContext::Geometry::estimatedMarginTop(const LayoutState& layoutState, const Box& layoutBox) >+LayoutUnit BlockFormattingContext::Geometry::estimatedMarginTop(const 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). >@@ -331,10 +332,10 @@ LayoutUnit BlockFormattingContext::Geometry::estimatedMarginTop(const LayoutStat > ASSERT(!layoutBox.establishesBlockFormattingContext()); > > // Let's just use the normal path for now. >- return MarginCollapse::marginTop(layoutState, layoutBox); >+ return { }; //MarginCollapse::marginTop(layoutState, layoutBox); > } > >-LayoutUnit BlockFormattingContext::Geometry::estimatedMarginBottom(const LayoutState& layoutState, const Box& layoutBox) >+LayoutUnit BlockFormattingContext::Geometry::estimatedMarginBottom(const 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). >@@ -343,7 +344,7 @@ LayoutUnit BlockFormattingContext::Geometry::estimatedMarginBottom(const LayoutS > ASSERT(!layoutBox.establishesBlockFormattingContext()); > > // Let's just use the normal path for now. >- return MarginCollapse::marginBottom(layoutState, layoutBox); >+ return { }; //MarginCollapse::marginBottom(layoutState, layoutBox); > } > > } >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContextQuirks.cpp b/Source/WebCore/layout/blockformatting/BlockFormattingContextQuirks.cpp >index b8f018ac1d2..bfe0ae56f14 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContextQuirks.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContextQuirks.cpp >@@ -82,8 +82,9 @@ HeightAndMargin BlockFormattingContext::Quirks::stretchedHeight(const LayoutStat > if (layoutBox.isDocumentBox()) { > auto verticalMargins = heightAndMargin.usedMarginValues(); > // Document box's margins do not collapse. >- ASSERT(!heightAndMargin.collapsedMargin); >- totalVerticalMargins = verticalMargins.top + verticalMargins.bottom; >+ ASSERT(!heightAndMargin.collapsedMargin.before && !heightAndMargin.collapsedMargin.after); >+ ASSERT(verticalMargins.before && verticalMargins.after); >+ totalVerticalMargins = *verticalMargins.before + *verticalMargins.after; > } else if (layoutBox.isBodyBox()) { > // 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. >@@ -94,7 +95,8 @@ HeightAndMargin BlockFormattingContext::Quirks::stretchedHeight(const LayoutStat > // This quirk happens when the body height is 0 which means its vertical margins collapse through (top and bottom margins are adjoining). > // However now that we stretch the body they don't collapse through anymore, so we need to use the non-collapsed values instead. > auto bodyBoxVerticalMargins = heightAndMargin.height ? heightAndMargin.usedMarginValues() : heightAndMargin.nonCollapsedMargin; >- totalVerticalMargins = bodyBoxVerticalMargins.top + bodyBoxVerticalMargins.bottom; >+ ASSERT(bodyBoxVerticalMargins.before && bodyBoxVerticalMargins.after); >+ totalVerticalMargins = *bodyBoxVerticalMargins.before + *bodyBoxVerticalMargins.after; > } > > // Stretch but never overstretch with the margins. >@@ -104,7 +106,7 @@ HeightAndMargin BlockFormattingContext::Quirks::stretchedHeight(const LayoutStat > return heightAndMargin; > } > >-bool BlockFormattingContext::Quirks::shouldIgnoreMarginTop(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Quirks::shouldIgnoreMarginBefore(const LayoutState& layoutState, const Box& layoutBox) > { > if (!layoutBox.parent()) > return false; >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingState.h b/Source/WebCore/layout/blockformatting/BlockFormattingState.h >index bb9669390ff..26a8d98fe78 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingState.h >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingState.h >@@ -41,7 +41,21 @@ public: > BlockFormattingState(Ref<FloatingState>&&, LayoutState&); > virtual ~BlockFormattingState(); > >+ struct PositiveAndNegativeMargins { >+ struct Values { >+ std::optional<LayoutUnit> positive; >+ std::optional<LayoutUnit> negative; >+ }; >+ Values before; >+ Values after; >+ }; >+ void setPositiveAndNegativeMargins(const Box& layoutBox, PositiveAndNegativeMargins marginValues) { m_positiveAndNegativeMargins.set(&layoutBox, marginValues); } >+ PositiveAndNegativeMargins positiveAndNegativeMargins(const Box& layoutBox) const { return m_positiveAndNegativeMargins.get(&layoutBox); } >+ > std::unique_ptr<FormattingContext> createFormattingContext(const Box& formattingContextRoot) override; >+ >+private: >+ HashMap<const Box*, PositiveAndNegativeMargins> m_positiveAndNegativeMargins; > }; > > } >diff --git a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp >index bf1a08ccb65..a20de912988 100644 >--- a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp >@@ -78,53 +78,74 @@ static bool establishesBlockFormattingContext(const Box& layoutBox) > return layoutBox.establishesBlockFormattingContext(); > } > >-static LayoutUnit marginValue(LayoutUnit currentMarginValue, LayoutUnit candidateMarginValue) >+static bool hasClearance(const LayoutState& layoutState, const Box& layoutBox) > { >- 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; >+ if (!layoutBox.hasFloatClear()) >+ return false; >+ return layoutState.displayBoxForLayoutBox(layoutBox).hasClearance(); > } > >-static bool isMarginTopCollapsedWithSibling(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginBeforeCollapsesWithPreviousSibling(const LayoutState& layoutState, const Box& layoutBox) > { > ASSERT(layoutBox.isBlockLevelBox()); > >- if (layoutBox.isFloatingPositioned()) >+ if (!layoutBox.previousInFlowSibling()) > return false; > >- if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned()) >- return true; >+ 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; > >- // Out of flow positioned. >- ASSERT(layoutBox.isOutOfFlowPositioned()); >- return layoutBox.style().top().isAuto(); >+ // 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; > } > >-static bool isMarginBottomCollapsedWithSibling(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithNextSibling(const LayoutState& layoutState, const Box& layoutBox) > { > ASSERT(layoutBox.isBlockLevelBox()); > >- if (layoutBox.isFloatingPositioned()) >+ if (!layoutBox.nextInFlowSibling()) > return false; > >- if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned()) >- return true; >+ auto& nextInFlowSibling = *layoutBox.nextInFlowSibling(); >+ >+ // Margins between a floated box and any other box do not collapse. >+ if (layoutBox.isFloatingPositioned() || nextInFlowSibling.isFloatingPositioned()) >+ return false; >+ >+ // Margins of absolutely positioned boxes do not collapse. >+ if ((layoutBox.isOutOfFlowPositioned() && !layoutBox.style().bottom().isAuto()) >+ || (nextInFlowSibling.isOutOfFlowPositioned() && !nextInFlowSibling.style().top().isAuto())) >+ return false; >+ >+ // Margins of inline-block boxes do not collapse. >+ if (layoutBox.isInlineBlockBox() || nextInFlowSibling.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, nextInFlowSibling)) >+ return false; > >- // Out of flow positioned. >- ASSERT(layoutBox.isOutOfFlowPositioned()); >- return layoutBox.style().bottom().isAuto(); >+ return true; > } > >-bool BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithParent(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginBeforeCollapsesWithParent(const LayoutState& layoutState, const Box& layoutBox) > { > // The first inflow child could propagate its top margin to parent. > // https://www.w3.org/TR/CSS21/box.html#collapsing-margins >@@ -133,7 +154,16 @@ bool BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithP > > ASSERT(layoutBox.isBlockLevelBox()); > >- if (layoutBox.isFloatingOrOutOfFlowPositioned()) >+ // Margins between a floated box and any other box do not collapse. >+ if (layoutBox.isFloatingPositioned()) >+ return false; >+ >+ // Margins of absolutely positioned boxes do not collapse. >+ if (layoutBox.isOutOfFlowPositioned()) >+ return false; >+ >+ // Margins of inline-block boxes do not collapse. >+ if (layoutBox.isInlineBlockBox()) > return false; > > // Only the first inlflow child collapses with parent. >@@ -141,230 +171,301 @@ bool BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithP > return false; > > auto& parent = *layoutBox.parent(); >- // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children >+ // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children. > if (establishesBlockFormattingContext(parent)) > 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(parent)) > return false; > >+ // ...no top padding > if (hasPaddingBefore(parent)) > return false; > >- if (BlockFormattingContext::Quirks::shouldIgnoreMarginTop(layoutState, layoutBox)) >- return false; >- >- return true; >-} >- >-static bool isMarginBottomCollapsedThrough(const Box& layoutBox) >-{ >- ASSERT(layoutBox.isBlockLevelBox()); >- >- // If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it. >- if (hasBorderBefore(layoutBox) || hasBorderAfter(layoutBox)) >- return false; >- >- if (hasPaddingBefore(layoutBox) || hasPaddingAfter(layoutBox)) >+ // ...and the child has no clearance. >+ if (hasClearance(layoutState, layoutBox)) > return false; > >- if (!layoutBox.style().height().isAuto() || !layoutBox.style().minHeight().isAuto()) >- return false; >- >- if (!is<Container>(layoutBox)) >- return true; >- >- auto& container = downcast<Container>(layoutBox); >- if (container.hasInFlowOrFloatingChild()) >+ if (BlockFormattingContext::Quirks::shouldIgnoreMarginBefore(layoutState, layoutBox)) > return false; > > return true; > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::collapsedMarginTopFromFirstChild(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginBeforeCollapsesWithFirstInFlowChild(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; >+ return false; > >- // FIXME: Take collapsed through margin into account. > auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild(); >- if (!isMarginTopCollapsedWithParent(layoutState, firstInFlowChild)) >- return 0; >- // Collect collapsed margin top recursively. >- return marginValue(computedNonCollapsedMarginTop(layoutState, firstInFlowChild), collapsedMarginTopFromFirstChild(layoutState, firstInFlowChild)); >+ return marginBeforeCollapsesWithParent(layoutState, firstInFlowChild); > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::nonCollapsedMarginTop(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithSiblingMarginBeforeWithClearance(const LayoutState& layoutState, const Box& layoutBox) > { >- ASSERT(layoutBox.isBlockLevelBox()); >- >- // Non collapsed margin top includes collapsed margin from inflow first child. >- return marginValue(computedNonCollapsedMarginTop(layoutState, layoutBox), collapsedMarginTopFromFirstChild(layoutState, layoutBox)); >-} >+ // If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins >+ // of following siblings but that resulting margin does not collapse with the bottom margin of the parent block. >+ if (!marginsCollapseThrough(layoutBox)) >+ return false; > >-/*static bool hasAdjoiningMarginTopAndBottom(const Box&) >-{ >- // Two margins are adjoining if and only if: >- // 1. both belong to in-flow block-level boxes that participate in the same block formatting context >- // 2. no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.) >- // 3. both belong to vertically-adjacent box edges, i.e. form one of the following pairs: >- // top margin of a box and top margin of its first in-flow child >- // bottom margin of box and top margin of its next in-flow following sibling >- // bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height >- // top and bottom margins of a box that does not establish a new block formatting context and that has zero computed 'min-height', >- // zero or 'auto' computed 'height', and no in-flow children >- // A collapsed margin is considered adjoining to another margin if any of its component margins is adjoining to that margin. >+ for (auto* previousSibling = layoutBox.previousInFlowSibling(); previousSibling; previousSibling = previousSibling->previousInFlowSibling()) { >+ if (!marginsCollapseThrough(*previousSibling)) >+ return false; >+ if (hasClearance(layoutState, *previousSibling)) >+ return true; >+ } > return false; >-}*/ >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::computedNonCollapsedMarginTop(const LayoutState& layoutState, const Box& layoutBox) >-{ >- ASSERT(layoutBox.isBlockLevelBox()); >- >- return computedNonCollapsedVerticalMarginValue(layoutState, layoutBox).top; > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::computedNonCollapsedMarginBottom(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithParentMarginBefore(const LayoutState& layoutState, const Box& layoutBox) > { >- ASSERT(layoutBox.isBlockLevelBox()); >+ // If the top margin of a box with non-zero computed 'min-height' and 'auto' computed 'height' collapses with the bottom margin >+ // of its last in-flow child, then the child's bottom margin does not collapse with the parent's bottom margin. >+ if (!marginsCollapseThrough(layoutBox)) >+ return false; >+ >+ auto& parent = *layoutBox.parent(); >+ auto& firstInFlowChild = *parent.firstInFlowChild(); >+ if (&firstInFlowChild == &layoutBox) >+ return marginBeforeCollapsesWithParent(layoutState, layoutBox); >+ >+ auto* previousSibling = layoutBox.previousInFlowSibling(); >+ while (&firstInFlowChild != previousSibling) { >+ if (!marginsCollapseThrough(*previousSibling)) >+ return false; >+ previousSibling = previousSibling->previousInFlowSibling(); >+ } > >- return computedNonCollapsedVerticalMarginValue(layoutState, layoutBox).bottom; >+ return marginBeforeCollapsesWithParent(layoutState, *previousSibling); > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::marginTop(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithParent(const LayoutState& layoutState, const Box& layoutBox) > { >+ // last inflow box to parent. >+ // https://www.w3.org/TR/CSS21/box.html#collapsing-margins > if (layoutBox.isAnonymous()) >- return 0; >+ return false; > > ASSERT(layoutBox.isBlockLevelBox()); > >- // TODO: take _hasAdjoiningMarginTopAndBottom() into account. >- if (isMarginTopCollapsedWithParent(layoutState, layoutBox)) >- return 0; >- >- // FIXME: Find out the logic behind this. >- if (BlockFormattingContext::Quirks::shouldIgnoreMarginTop(layoutState, layoutBox)) >- return 0; >- >- if (!isMarginTopCollapsedWithSibling(layoutBox)) { >- if (!isMarginBottomCollapsedThrough(layoutBox)) >- return nonCollapsedMarginTop(layoutState, layoutBox); >- // Compute the collapsed through value. >- auto marginTop = nonCollapsedMarginTop(layoutState, layoutBox); >- auto marginBottom = nonCollapsedMarginBottom(layoutState, layoutBox); >- return marginValue(marginTop, marginBottom); >- } >+ // Margins between a floated box and any other box do not collapse. >+ if (layoutBox.isFloatingPositioned()) >+ 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. >- auto* previousInFlowSibling = layoutBox.previousInFlowSibling(); >- if (!previousInFlowSibling) >- return nonCollapsedMarginTop(layoutState, layoutBox); >+ // Margins of absolutely positioned boxes do not collapse. >+ if (layoutBox.isOutOfFlowPositioned()) >+ return false; > >- auto previousSiblingMarginBottom = nonCollapsedMarginBottom(layoutState, *previousInFlowSibling); >- auto marginTop = nonCollapsedMarginTop(layoutState, layoutBox); >- return marginValue(marginTop, previousSiblingMarginBottom); >-} >+ // Margins of inline-block boxes do not collapse. >+ if (layoutBox.isInlineBlockBox()) >+ return false; > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::marginBottom(const LayoutState& layoutState, const Box& layoutBox) >-{ >- if (layoutBox.isAnonymous()) >- return 0; >+ // Only the last inlflow child collapses with parent. >+ if (layoutBox.nextInFlowSibling()) >+ return false; > >- ASSERT(layoutBox.isBlockLevelBox()); >+ auto& parent = *layoutBox.parent(); >+ // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children. >+ if (establishesBlockFormattingContext(parent)) >+ return false; > >- // TODO: take _hasAdjoiningMarginTopAndBottom() into account. >- if (isMarginBottomCollapsedWithParent(layoutBox)) >- return 0; >+ // 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 (parent.style().height().isAuto()) { >+ // the box has no bottom padding, and >+ if (hasPaddingBefore(parent)) >+ return false; > >- if (isMarginBottomCollapsedThrough(layoutBox)) >- return 0; >+ // the box has no bottom border, and >+ if (hasBorderBefore(parent)) >+ return false; > >- // Floats and out of flow positioned boxes do not collapse their margins. >- if (!isMarginBottomCollapsedWithSibling(layoutBox)) >- return nonCollapsedMarginBottom(layoutState, layoutBox); >+ // the child's bottom margin neither collapses with a top margin that has clearance... >+ if (marginAfterCollapsesWithSiblingMarginBeforeWithClearance(layoutState, layoutBox)) >+ 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 (layoutBox.nextInFlowSibling()) >- return 0; >- return nonCollapsedMarginBottom(layoutState, layoutBox); >+ // nor (if the box's min-height is non-zero) with the box's top margin. >+ auto computedMinHeight = parent.style().logicalMinHeight(); >+ if (!computedMinHeight.isAuto() && computedMinHeight.value() && marginAfterCollapsesWithParentMarginBefore(layoutState, layoutBox)) >+ return false; >+ } >+ return true; > } > >-bool BlockFormattingContext::Geometry::MarginCollapse::isMarginBottomCollapsedWithParent(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::MarginCollapse::marginAfterCollapsesWithLastInFlowChild(const LayoutState& layoutState, const Box& layoutBox) > { >- // last inflow box to parent. >- // https://www.w3.org/TR/CSS21/box.html#collapsing-margins >- if (layoutBox.isAnonymous()) >+ if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild()) > return false; > >- ASSERT(layoutBox.isBlockLevelBox()); >- >- if (layoutBox.isFloatingOrOutOfFlowPositioned()) >- return false; >+ auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild(); >+ return marginAfterCollapsesWithParent(layoutState, lastInFlowChild); >+} > >- if (isMarginBottomCollapsedThrough(layoutBox)) >- return false; >+bool BlockFormattingContext::Geometry::MarginCollapse::marginsCollapseThrough(const Box& layoutBox) >+{ >+ ASSERT(layoutBox.isBlockLevelBox()); > >- // Only the last inlflow child collapses with parent. >- if (layoutBox.nextInFlowSibling()) >+ // A box's own margins collapse if the 'min-height' property is zero, and it has neither top or bottom borders nor top or bottom padding, >+ // and it has a 'height' of either 0 or 'auto', and it does not contain a line box, and all of its in-flow children's margins (if any) collapse. >+ if (hasBorderBefore(layoutBox) || hasBorderAfter(layoutBox)) > return false; > >- auto& parent = *layoutBox.parent(); >- // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children >- if (establishesBlockFormattingContext(parent)) >+ if (hasPaddingBefore(layoutBox) || hasPaddingAfter(layoutBox)) > return false; > >- if (hasBorderBefore(parent)) >+ // FIXME: Check for computed 0 height. >+ if (!layoutBox.style().height().isAuto() || !layoutBox.style().minHeight().isAuto()) > return false; > >- if (hasPaddingBefore(parent)) >- return false; >+ if (!is<Container>(layoutBox)) >+ return true; > >- if (!parent.style().height().isAuto()) >+ // FIXME: Check if the inflow block children have collapsed margins >+ auto& container = downcast<Container>(layoutBox); >+ if (container.hasInFlowOrFloatingChild()) > return false; > >+ // FIXME: Check if there's no line box (inline formatting context). Though it can't have a linebox unless it has a child. > return true; > } > >-bool BlockFormattingContext::Geometry::MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(const Box&) >+static BlockFormattingState::PositiveAndNegativeMargins::Values computedPositiveAndNegativeMargin(BlockFormattingState::PositiveAndNegativeMargins::Values a, BlockFormattingState::PositiveAndNegativeMargins::Values b) > { >- return false; >+ BlockFormattingState::PositiveAndNegativeMargins::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::max(*a.negative, *b.negative); >+ else >+ computedValues.negative = a.negative ? a.negative : b.negative; >+ >+ return computedValues; > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::collapsedMarginBottomFromLastChild(const LayoutState& layoutState, const Box& layoutBox) >+static BlockFormattingState::PositiveAndNegativeMargins::Values computedPositiveAndNegativeMargin(BlockFormattingState::PositiveAndNegativeMargins::Values a, std::optional<LayoutUnit> marginValue) > { >- ASSERT(layoutBox.isBlockLevelBox()); >+ if (!marginValue) >+ return a; >+ if (*marginValue > 0) >+ return computedPositiveAndNegativeMargin(a, { marginValue, { } }); >+ else if (*marginValue < 0) >+ return computedPositiveAndNegativeMargin(a, { { }, marginValue }); >+ return { }; >+} > >- // Check if the last child propagates its margin bottom. >- if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild()) >- return 0; >+static std::optional<LayoutUnit> marginValue(BlockFormattingState::PositiveAndNegativeMargins::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; > >- // 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; >+ if (!marginValues.positive) >+ return marginValues.negative; > >- // FIXME: Check for collapsed through margin. >- auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild(); >- if (!isMarginBottomCollapsedWithParent(lastInFlowChild)) >- return 0; >+ return *marginValues.positive + *marginValues.negative; >+} > >- // Collect collapsed margin bottom recursively. >- return marginValue(computedNonCollapsedMarginBottom(layoutState, lastInFlowChild), collapsedMarginBottomFromLastChild(layoutState, lastInFlowChild)); >+MarginValues BlockFormattingContext::Geometry::MarginCollapse::computedNonCollapsedMargins(const LayoutState& layoutState, const Box& layoutBox) >+{ >+ auto containingBlockContentBoxWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth(); >+ return { computedValueIfNotAuto(layoutBox.style().marginBefore(), containingBlockContentBoxWidth), >+ computedValueIfNotAuto(layoutBox.style().marginAfter(), containingBlockContentBoxWidth) }; >+} >+ >+void BlockFormattingContext::Geometry::MarginCollapse::updateCollapsedMarginAfter(const LayoutState& layoutState, const Box& layoutBox) >+{ >+ // 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 collpased margin after value as well as the positive/negative cache. >+ // 3. Check if the box's margins collapse through. >+ // 4. If so, update the collpased 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 (!marginAfterCollapsesWithNextSibling(layoutState, layoutBox)) >+ return; >+ >+ auto marginsCollapseThrough = MarginCollapse::marginsCollapseThrough(layoutBox); >+ auto& nextInFlowSibling = *layoutBox.nextInFlowSibling(); >+ auto nextSiblingMarginBefore = layoutState.displayBoxForLayoutBox(nextInFlowSibling).marginTop(); >+ >+ auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox); >+ displayBox.setVerticalMargin({ marginsCollapseThrough ? nextSiblingMarginBefore : displayBox.marginTop(), nextSiblingMarginBefore }); >+ >+ auto& blockFormattingState = downcast<BlockFormattingState>(layoutState.formattingStateForBox(layoutBox)); >+ auto positiveAndNegativeValues = blockFormattingState.positiveAndNegativeMargins(layoutBox); >+ auto nextSiblingPositiveAndNegativeValues = blockFormattingState.positiveAndNegativeMargins(nextInFlowSibling); >+ >+ positiveAndNegativeValues.after = computedPositiveAndNegativeMargin(nextSiblingPositiveAndNegativeValues.before, positiveAndNegativeValues.after); >+ if (marginsCollapseThrough) >+ positiveAndNegativeValues.before = computedPositiveAndNegativeMargin(positiveAndNegativeValues.before, positiveAndNegativeValues.before); >+ blockFormattingState.setPositiveAndNegativeMargins(layoutBox, positiveAndNegativeValues); >+ >+ if (!marginBeforeCollapsesWithPreviousSibling(layoutState, layoutBox)) >+ return; >+ >+ updateCollapsedMarginAfter(layoutState, *layoutBox.previousInFlowSibling()); > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::nonCollapsedMarginBottom(const LayoutState& layoutState, const Box& layoutBox) >+BlockFormattingContext::Geometry::MarginCollapse::VerticalValues BlockFormattingContext::Geometry::MarginCollapse::verticalMargins(const LayoutState& layoutState, const Box& layoutBox) > { >+ if (layoutBox.isAnonymous()) >+ return { }; >+ > 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& blockFormattingState = downcast<BlockFormattingState>(layoutState.formattingStateForBox(layoutBox)); >+ >+ // Margin before >+ BlockFormattingState::PositiveAndNegativeMargins::Values fistChildMinMaxMarginBefore; >+ if (marginBeforeCollapsesWithFirstInFlowChild(layoutState, layoutBox)) { >+ // By now the first child has to have some min/max value (even if they are just 0 computed values.) >+ auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild(); >+ ASSERT(&blockFormattingState == &downcast<BlockFormattingState>(layoutState.formattingStateForBox(firstInFlowChild))); >+ fistChildMinMaxMarginBefore = blockFormattingState.positiveAndNegativeMargins(firstInFlowChild).before; >+ } >+ >+ BlockFormattingState::PositiveAndNegativeMargins::Values previousSiblingMinMaxMarginAfter; >+ if (marginBeforeCollapsesWithPreviousSibling(layoutState, layoutBox)) { >+ auto& previousInFlowSibling = *layoutBox.previousInFlowSibling(); >+ ASSERT(&blockFormattingState == &downcast<BlockFormattingState>(layoutState.formattingStateForBox(previousInFlowSibling))); >+ previousSiblingMinMaxMarginAfter = blockFormattingState.positiveAndNegativeMargins(previousInFlowSibling).after; >+ } >+ >+ // Margin after >+ BlockFormattingState::PositiveAndNegativeMargins::Values lastChildMinMaxMarginAfter; >+ if (marginAfterCollapsesWithLastInFlowChild(layoutState, layoutBox)) { >+ // By now the last child has to have some min/max value (even if they are just 0 computed values.) >+ auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild(); >+ ASSERT(&blockFormattingState == &downcast<BlockFormattingState>(layoutState.formattingStateForBox(lastInFlowChild))); >+ lastChildMinMaxMarginAfter = blockFormattingState.positiveAndNegativeMargins(lastInFlowChild).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 when we compute the next sibling's margin before. >+ } >+ >+ auto minMaxMarginBefore = computedPositiveAndNegativeMargin(fistChildMinMaxMarginBefore, previousSiblingMinMaxMarginAfter); >+ auto nonCollapsedMargins = computedNonCollapsedMargins(layoutState, layoutBox); >+ >+ BlockFormattingState::PositiveAndNegativeMargins minMaxMarginValues { >+ computedPositiveAndNegativeMargin(minMaxMarginBefore, nonCollapsedMargins.before), >+ computedPositiveAndNegativeMargin(lastChildMinMaxMarginAfter, nonCollapsedMargins.after)}; >+ >+ if (marginsCollapseThrough(layoutBox)) { >+ // Before and after margins collapse. >+ minMaxMarginValues.before = computedPositiveAndNegativeMargin(minMaxMarginValues.before, minMaxMarginValues.after); >+ minMaxMarginValues.after = minMaxMarginValues.before; >+ } > >- // Non collapsed margin bottom includes collapsed margin from inflow last child. >- return marginValue(computedNonCollapsedMarginBottom(layoutState, layoutBox), collapsedMarginBottomFromLastChild(layoutState, layoutBox)); >+ blockFormattingState.setPositiveAndNegativeMargins(layoutBox, minMaxMarginValues); >+ return { { nonCollapsedMargins.before, marginValue(minMaxMarginValues.before) }, { nonCollapsedMargins.after, marginValue(minMaxMarginValues.after) } }; > } > > } >diff --git a/Source/WebCore/layout/displaytree/DisplayBox.h b/Source/WebCore/layout/displaytree/DisplayBox.h >index 62a3992cada..a939af12eb1 100644 >--- a/Source/WebCore/layout/displaytree/DisplayBox.h >+++ b/Source/WebCore/layout/displaytree/DisplayBox.h >@@ -174,6 +174,9 @@ public: > Rect paddingBox() const; > Rect contentBox() const; > >+ void setHasClearance(bool clearance); >+ bool hasClearance() const; >+ > private: > struct Style { > Style(const RenderStyle&); >@@ -191,8 +194,8 @@ private: > void setContentBoxWidth(LayoutUnit); > > void setHorizontalMargin(Layout::HorizontalEdges); >- void setVerticalMargin(Layout::VerticalEdges); >- void setVerticalNonCollapsedMargin(Layout::VerticalEdges); >+ void setVerticalMargin(Layout::MarginValues); >+ void setVerticalNonCollapsedMargin(Layout::MarginValues); > void setHorizontalNonComputedMargin(Layout::HorizontalEdges); > void setEstimatedMarginTop(LayoutUnit marginTop) { m_estimatedMarginTop = marginTop; } > >@@ -232,6 +235,9 @@ private: > Layout::Edges m_border; > std::optional<Layout::Edges> m_padding; > >+ // FIXME: This might need to go to the state instead. >+ bool m_hasClearance { false }; >+ > #if !ASSERT_DISABLED > bool m_hasValidTop { false }; > bool m_hasValidLeft { false }; >@@ -243,6 +249,7 @@ private: > bool m_hasValidPadding { false }; > bool m_hasValidContentHeight { false }; > bool m_hasValidContentWidth { false }; >+ bool m_hasValidClearance { false }; > #endif > }; > >@@ -515,21 +522,21 @@ inline void Box::setHorizontalMargin(Layout::HorizontalEdges margin) > m_margin.horizontal = margin; > } > >-inline void Box::setVerticalMargin(Layout::VerticalEdges margin) >+inline void Box::setVerticalMargin(Layout::MarginValues margin) > { > #if !ASSERT_DISABLED > setHasValidVerticalMargin(); > #endif >- ASSERT(!m_estimatedMarginTop || *m_estimatedMarginTop == margin.top); >- m_margin.vertical = margin; >+ ASSERT(!m_estimatedMarginTop || *m_estimatedMarginTop == margin.before); >+ m_margin.vertical = { *margin.before, *margin.after }; > } > >-inline void Box::setVerticalNonCollapsedMargin(Layout::VerticalEdges margin) >+inline void Box::setVerticalNonCollapsedMargin(Layout::MarginValues margin) > { > #if !ASSERT_DISABLED > setHasValidVerticalNonCollapsedMargin(); > #endif >- m_verticalNonCollapsedMargin = margin; >+ m_verticalNonCollapsedMargin = { *margin.before, *margin.after }; > } > > inline void Box::setHorizontalNonComputedMargin(Layout::HorizontalEdges margin) >@@ -660,6 +667,20 @@ inline LayoutUnit Box::borderRight() const > return m_border.horizontal.right; > } > >+inline void Box::setHasClearance(bool clearance) >+{ >+#if !ASSERT_DISABLED >+ m_hasValidClearance = true; >+#endif >+ m_hasClearance = clearance; >+} >+ >+inline bool Box::hasClearance() const >+{ >+ ASSERT(m_hasValidClearance); >+ return m_hasClearance; >+} >+ > } > } > #endif
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 192625
:
357130
|
357423
|
358137
|
358138
|
358142
|
358143
|
358144
|
358455
|
358479
|
358589
|
358670