[LFC] Implement BlockMarginCollapse functions.
https://bugs.webkit.org/show_bug.cgi?id=185036
Reviewed by Antti Koivisto.
* layout/blockformatting/BlockMarginCollapse.cpp:
(WebCore::Layout::marginValue):
(WebCore::Layout::BlockMarginCollapse::BlockMarginCollapse):
(WebCore::Layout::BlockMarginCollapse::marginTop const):
(WebCore::Layout::BlockMarginCollapse::marginBottom const):
(WebCore::Layout::BlockMarginCollapse::isMarginTopCollapsedWithSibling const):
(WebCore::Layout::BlockMarginCollapse::isMarginBottomCollapsedWithSibling const):
(WebCore::Layout::BlockMarginCollapse::isMarginTopCollapsedWithParent const):
(WebCore::Layout::BlockMarginCollapse::isMarginBottomCollapsedWithParent const):
(WebCore::Layout::BlockMarginCollapse::nonCollapsedMarginTop const):
(WebCore::Layout::BlockMarginCollapse::nonCollapsedMarginBottom const):
(WebCore::Layout::BlockMarginCollapse::collapsedMarginTopFromFirstChild const):
(WebCore::Layout::BlockMarginCollapse::collapsedMarginBottomFromLastChild const):
(WebCore::Layout::BlockMarginCollapse::hasAdjoiningMarginTopAndBottom const):
* layout/blockformatting/BlockMarginCollapse.h:
* layout/layouttree/LayoutBox.h:
(WebCore::Layout::Box::style const):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@231140 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 3ee1a38..f37324f 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,28 @@
+2018-04-28 Zalan Bujtas <zalan@apple.com>
+
+ [LFC] Implement BlockMarginCollapse functions.
+ https://bugs.webkit.org/show_bug.cgi?id=185036
+
+ Reviewed by Antti Koivisto.
+
+ * layout/blockformatting/BlockMarginCollapse.cpp:
+ (WebCore::Layout::marginValue):
+ (WebCore::Layout::BlockMarginCollapse::BlockMarginCollapse):
+ (WebCore::Layout::BlockMarginCollapse::marginTop const):
+ (WebCore::Layout::BlockMarginCollapse::marginBottom const):
+ (WebCore::Layout::BlockMarginCollapse::isMarginTopCollapsedWithSibling const):
+ (WebCore::Layout::BlockMarginCollapse::isMarginBottomCollapsedWithSibling const):
+ (WebCore::Layout::BlockMarginCollapse::isMarginTopCollapsedWithParent const):
+ (WebCore::Layout::BlockMarginCollapse::isMarginBottomCollapsedWithParent const):
+ (WebCore::Layout::BlockMarginCollapse::nonCollapsedMarginTop const):
+ (WebCore::Layout::BlockMarginCollapse::nonCollapsedMarginBottom const):
+ (WebCore::Layout::BlockMarginCollapse::collapsedMarginTopFromFirstChild const):
+ (WebCore::Layout::BlockMarginCollapse::collapsedMarginBottomFromLastChild const):
+ (WebCore::Layout::BlockMarginCollapse::hasAdjoiningMarginTopAndBottom const):
+ * layout/blockformatting/BlockMarginCollapse.h:
+ * layout/layouttree/LayoutBox.h:
+ (WebCore::Layout::Box::style const):
+
2018-04-27 David Kilzer <ddkilzer@apple.com>
Add logging when SpringBoard enables WebThread
diff --git a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp
index f9ab0fb..bd4046c 100644
--- a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp
+++ b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.cpp
@@ -25,3 +25,234 @@
#include "config.h"
#include "BlockMarginCollapse.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "LayoutBox.h"
+#include "LayoutContainer.h"
+#include "LayoutUnit.h"
+#include "RenderStyle.h"
+#include <wtf/IsoMallocInlines.h>
+
+namespace WebCore {
+namespace Layout {
+
+WTF_MAKE_ISO_ALLOCATED_IMPL(BlockMarginCollapse);
+
+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;
+}
+
+BlockMarginCollapse::BlockMarginCollapse()
+{
+}
+
+LayoutUnit BlockMarginCollapse::marginTop(const Box& layoutBox) const
+{
+ if (layoutBox.isAnonymous())
+ return 0;
+
+ // TODO: take _hasAdjoiningMarginTopAndBottom() into account.
+ if (isMarginTopCollapsedWithParent(layoutBox))
+ return 0;
+
+ // Floats and out of flow positioned boxes do not collapse their margins.
+ if (!isMarginTopCollapsedWithSibling(layoutBox))
+ return nonCollapsedMarginTop(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,
+ // unless that sibling has clearance.
+ auto* previousInFlowSibling = layoutBox.previousInFlowSibling();
+ if (!previousInFlowSibling)
+ return nonCollapsedMarginTop(layoutBox);
+
+ auto previousSiblingMarginBottom = nonCollapsedMarginBottom(*previousInFlowSibling);
+ auto marginTop = nonCollapsedMarginTop(layoutBox);
+ return marginValue(marginTop, previousSiblingMarginBottom);
+}
+
+LayoutUnit BlockMarginCollapse::marginBottom(const Box& layoutBox) const
+{
+ if (layoutBox.isAnonymous())
+ return 0;
+
+ // TODO: take _hasAdjoiningMarginTopAndBottom() into account.
+ if (isMarginBottomCollapsedWithParent(layoutBox))
+ return 0;
+
+ // Floats and out of flow positioned boxes do not collapse their margins.
+ if (!isMarginBottomCollapsedWithSibling(layoutBox))
+ return nonCollapsedMarginBottom(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,
+ // unless that sibling has clearance.
+ if (layoutBox.nextInFlowSibling())
+ return 0;
+ return nonCollapsedMarginBottom(layoutBox);
+}
+
+bool BlockMarginCollapse::isMarginTopCollapsedWithSibling(const Box& layoutBox) const
+{
+ if (layoutBox.isFloatingPositioned())
+ return false;
+
+ if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned())
+ return true;
+
+ // Out of flow positioned.
+ ASSERT(layoutBox.isOutOfFlowPositioned());
+ return layoutBox.style().top().isAuto();
+}
+
+bool BlockMarginCollapse::isMarginBottomCollapsedWithSibling(const Box& layoutBox) const
+{
+ if (layoutBox.isFloatingPositioned())
+ return false;
+
+ if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned())
+ return true;
+
+ // Out of flow positioned.
+ ASSERT(layoutBox.isOutOfFlowPositioned());
+ return layoutBox.style().bottom().isAuto();
+}
+
+bool BlockMarginCollapse::isMarginTopCollapsedWithParent(const Box& layoutBox) const
+{
+ // The first inflow child could propagate its top margin to parent.
+ // https://www.w3.org/TR/CSS21/box.html#collapsing-margins
+ if (layoutBox.isAnonymous())
+ return false;
+
+ if (layoutBox.isFloatingOrOutOfFlowPositioned())
+ return false;
+
+ // We never margin collapse the initial containing block.
+ ASSERT(layoutBox.parent());
+ auto& parent = *layoutBox.parent();
+ // Is this box the first inlflow child?
+ if (parent.firstInFlowChild() != &layoutBox)
+ return false;
+
+ if (parent.establishesBlockFormattingContext())
+ return false;
+
+ // Margins of the root element's box do not collapse.
+ if (parent.isInitialContainingBlock())
+ return false;
+
+ if (!parent.style().borderTop().nonZero())
+ return false;
+
+ if (!parent.style().paddingTop().isZero())
+ return false;
+
+ return true;
+}
+
+bool BlockMarginCollapse::isMarginBottomCollapsedWithParent(const Box& layoutBox) const
+{
+ // last inflow box to parent.
+ // https://www.w3.org/TR/CSS21/box.html#collapsing-margins
+ if (layoutBox.isAnonymous())
+ return false;
+
+ if (layoutBox.isFloatingOrOutOfFlowPositioned())
+ return false;
+
+ // We never margin collapse the initial containing block.
+ ASSERT(layoutBox.parent());
+ auto& parent = *layoutBox.parent();
+ // Is this the last inlflow child?
+ if (parent.lastInFlowChild() != &layoutBox)
+ return false;
+
+ if (parent.establishesBlockFormattingContext())
+ return false;
+
+ // Margins of the root element's box do not collapse.
+ if (parent.isInitialContainingBlock())
+ return false;
+
+ if (!parent.style().borderTop().nonZero())
+ return false;
+
+ if (!parent.style().paddingTop().isZero())
+ return false;
+
+ if (!parent.style().height().isAuto())
+ return false;
+
+ return true;
+}
+
+LayoutUnit BlockMarginCollapse::nonCollapsedMarginTop(const Box& layoutBox) const
+{
+ // Non collapsed margin top includes collapsed margin from inflow first child.
+ return marginValue(layoutBox.style().marginTop().value(), collapsedMarginTopFromFirstChild(layoutBox));
+}
+
+LayoutUnit BlockMarginCollapse::nonCollapsedMarginBottom(const Box& layoutBox) const
+{
+ // Non collapsed margin bottom includes collapsed margin from inflow last child.
+ return marginValue(layoutBox.style().marginBottom().value(), collapsedMarginBottomFromLastChild(layoutBox));
+}
+
+LayoutUnit BlockMarginCollapse::collapsedMarginTopFromFirstChild(const Box& layoutBox) const
+{
+ // Check if the first child collapses its margin top.
+ if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
+ return 0;
+
+ auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild();
+ if (!isMarginTopCollapsedWithParent(firstInFlowChild))
+ return 0;
+
+ // Collect collapsed margin top recursively.
+ return marginValue(firstInFlowChild.style().marginTop().value(), collapsedMarginTopFromFirstChild(firstInFlowChild));
+}
+
+LayoutUnit BlockMarginCollapse::collapsedMarginBottomFromLastChild(const Box& layoutBox) const
+{
+ // Check if the last child propagates its margin bottom.
+ if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
+ return 0;
+
+ auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild();
+ if (!isMarginBottomCollapsedWithParent(lastInFlowChild))
+ return 0;
+
+ // Collect collapsed margin bottom recursively.
+ return marginValue(lastInFlowChild.style().marginBottom().value(), collapsedMarginBottomFromLastChild(lastInFlowChild));
+}
+
+bool BlockMarginCollapse::hasAdjoiningMarginTopAndBottom(const Box&) const
+{
+ // 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;
+}
+
+}
+}
+#endif
diff --git a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.h b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.h
index b20fab8..8dd229c 100644
--- a/Source/WebCore/layout/blockformatting/BlockMarginCollapse.h
+++ b/Source/WebCore/layout/blockformatting/BlockMarginCollapse.h
@@ -55,7 +55,6 @@
LayoutUnit nonCollapsedMarginBottom(const Box&) const;
LayoutUnit collapsedMarginTopFromFirstChild(const Box&) const;
LayoutUnit collapsedMarginBottomFromLastChild(const Box&) const;
- LayoutUnit marginValue(LayoutUnit currentMarginValue, LayoutUnit candidateMarginValue) const;
bool hasAdjoiningMarginTopAndBottom(const Box&) const;
};
diff --git a/Source/WebCore/layout/layouttree/LayoutBox.h b/Source/WebCore/layout/layouttree/LayoutBox.h
index 2c81a91..8aa330d 100644
--- a/Source/WebCore/layout/layouttree/LayoutBox.h
+++ b/Source/WebCore/layout/layouttree/LayoutBox.h
@@ -88,6 +88,7 @@
bool isInlineBox() const { return m_baseTypeFlags & InlineBoxFlag; }
bool isInlineContainer() const { return m_baseTypeFlags & InlineContainerFlag; }
+ const RenderStyle& style() const { return m_style; }
auto& weakPtrFactory() const { return m_weakFactory; }
protected: