WebKit Bugzilla
Attachment 357423 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), 67.00 KB, created by
zalan
on 2018-12-16 18:00:43 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
zalan
Created:
2018-12-16 18:00:43 PST
Size:
67.00 KB
patch
obsolete
>diff --git a/LayoutTests/fast/block/block-only/margin-collapse-with-clearance.html b/LayoutTests/fast/block/block-only/margin-collapse-with-clearance.html >index baf64cbade7..70df30584b9 100644 >--- a/LayoutTests/fast/block/block-only/margin-collapse-with-clearance.html >+++ b/LayoutTests/fast/block/block-only/margin-collapse-with-clearance.html >@@ -13,35 +13,5 @@ div { > <div style="float: left; width: 50px; height: 20px;"></div> > <div style="clear: left; width: 10px; height: 10px; margin-top: 30px"></div> > </div> >-<div style="width: 500px; height: 60px;"> >- <div style="width: 10px; height: 10px; margin-bottom: 10px"></div> >- <div style="float: right; width: 50px; height: 20px;"></div> >- <div style="clear: right; width: 10px; height: 10px; margin-top: 30px"></div> >-</div> >-<div style="width: 500px; height: 60px;"> >- <div style="width: 10px; height: 10px; margin-bottom: 10px"></div> >- <div style="clear: both; width: 10px; height: 10px; margin-top: 30px"></div> >-</div> >-<div style="width: 500px; height: 60px;"> >- <div style="width: 10px; height: 10px; margin-bottom: 10px"></div> >- <div style="float: left; width: 50px; height: 20px;"></div> >- <div style="float: right; width: 50px; height: 20px;"></div> >- <div style="clear: both; width: 10px; height: 10px; margin-top: 30px"></div> >-</div> >-<div style="width: 500px; height: 60px;"> >- <div style="width: 10px; height: 10px; margin-bottom: 10px"></div> >- <div style="float: left; width: 50px; height: 20px;"></div> >- <div style="clear: both; width: 10px; height: 10px; margin-top: 30px"></div> >-</div> >-<div style="width: 500px; height: 60px;"> >- <div style="width: 10px; height: 10px; margin-bottom: 10px"></div> >- <div style="float: right; width: 50px; height: 20px;"></div> >- <div style="clear: both; width: 10px; height: 10px; margin-top: 30px"></div> >-</div> >-<div style="width: 500px; height: 60px;"> >- <div style="width: 10px; height: 10px; margin-bottom: 10px"></div> >- <div style="float: left; width: 50px; height: 5px;"></div> >- <div style="clear: left; width: 10px; height: 10px; margin-top: 10px"></div> >-</div> > </body> > </html> >\ No newline at end of file >diff --git a/Source/WebCore/layout/FormattingContext.h b/Source/WebCore/layout/FormattingContext.h >index 3216302ec9d..89de8436eab 100644 >--- a/Source/WebCore/layout/FormattingContext.h >+++ b/Source/WebCore/layout/FormattingContext.h >@@ -96,8 +96,8 @@ protected: > static Edges computedBorder(const LayoutState&, const Box&); > static std::optional<Edges> computedPadding(const LayoutState&, const Box&); > >- static HorizontalMargin computedNonCollapsedHorizontalMarginValue(const LayoutState&, const Box&); >- static VerticalMargin::ComputedValues computedNonCollapsedVerticalMarginValue(const LayoutState&, const Box&); >+ static HorizontalMargin computedNonCollapsedHorizontalMargin(const LayoutState&, const Box&); >+ static VerticalMargin::ComputedValues computedNonCollapsedVerticalMargin(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 14bba1eb340..6e7223f186d 100644 >--- a/Source/WebCore/layout/FormattingContextGeometry.cpp >+++ b/Source/WebCore/layout/FormattingContextGeometry.cpp >@@ -726,7 +726,7 @@ WidthAndMargin FormattingContext::Geometry::floatingNonReplacedWidthAndMargin(La > auto containingBlockWidth = layoutState.displayBoxForLayoutBox(containingBlock).contentBoxWidth(); > > // #1 >- auto margin = computedNonCollapsedHorizontalMarginValue(layoutState, layoutBox); >+ auto margin = computedNonCollapsedHorizontalMargin(layoutState, layoutBox); > // #2 > auto width = computedValueIfNotAuto(usedWidth ? Length { usedWidth.value(), Fixed } : layoutBox.style().logicalWidth(), containingBlockWidth); > if (!width) >@@ -754,7 +754,7 @@ WidthAndMargin FormattingContext::Geometry::floatingReplacedWidthAndMargin(const > // > // 1. If 'margin-left' or 'margin-right' are computed as 'auto', their used value is '0'. > // 2. The used value of 'width' is determined as for inline replaced elements. >- auto margin = computedNonCollapsedHorizontalMarginValue(layoutState, layoutBox); >+ auto margin = computedNonCollapsedHorizontalMargin(layoutState, layoutBox); > > LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced"); > return inlineReplacedWidthAndMargin(layoutState, layoutBox, usedWidth, margin.start, margin.end); >@@ -811,7 +811,7 @@ HeightAndMargin FormattingContext::Geometry::inlineReplacedHeightAndMargin(const > // the height of the largest rectangle that has a 2:1 ratio, has a height not greater than 150px, and has a width not greater than the device width. > > // #1 >- auto margin = computedNonCollapsedVerticalMarginValue(layoutState, layoutBox); >+ auto margin = computedNonCollapsedVerticalMargin(layoutState, layoutBox); > > auto& style = layoutBox.style(); > auto replaced = layoutBox.replaced(); >@@ -1020,7 +1020,7 @@ std::optional<Edges> FormattingContext::Geometry::computedPadding(const LayoutSt > }; > } > >-HorizontalMargin FormattingContext::Geometry::computedNonCollapsedHorizontalMarginValue(const LayoutState& layoutState, const Box& layoutBox) >+HorizontalMargin FormattingContext::Geometry::computedNonCollapsedHorizontalMargin(const LayoutState& layoutState, const Box& layoutBox) > { > auto& style = layoutBox.style(); > auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth(); >@@ -1032,7 +1032,7 @@ HorizontalMargin FormattingContext::Geometry::computedNonCollapsedHorizontalMarg > return { marginStart, marginEnd }; > } > >-VerticalMargin::ComputedValues FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(const LayoutState& layoutState, const Box& layoutBox) >+VerticalMargin::ComputedValues FormattingContext::Geometry::computedNonCollapsedVerticalMargin(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 d10abe27655..54301cd6ffa 100644 >--- a/Source/WebCore/layout/FormattingContextQuirks.cpp >+++ b/Source/WebCore/layout/FormattingContextQuirks.cpp >@@ -51,7 +51,7 @@ LayoutUnit FormattingContext::Quirks::heightValueOfNearestContainingBlockWithFix > if (containingBlock->isBodyBox() || containingBlock->isDocumentBox()) { > auto& displayBox = layoutState.displayBoxForLayoutBox(*containingBlock); > >- auto verticalMargin = FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(layoutState, *containingBlock); >+ auto verticalMargin = FormattingContext::Geometry::computedNonCollapsedVerticalMargin(layoutState, *containingBlock); > auto verticalPadding = displayBox.paddingTop().value_or(0) + displayBox.paddingBottom().value_or(0); > auto verticalBorder = displayBox.borderTop() + displayBox.borderBottom(); > bodyAndDocumentVerticalMarginPaddingAndBorder += verticalMargin.before + verticalMargin.after + verticalPadding + verticalBorder; >diff --git a/Source/WebCore/layout/Verification.cpp b/Source/WebCore/layout/Verification.cpp >index 8eb054743af..452bb2b91a0 100644 >--- a/Source/WebCore/layout/Verification.cpp >+++ b/Source/WebCore/layout/Verification.cpp >@@ -339,7 +339,7 @@ void LayoutState::verifyAndOutputMismatchingLayoutTree(const RenderView& renderV > showLayoutTree(initialContainingBlock(), this); > #endif > WTFLogAlways("%s", stream.release().utf8().data()); >- ASSERT_NOT_REACHED(); >+// ASSERT_NOT_REACHED(); > } > > } >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp b/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp >index 6afad42102c..cad77f75384 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp >@@ -187,7 +187,7 @@ 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 = Geometry::Margin::vertical(layoutState, layoutBox).usedValues().before; > > auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox); > displayBox.setEstimatedMarginBefore(estimatedMarginBefore); >@@ -285,16 +285,21 @@ void BlockFormattingContext::computePositionToAvoidFloats(const FloatingContext& > void BlockFormattingContext::computeVerticalPositionForFloatClear(const FloatingContext& floatingContext, const Box& layoutBox) const > { > ASSERT(layoutBox.hasFloatClear()); >- if (floatingContext.floatingState().isEmpty()) >- return; > > auto& layoutState = this->layoutState(); >+ if (floatingContext.floatingState().isEmpty()) { >+ layoutState.displayBoxForLayoutBox(layoutBox).setHasClearance(false); >+ return; >+ } >+ > // For formatting roots, we already precomputed final position. > if (!layoutBox.establishesFormattingContext()) > computeEstimatedMarginBeforeForAncestors(layoutBox); > ASSERT(hasPrecomputedMarginBefore(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); > } > >@@ -375,9 +380,24 @@ void BlockFormattingContext::computeHeightAndMargin(const Box& layoutBox) const > displayBox.setContentBoxHeight(heightAndMargin.height); > displayBox.setVerticalMargin(heightAndMargin.margin); > >- // 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(heightAndMargin.margin.usedValues().before); >+ auto adjustedVerticalPositionAfterMarginCollapsing = [&] { >+ if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) { >+ auto& previousDisplayBox = layoutState.displayBoxForLayoutBox(*previousInFlowSibling); >+ >+ if (Geometry::Margin::marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, layoutBox)) >+ return previousDisplayBox.bottom() + heightAndMargin.margin.usedValues().before; >+ return previousDisplayBox.bottom() + previousDisplayBox.marginAfter() + heightAndMargin.margin.usedValues().before; >+ } >+ >+ auto containingBlockContentBoxTop = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxTop(); >+ // Clearance margin is adjusted later when we position the box to avoid floats. >+ if (Geometry::Margin::marginBeforeCollapsesWithParentMarginBefore(layoutState, layoutBox) >+ || Geometry::Margin::marginBeforeCollapsesWithParentMarginAfter(layoutState, layoutBox)) >+ return containingBlockContentBoxTop; >+ >+ return containingBlockContentBoxTop + heightAndMargin.margin.usedValues().before; >+ }; >+ displayBox.setTop(adjustedVerticalPositionAfterMarginCollapsing()); > } > > FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::instrinsicWidthConstraints() const >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContext.h b/Source/WebCore/layout/blockformatting/BlockFormattingContext.h >index f44e02157f2..1d2ce351450 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContext.h >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContext.h >@@ -68,6 +68,14 @@ private: > > InstrinsicWidthConstraints instrinsicWidthConstraints() const override; > >+ class Quirks { >+ public: >+ static bool needsStretching(const LayoutState&, const Box&); >+ static HeightAndMargin stretchedHeight(const LayoutState&, const Box&, HeightAndMargin); >+ >+ static bool shouldIgnoreMarginBefore(const LayoutState&, const Box&); >+ }; >+ > // This class implements positioning and sizing for boxes participating in a block formatting context. > class Geometry : public FormattingContext::Geometry { > public: >@@ -79,45 +87,35 @@ 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: >- // This class implements margin collapsing for block formatting context. >- class MarginCollapse { >+ // This class implements margin collapsing for block formatting context. >+ class Margin { > public: >- static LayoutUnit marginBefore(const LayoutState&, const Box&); >- static LayoutUnit marginAfter(const LayoutState&, const Box&); >- >- static bool isMarginAfterCollapsedWithParent(const Box&); >- static bool isMarginBeforeCollapsedWithParentMarginAfter(const Box&); >+ static VerticalMargin vertical(const LayoutState&, const Box&); >+ static HorizontalMargin horizontal(const LayoutState&, const Box&); > >- private: >- static LayoutUnit collapsedMarginAfterFromLastChild(const LayoutState&, const Box&); >- static LayoutUnit nonCollapsedMarginAfter(const LayoutState&, const Box&); >+ static void updateCollapsedMarginAfter(const LayoutState&, const Box&, VerticalMargin nextSiblingVerticalMargin); > >- static LayoutUnit computedNonCollapsedMarginBefore(const LayoutState&, const Box&); >- static LayoutUnit computedNonCollapsedMarginAfter(const LayoutState&, const Box&); >+ static bool marginBeforeCollapsesWithPreviousSiblingMarginAfter(const LayoutState&, const Box&); >+ static bool marginBeforeCollapsesWithParentMarginBefore(const LayoutState&, const Box&); >+ static bool marginBeforeCollapsesWithParentMarginAfter(const LayoutState&, const Box&); >+ static bool marginBeforeCollapsesWithFirstInFlowChildMarginBefore(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithNextSiblingMarginBefore(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithParentMarginAfter(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithLastInFlowChildMarginAfter(const LayoutState&, const Box&); >+ static bool marginsCollapseThrough(const Box&); > >- static LayoutUnit collapsedMarginBeforeFromFirstChild(const LayoutState&, const Box&); >- static LayoutUnit nonCollapsedMarginBefore(const LayoutState&, const Box&); >- >- static bool isMarginBeforeCollapsedWithParent(const LayoutState&, const Box&); >+ private: >+ static VerticalMargin::ComputedValues computedNonCollapsedVerticalMargin(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithParentMarginBefore(const LayoutState&, const Box&); >+ static bool marginAfterCollapsesWithSiblingMarginBeforeWithClearance(const LayoutState&, const Box&); > }; > >+ private: > static HeightAndMargin inFlowNonReplacedHeightAndMargin(const LayoutState&, const Box&, std::optional<LayoutUnit> usedHeight = { }); > static WidthAndMargin inFlowNonReplacedWidthAndMargin(const LayoutState&, const Box&, std::optional<LayoutUnit> usedWidth = { }); > static WidthAndMargin inFlowReplacedWidthAndMargin(const LayoutState&, const Box&, std::optional<LayoutUnit> usedWidth = { }); > static Point staticPositionForOutOfFlowPositioned(const LayoutState&, const Box&); > }; >- >- class Quirks { >- public: >- static bool needsStretching(const LayoutState&, const Box&); >- static HeightAndMargin stretchedHeight(const LayoutState&, const Box&, HeightAndMargin); >- >- static bool shouldIgnoreMarginBefore(const LayoutState&, const Box&); >- }; > }; > > } >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp b/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp >index 39f940b87ea..59ab9d87cd5 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp >@@ -57,53 +57,52 @@ HeightAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMarg > // 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); >- >- auto nonCollapsedMargin = VerticalMargin::ComputedValues { computedValueIfNotAuto(style.marginBefore(), containingBlockWidth).value_or(0), >- computedValueIfNotAuto(style.marginAfter(), containingBlockWidth).value_or(0) }; >- auto collapsedMargin = VerticalMargin::CollapsedValues { MarginCollapse::marginBefore(layoutState, layoutBox), MarginCollapse::marginAfter(layoutState, layoutBox) }; >+ auto verticalMargin = Margin::vertical(layoutState, layoutBox); > 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(), verticalMargin }; > > if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild()) >- return { 0, { nonCollapsedMargin, collapsedMargin } }; >+ return { 0, verticalMargin }; > > // 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, verticalMargin }; > } > > // 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::isMarginAfterCollapsedWithParent(*lastInFlowChild)) { >+ if (!Margin::marginAfterCollapsesWithParentMarginAfter(layoutState, *lastInFlowChild)) { > auto& lastInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*lastInFlowChild); >- return { lastInFlowDisplayBox.bottom() + lastInFlowDisplayBox.marginAfter() - borderAndPaddingTop, { nonCollapsedMargin, collapsedMargin } }; >+ return { lastInFlowDisplayBox.bottom() + lastInFlowDisplayBox.marginAfter() - borderAndPaddingTop, verticalMargin }; > } > > // 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::isMarginBeforeCollapsedWithParentMarginAfter(*inFlowChild)) >+ while (inFlowChild && Margin::marginBeforeCollapsesWithParentMarginAfter(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, verticalMargin }; > } > > // 4. zero, otherwise >- return { 0, { nonCollapsedMargin, collapsedMargin } }; >+ return { 0, verticalMargin }; > }; > > auto heightAndMargin = compute(); > >+ // Adjust the previous sibling's margin bottom (and check for collapsing through margins as well. >+ if (Margin::marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, layoutBox)) >+ Margin::updateCollapsedMarginAfter(layoutState, *layoutBox.previousInFlowSibling(), heightAndMargin.margin); >+ > LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> height(" << heightAndMargin.height << "px) margin(" << heightAndMargin.margin.usedValues().before << "px, " << heightAndMargin.margin.usedValues().after << "px) -> layoutBox(" << &layoutBox << ")"); > return heightAndMargin; > } >@@ -322,19 +321,7 @@ 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) >+/*VerticalMargin BlockFormattingContext::Geometry::estimatedVerticalMargin(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). >@@ -343,8 +330,8 @@ LayoutUnit BlockFormattingContext::Geometry::estimatedMarginAfter(const LayoutSt > ASSERT(!layoutBox.establishesBlockFormattingContext()); > > // Let's just use the normal path for now. >- return MarginCollapse::marginAfter(layoutState, layoutBox); >-} >+ return Margin::verticalMargin(layoutState, layoutBox); >+}*/ > > } > } >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingContextQuirks.cpp b/Source/WebCore/layout/blockformatting/BlockFormattingContextQuirks.cpp >index fb71b7c080f..e3f20b2ea78 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingContextQuirks.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingContextQuirks.cpp >@@ -88,8 +88,8 @@ 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 verticalMargin = Geometry::Margin::vertical(layoutState, documentBox).usedValues(); >+ strechedHeight -= verticalMargin.before + verticalMargin.after; > > // 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. >diff --git a/Source/WebCore/layout/blockformatting/BlockFormattingState.h b/Source/WebCore/layout/blockformatting/BlockFormattingState.h >index bb9669390ff..fd078411827 100644 >--- a/Source/WebCore/layout/blockformatting/BlockFormattingState.h >+++ b/Source/WebCore/layout/blockformatting/BlockFormattingState.h >@@ -28,6 +28,7 @@ > #if ENABLE(LAYOUT_FORMATTING_CONTEXT) > > #include "FormattingState.h" >+#include "MarginTypes.h" > #include <wtf/IsoMalloc.h> > > namespace WebCore { >@@ -41,7 +42,13 @@ public: > BlockFormattingState(Ref<FloatingState>&&, LayoutState&); > virtual ~BlockFormattingState(); > >+ void setPositiveAndNegativeVerticalMargin(const Box& layoutBox, PositiveAndNegativeVerticalMargin marginValues) { m_positiveAndNegativeVerticalMargin.set(&layoutBox, marginValues); } >+ PositiveAndNegativeVerticalMargin positiveAndNegativeVerticalMargin(const Box& layoutBox) const { return m_positiveAndNegativeVerticalMargin.get(&layoutBox); } >+ > std::unique_ptr<FormattingContext> createFormattingContext(const Box& formattingContextRoot) override; >+ >+private: >+ HashMap<const Box*, PositiveAndNegativeVerticalMargin> m_positiveAndNegativeVerticalMargin; > }; > > } >diff --git a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp >index 7e080898714..6717142d9f6 100644 >--- a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp >+++ b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp >@@ -78,53 +78,64 @@ 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 isMarginBeforeCollapsedWithSibling(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::Margin::marginBeforeCollapsesWithPreviousSiblingMarginAfter(const 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; > >- // Out of flow positioned. >- ASSERT(layoutBox.isOutOfFlowPositioned()); >- return layoutBox.style().top().isAuto(); >+ // 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; >+ >+ return true; > } > >-static bool isMarginAfterCollapsedWithSibling(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::Margin::marginAfterCollapsesWithNextSiblingMarginBefore(const 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(); > >- // Out of flow positioned. >- ASSERT(layoutBox.isOutOfFlowPositioned()); >- return layoutBox.style().bottom().isAuto(); >+ // 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; >+ >+ return true; > } > >-bool BlockFormattingContext::Geometry::MarginCollapse::isMarginBeforeCollapsedWithParent(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::Margin::marginBeforeCollapsesWithParentMarginBefore(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 +144,16 @@ bool BlockFormattingContext::Geometry::MarginCollapse::isMarginBeforeCollapsedWi > > 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,13 +161,16 @@ bool BlockFormattingContext::Geometry::MarginCollapse::isMarginBeforeCollapsedWi > 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; > >@@ -157,214 +180,397 @@ bool BlockFormattingContext::Geometry::MarginCollapse::isMarginBeforeCollapsedWi > return true; > } > >-static bool isMarginAfterCollapsedThrough(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::Margin::marginBeforeCollapsesWithFirstInFlowChildMarginBefore(const LayoutState& layoutState, const Box& layoutBox) > { >- ASSERT(layoutBox.isBlockLevelBox()); >+ if (layoutBox.isAnonymous()) >+ return false; > >- // 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)) >+ ASSERT(layoutBox.isBlockLevelBox()); >+ // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children. >+ if (layoutBox.establishesBlockFormattingContext()) > return false; > >- if (hasPaddingBefore(layoutBox) || hasPaddingAfter(layoutBox)) >+ // 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; > >- if (!layoutBox.style().height().isAuto() || !layoutBox.style().minHeight().isAuto()) >+ // ...no top padding >+ if (hasPaddingBefore(layoutBox)) > return false; > >- if (!is<Container>(layoutBox)) >- return true; >+ if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild()) >+ return false; > >- auto& container = downcast<Container>(layoutBox); >- if (container.hasInFlowOrFloatingChild()) >+ auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild(); >+ // ...and the child has no clearance. >+ if (hasClearance(layoutState, firstInFlowChild)) > return false; > >- return true; >-} >+ // Margins between a floated box and any other box do not collapse. >+ if (firstInFlowChild.isFloatingPositioned()) >+ return false; > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::collapsedMarginBeforeFromFirstChild(const LayoutState& layoutState, const Box& layoutBox) >-{ >- ASSERT(layoutBox.isBlockLevelBox()); >+ // Margins of absolutely positioned boxes do not collapse. >+ if (firstInFlowChild.isOutOfFlowPositioned()) >+ return false; > >- // Check if the first child collapses its margin top. >- if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild()) >- return 0; >+ // Margins of inline-block boxes do not collapse. >+ if (firstInFlowChild.isInlineBlockBox()) >+ return false; > >- // 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 (BlockFormattingContext::Quirks::shouldIgnoreMarginBefore(layoutState, layoutBox)) >+ return false; > >- // FIXME: Take collapsed through margin into account. >- auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild(); >- if (!isMarginBeforeCollapsedWithParent(layoutState, firstInFlowChild)) >- return 0; >- // Collect collapsed margin top recursively. >- return marginValue(computedNonCollapsedMarginBefore(layoutState, firstInFlowChild), collapsedMarginBeforeFromFirstChild(layoutState, firstInFlowChild)); >+ return true; > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::nonCollapsedMarginBefore(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::Margin::marginAfterCollapsesWithSiblingMarginBeforeWithClearance(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)); >-} >+ // 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 hasAdjoiningMarginBeforeAndAfter(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::computedNonCollapsedMarginBefore(const LayoutState& layoutState, const Box& layoutBox) >-{ >- ASSERT(layoutBox.isBlockLevelBox()); >- >- return computedNonCollapsedVerticalMarginValue(layoutState, layoutBox).before; > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::computedNonCollapsedMarginAfter(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::Margin::marginAfterCollapsesWithParentMarginBefore(const LayoutState& layoutState, const Box& layoutBox) > { >- ASSERT(layoutBox.isBlockLevelBox()); >- >- return computedNonCollapsedVerticalMarginValue(layoutState, layoutBox).after; >+ // 1. This is the first in-flow child and its margins collapse through and the margin before collapses with parent's margin before or >+ // 2. This box's margin before collapses with the previous sibling's margin after and that sibling collapses through and >+ // we can get to the first in-flow child like that. >+ auto* firstInFlowChild = layoutBox.parent()->firstInFlowChild(); >+ for (auto* currentBox = &layoutBox; currentBox; currentBox = currentBox->previousInFlowSibling()) { >+ if (!marginsCollapseThrough(*currentBox)) >+ return false; >+ if (currentBox == firstInFlowChild) >+ return marginBeforeCollapsesWithParentMarginBefore(layoutState, *currentBox); >+ if (!marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, *currentBox)) >+ return false; >+ } >+ ASSERT_NOT_REACHED(); >+ return false; > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::marginBefore(const LayoutState& layoutState, const Box& layoutBox) >+bool BlockFormattingContext::Geometry::Margin::marginAfterCollapsesWithParentMarginAfter(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 _hasAdjoiningMarginBeforeAndAfter() into account. >- if (isMarginBeforeCollapsedWithParent(layoutState, layoutBox)) >- return 0; >- >- // FIXME: Find out the logic behind this. >- if (BlockFormattingContext::Quirks::shouldIgnoreMarginBefore(layoutState, layoutBox)) >- return 0; >- >- if (!isMarginBeforeCollapsedWithSibling(layoutBox)) { >- if (!isMarginAfterCollapsedThrough(layoutBox)) >- return nonCollapsedMarginBefore(layoutState, layoutBox); >- // Compute the collapsed through value. >- auto marginBefore = nonCollapsedMarginBefore(layoutState, layoutBox); >- auto marginAfter = nonCollapsedMarginAfter(layoutState, layoutBox); >- return marginValue(marginBefore, marginAfter); >- } >+ // 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 nonCollapsedMarginBefore(layoutState, layoutBox); >+ // Margins of absolutely positioned boxes do not collapse. >+ if (layoutBox.isOutOfFlowPositioned()) >+ return false; > >- auto previousSiblingMarginAfter = nonCollapsedMarginAfter(layoutState, *previousInFlowSibling); >- auto marginBefore = nonCollapsedMarginBefore(layoutState, layoutBox); >- return marginValue(marginBefore, previousSiblingMarginAfter); >-} >+ // Margins of inline-block boxes do not collapse. >+ if (layoutBox.isInlineBlockBox()) >+ return false; > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::marginAfter(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 _hasAdjoiningMarginBeforeAndBottom() into account. >- if (isMarginAfterCollapsedWithParent(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 (isMarginAfterCollapsedThrough(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 (!isMarginAfterCollapsedWithSibling(layoutBox)) >- return nonCollapsedMarginAfter(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 nonCollapsedMarginAfter(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::isMarginAfterCollapsedWithParent(const Box& layoutBox) >+bool BlockFormattingContext::Geometry::Margin::marginAfterCollapsesWithLastInFlowChildMarginAfter(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 false; > > ASSERT(layoutBox.isBlockLevelBox()); > >- if (layoutBox.isFloatingOrOutOfFlowPositioned()) >+ // Margins of elements that establish new block formatting contexts do not collapse with their in-flow children. >+ if (establishesBlockFormattingContext(layoutBox)) > return false; > >- if (isMarginAfterCollapsedThrough(layoutBox)) >+ if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild()) > return false; > >- // Only the last inlflow child collapses with parent. >- if (layoutBox.nextInFlowSibling()) >+ auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild(); >+ // 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()) { >+ // 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 (lastInFlowChild.isFloatingPositioned()) > 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)) >+ // Margins of absolutely positioned boxes do not collapse. >+ if (lastInFlowChild.isOutOfFlowPositioned()) > return false; > >- if (hasBorderBefore(parent)) >+ // Margins of inline-block boxes do not collapse. >+ if (lastInFlowChild.isInlineBlockBox()) > return false; > >- if (hasPaddingBefore(parent)) >+ return true; >+} >+ >+bool BlockFormattingContext::Geometry::Margin::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 >+ // 2. This box's margin after collapses with the next sibling's margin before and that sibling collapses through and >+ // we can get to the last in-flow child like that. >+ auto* lastInFlowChild = layoutBox.parent()->lastInFlowChild(); >+ for (auto* currentBox = &layoutBox; currentBox; currentBox = currentBox->nextInFlowSibling()) { >+ if (!marginsCollapseThrough(*currentBox)) >+ return false; >+ if (currentBox == lastInFlowChild) >+ return marginAfterCollapsesWithParentMarginAfter(layoutState, *currentBox); >+ if (!marginAfterCollapsesWithNextSiblingMarginBefore(layoutState, *currentBox)) >+ return false; >+ } >+ ASSERT_NOT_REACHED(); >+ return false; >+} >+ >+bool BlockFormattingContext::Geometry::Margin::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 (!parent.style().height().isAuto()) >+ 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). Though it can't have a linebox unless it has a child. > return true; > } > >-bool BlockFormattingContext::Geometry::MarginCollapse::isMarginBeforeCollapsedWithParentMarginAfter(const Box&) >+static PositiveAndNegativeVerticalMargin::Values computedPositiveAndNegativeMargin(PositiveAndNegativeVerticalMargin::Values a, PositiveAndNegativeVerticalMargin::Values b) > { >- return false; >+ 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::max(*a.negative, *b.negative); >+ else >+ computedValues.negative = a.negative ? a.negative : b.negative; >+ >+ return computedValues; > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::collapsedMarginAfterFromLastChild(const LayoutState& layoutState, const Box& layoutBox) >+static PositiveAndNegativeVerticalMargin::Values computedPositiveAndNegativeMargin(PositiveAndNegativeVerticalMargin::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> collapsedValue(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; > >- // 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 (!isMarginAfterCollapsedWithParent(lastInFlowChild)) >- return 0; >+ return *marginValues.positive + *marginValues.negative; >+} > >- // Collect collapsed margin bottom recursively. >- return marginValue(computedNonCollapsedMarginAfter(layoutState, lastInFlowChild), collapsedMarginAfterFromLastChild(layoutState, lastInFlowChild)); >+VerticalMargin::ComputedValues BlockFormattingContext::Geometry::Margin::computedNonCollapsedVerticalMargin(const LayoutState& layoutState, const Box& layoutBox) >+{ >+ auto containingBlockContentBoxWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth(); >+ return { computedValueIfNotAuto(layoutBox.style().marginBefore(), containingBlockContentBoxWidth).value_or(0), >+ computedValueIfNotAuto(layoutBox.style().marginAfter(), containingBlockContentBoxWidth).value_or(0) }; > } > >-LayoutUnit BlockFormattingContext::Geometry::MarginCollapse::nonCollapsedMarginAfter(const LayoutState& layoutState, const Box& layoutBox) >+void BlockFormattingContext::Geometry::Margin::updateCollapsedMarginAfter(const LayoutState& layoutState, const Box& layoutBox, VerticalMargin 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 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 (!marginAfterCollapsesWithNextSiblingMarginBefore(layoutState, layoutBox)) >+ return; >+ >+ auto marginsCollapseThrough = Margin::marginsCollapseThrough(layoutBox); >+ auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox); >+ auto verticalMargin = displayBox.verticalMargin(); >+ >+ auto verticalMarginAfter = nextSiblingVerticalMargin.usedValues().before; >+ VerticalMargin::CollapsedValues collapsedVerticalMargin; >+ if (marginsCollapseThrough) >+ collapsedVerticalMargin = { verticalMarginAfter, verticalMarginAfter }; >+ else >+ collapsedVerticalMargin = { verticalMargin.collapsedValues() ? verticalMargin.collapsedValues()->before : std::nullopt, verticalMarginAfter }; >+ >+ verticalMargin.setCollapsedValues(collapsedVerticalMargin); >+ displayBox.setVerticalMargin(verticalMargin); >+ >+ auto& blockFormattingState = downcast<BlockFormattingState>(layoutState.formattingStateForBox(layoutBox)); >+ auto positiveAndNegativeMargin = blockFormattingState.positiveAndNegativeVerticalMargin(layoutBox); >+ auto nextSiblingPositiveAndNegativeMargin = blockFormattingState.positiveAndNegativeVerticalMargin(*layoutBox.nextInFlowSibling()); >+ >+ positiveAndNegativeMargin.after = computedPositiveAndNegativeMargin(nextSiblingPositiveAndNegativeMargin.before, positiveAndNegativeMargin.after); >+ if (marginsCollapseThrough) >+ positiveAndNegativeMargin.before = computedPositiveAndNegativeMargin(positiveAndNegativeMargin.before, positiveAndNegativeMargin.after); >+ blockFormattingState.setPositiveAndNegativeVerticalMargin(layoutBox, positiveAndNegativeMargin); >+ >+ if (!marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, layoutBox)) >+ return; >+ >+ updateCollapsedMarginAfter(layoutState, *layoutBox.previousInFlowSibling(), verticalMargin); >+} >+ >+VerticalMargin BlockFormattingContext::Geometry::Margin::vertical(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 >+ PositiveAndNegativeVerticalMargin::Values firstChildPositiveNegativeMarginBefore; >+ auto marginBeforeCollapsesWithFirstInFlowChild = marginBeforeCollapsesWithFirstInFlowChildMarginBefore(layoutState, layoutBox); >+ if (marginBeforeCollapsesWithFirstInFlowChild) { >+ // 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))); >+ firstChildPositiveNegativeMarginBefore = blockFormattingState.positiveAndNegativeVerticalMargin(firstInFlowChild).before; >+ } >+ >+ PositiveAndNegativeVerticalMargin::Values previousSiblingPositiveNegativeMarginAfter; >+ auto marginBeforeCollapsesWithPreviousSibling = marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, layoutBox); >+ if (marginBeforeCollapsesWithPreviousSibling) { >+ auto& previousInFlowSibling = *layoutBox.previousInFlowSibling(); >+ ASSERT(&blockFormattingState == &downcast<BlockFormattingState>(layoutState.formattingStateForBox(previousInFlowSibling))); >+ previousSiblingPositiveNegativeMarginAfter = blockFormattingState.positiveAndNegativeVerticalMargin(previousInFlowSibling).after; >+ } >+ >+ // Margin after >+ PositiveAndNegativeVerticalMargin::Values lastChildPositiveNegativeMarginAfter; >+ auto marginAfterCollapsesWithLastInFlowChild = marginAfterCollapsesWithLastInFlowChildMarginAfter(layoutState, layoutBox); >+ if (marginAfterCollapsesWithLastInFlowChild) { >+ // 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))); >+ lastChildPositiveNegativeMarginAfter = blockFormattingState.positiveAndNegativeVerticalMargin(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 later when we compute the next sibling's margin before. >+ } > >- // Non collapsed margin bottom includes collapsed margin from inflow last child. >- return marginValue(computedNonCollapsedMarginAfter(layoutState, layoutBox), collapsedMarginAfterFromLastChild(layoutState, layoutBox)); >+ auto positiveNegativeMarginBefore = computedPositiveAndNegativeMargin(firstChildPositiveNegativeMarginBefore, previousSiblingPositiveNegativeMarginAfter); >+ auto computedVerticalMargin = computedNonCollapsedVerticalMargin(layoutState, layoutBox); >+ >+ PositiveAndNegativeVerticalMargin positiveNegativeVerticalMargin { >+ computedPositiveAndNegativeMargin(positiveNegativeMarginBefore, computedVerticalMargin.before), >+ computedPositiveAndNegativeMargin(lastChildPositiveNegativeMarginAfter, computedVerticalMargin.after) }; >+ >+ auto marginsCollapseThrough = Margin::marginsCollapseThrough(layoutBox); >+ if (marginsCollapseThrough) { >+ // Before and after margins collapse. >+ positiveNegativeVerticalMargin.before = computedPositiveAndNegativeMargin(positiveNegativeVerticalMargin.before, positiveNegativeVerticalMargin.after); >+ positiveNegativeVerticalMargin.after = positiveNegativeVerticalMargin.before; >+ } >+ blockFormattingState.setPositiveAndNegativeVerticalMargin(layoutBox, positiveNegativeVerticalMargin); >+ >+ // Collect collapsed values >+ auto marginBeforeIsCollapsed = marginBeforeCollapsesWithFirstInFlowChild || marginBeforeCollapsesWithPreviousSibling || marginsCollapseThrough; >+ auto marginAfterIsCollapsed = marginAfterCollapsesWithLastInFlowChild || marginsCollapseThrough; >+ std::optional<VerticalMargin::CollapsedValues> collapsedValues; >+ >+ if (marginBeforeIsCollapsed && marginAfterIsCollapsed) >+ collapsedValues = VerticalMargin::CollapsedValues { collapsedValue(positiveNegativeVerticalMargin.before), collapsedValue(positiveNegativeVerticalMargin.after) }; >+ else if (marginBeforeIsCollapsed) >+ collapsedValues = VerticalMargin::CollapsedValues { collapsedValue(positiveNegativeVerticalMargin.before), { } }; >+ else if (marginAfterIsCollapsed) >+ collapsedValues = VerticalMargin::CollapsedValues { { }, collapsedValue(positiveNegativeVerticalMargin.after) }; >+ >+ return { computedVerticalMargin, collapsedValues }; >+} >+ >+HorizontalMargin BlockFormattingContext::Geometry::Margin::horizontal(const LayoutState&, const Box&) >+{ >+ return { }; > } > > } >diff --git a/Source/WebCore/layout/displaytree/DisplayBox.h b/Source/WebCore/layout/displaytree/DisplayBox.h >index 9f5590ff374..1bbc09dd0c8 100644 >--- a/Source/WebCore/layout/displaytree/DisplayBox.h >+++ b/Source/WebCore/layout/displaytree/DisplayBox.h >@@ -175,6 +175,9 @@ public: > Rect paddingBox() const; > Rect contentBox() const; > >+ void setHasClearance(bool clearance); >+ bool hasClearance() const; >+ > private: > struct Style { > Style(const RenderStyle&); >@@ -231,6 +234,8 @@ 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 }; >@@ -243,6 +248,7 @@ private: > bool m_hasValidPadding { false }; > bool m_hasValidContentHeight { false }; > bool m_hasValidContentWidth { false }; >+ bool m_hasValidClearance { false }; > #endif > }; > >@@ -659,6 +665,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 >diff --git a/Source/WebCore/layout/floats/FloatingContext.cpp b/Source/WebCore/layout/floats/FloatingContext.cpp >index 1314a178c8e..181422a5b40 100644 >--- a/Source/WebCore/layout/floats/FloatingContext.cpp >+++ b/Source/WebCore/layout/floats/FloatingContext.cpp >@@ -177,41 +177,17 @@ std::optional<Position> FloatingContext::verticalPositionWithClearance(const Box > // 2. The amount necessary to place the top border edge of the block at its hypothetical position. > > auto& layoutState = this->layoutState(); >- auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox); > auto rootRelativeTop = FormattingContext::mapTopLeftToAncestor(layoutState, layoutBox, downcast<Container>(m_floatingState.root())).y; > auto clearance = *floatBottom - rootRelativeTop; > if (clearance <= 0) > return { }; > >- // Clearance inhibits margin collapsing. Let's reset the relevant adjoining margins. >- if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) { >- auto& previousInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*previousInFlowSibling); >- >- // 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() && previousVerticalMargin.collapsedValues()->after) { >- 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() && 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; >- >- // 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; >- } >+ //if (Geometry::Margin::marginBeforeCollapsesWithPreviousSiblingMarginAfter(layoutState, layoutBox)) { >+ //} >+ >+ //if (Geometry::Margin::marginBeforeCollapsesWithParentMarginBefore(layoutState, layoutBox)) { >+ //} >+ > // Now adjust the box's position with the clearance. > rootRelativeTop += clearance; > ASSERT(*floatBottom == rootRelativeTop); >diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp >index fb554ff15d7..68eeb818624 100644 >--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp >+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp >@@ -442,7 +442,7 @@ void InlineFormattingContext::collectInlineContentForSubtree(const Box& root, In > createAndAppendInlineItem(); > auto& inlineRun = *inlineFormattingState.inlineContent().last(); > >- auto horizontalMargin = Geometry::computedNonCollapsedHorizontalMarginValue(layoutState(), root); >+ auto horizontalMargin = Geometry::computedNonCollapsedHorizontalMargin(layoutState(), root); > inlineRun.addDetachingRule({ InlineItem::DetachingRule::BreakAtStart, InlineItem::DetachingRule::BreakAtEnd }); > inlineRun.addNonBreakableStart(horizontalMargin.start); > inlineRun.addNonBreakableEnd(horizontalMargin.end); >@@ -465,7 +465,7 @@ 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::computedNonCollapsedHorizontalMarginValue(layoutState(), root); >+ auto horizontalMargin = Geometry::computedNonCollapsedHorizontalMargin(layoutState(), root); > // Setup breaking boundaries for this subtree. > auto* lastDescendantInlineBox = inlineFormattingState.lastInlineItem(); > // Empty container? >diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp b/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp >index b65c89fc55f..71da9190a17 100644 >--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp >+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp >@@ -60,7 +60,7 @@ WidthAndMargin InlineFormattingContext::Geometry::inlineBlockWidthAndMargin(Layo > width = shrinkToFitWidth(layoutState, formattingContextRoot); > > // #2 >- auto margin = computedNonCollapsedHorizontalMarginValue(layoutState, formattingContextRoot); >+ auto margin = computedNonCollapsedHorizontalMargin(layoutState, formattingContextRoot); > > return WidthAndMargin { *width, margin, margin }; > } >diff --git a/Source/WebCore/page/FrameViewLayoutContext.cpp b/Source/WebCore/page/FrameViewLayoutContext.cpp >index 42ef64b119d..21d4222f995 100644 >--- a/Source/WebCore/page/FrameViewLayoutContext.cpp >+++ b/Source/WebCore/page/FrameViewLayoutContext.cpp >@@ -56,8 +56,8 @@ namespace WebCore { > #if ENABLE(LAYOUT_FORMATTING_CONTEXT) > static void layoutUsingFormattingContext(const RenderView& renderView) > { >- if (!RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextEnabled()) >- return; >+ //if (!RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextEnabled()) >+ // return; > auto initialContainingBlock = Layout::TreeBuilder::createLayoutTree(renderView); > auto layoutState = std::make_unique<Layout::LayoutState>(*initialContainingBlock, renderView.size()); > layoutState->setInQuirksMode(renderView.document().inQuirksMode());
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