BlockMarginCollapse.cpp [plain text]
#include "config.h"
#include "BlockFormattingContext.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "LayoutBox.h"
#include "LayoutContainer.h"
#include "LayoutUnit.h"
#include "RenderStyle.h"
namespace WebCore {
namespace Layout {
static LayoutUnit marginValue(LayoutUnit currentMarginValue, LayoutUnit candidateMarginValue)
{
if (!candidateMarginValue)
return currentMarginValue;
if (!currentMarginValue)
return candidateMarginValue;
if (candidateMarginValue > 0 && currentMarginValue > 0)
return std::max(candidateMarginValue, currentMarginValue);
if (candidateMarginValue < 0 && currentMarginValue < 0)
return 0 - std::max(std::abs(candidateMarginValue.toFloat()), std::abs(currentMarginValue.toFloat()));
return currentMarginValue + candidateMarginValue;
}
static bool isMarginTopCollapsedWithSibling(const Box& layoutBox)
{
if (layoutBox.isFloatingPositioned())
return false;
if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned())
return true;
ASSERT(layoutBox.isOutOfFlowPositioned());
return layoutBox.style().top().isAuto();
}
static bool isMarginBottomCollapsedWithSibling(const Box& layoutBox)
{
if (layoutBox.isFloatingPositioned())
return false;
if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned())
return true;
ASSERT(layoutBox.isOutOfFlowPositioned());
return layoutBox.style().bottom().isAuto();
}
static bool isMarginTopCollapsedWithParent(const LayoutContext& layoutContext, const Box& layoutBox)
{
if (layoutBox.isAnonymous())
return false;
if (layoutBox.isFloatingOrOutOfFlowPositioned())
return false;
ASSERT(layoutBox.parent());
auto& parent = *layoutBox.parent();
if (parent.firstInFlowChild() != &layoutBox)
return false;
if (parent.establishesBlockFormattingContext())
return false;
if (parent.isDocumentBox() || parent.isInitialContainingBlock())
return false;
auto& parentDisplayBox = *layoutContext.displayBoxForLayoutBox(parent);
if (parentDisplayBox.borderTop())
return false;
if (parentDisplayBox.paddingTop())
return false;
return true;
}
static bool isMarginBottomCollapsedThrough(const LayoutContext& layoutContext, const Box& layoutBox)
{
auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
if (displayBox.borderTop() || displayBox.borderBottom())
return false;
if (displayBox.paddingTop() || displayBox.paddingBottom())
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())
return false;
return true;
}
LayoutUnit BlockFormattingContext::MarginCollapse::collapsedMarginTopFromFirstChild(const LayoutContext& layoutContext, const Box& layoutBox)
{
if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
return 0;
auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild();
if (!isMarginTopCollapsedWithParent(layoutContext, firstInFlowChild))
return 0;
return marginValue(computedNonCollapsedMarginTop(layoutContext, firstInFlowChild), collapsedMarginTopFromFirstChild(layoutContext, firstInFlowChild));
}
LayoutUnit BlockFormattingContext::MarginCollapse::nonCollapsedMarginTop(const LayoutContext& layoutContext, const Box& layoutBox)
{
return marginValue(computedNonCollapsedMarginTop(layoutContext, layoutBox), collapsedMarginTopFromFirstChild(layoutContext, layoutBox));
}
LayoutUnit BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginTop(const LayoutContext& layoutContext, const Box& layoutBox)
{
return FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(layoutContext, layoutBox).top;
}
LayoutUnit BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginBottom(const LayoutContext& layoutContext, const Box& layoutBox)
{
return FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(layoutContext, layoutBox).bottom;
}
LayoutUnit BlockFormattingContext::MarginCollapse::marginTop(const LayoutContext& layoutContext, const Box& layoutBox)
{
if (layoutBox.isAnonymous())
return 0;
if (isMarginTopCollapsedWithParent(layoutContext, layoutBox))
return 0;
if (!isMarginTopCollapsedWithSibling(layoutBox)) {
if (!isMarginBottomCollapsedThrough(layoutContext, layoutBox))
return nonCollapsedMarginTop(layoutContext, layoutBox);
auto marginTop = nonCollapsedMarginTop(layoutContext, layoutBox);
auto marginBottom = nonCollapsedMarginBottom(layoutContext, layoutBox);
return marginValue(marginTop, marginBottom);
}
auto* previousInFlowSibling = layoutBox.previousInFlowSibling();
if (!previousInFlowSibling)
return nonCollapsedMarginTop(layoutContext, layoutBox);
auto previousSiblingMarginBottom = nonCollapsedMarginBottom(layoutContext, *previousInFlowSibling);
auto marginTop = nonCollapsedMarginTop(layoutContext, layoutBox);
return marginValue(marginTop, previousSiblingMarginBottom);
}
LayoutUnit BlockFormattingContext::MarginCollapse::marginBottom(const LayoutContext& layoutContext, const Box& layoutBox)
{
if (layoutBox.isAnonymous())
return 0;
if (isMarginBottomCollapsedWithParent(layoutContext, layoutBox))
return 0;
if (isMarginBottomCollapsedThrough(layoutContext, layoutBox))
return 0;
if (!isMarginBottomCollapsedWithSibling(layoutBox))
return nonCollapsedMarginBottom(layoutContext, layoutBox);
if (layoutBox.nextInFlowSibling())
return 0;
return nonCollapsedMarginBottom(layoutContext, layoutBox);
}
bool BlockFormattingContext::MarginCollapse::isMarginBottomCollapsedWithParent(const LayoutContext& layoutContext, const Box& layoutBox)
{
if (layoutBox.isAnonymous())
return false;
if (layoutBox.isFloatingOrOutOfFlowPositioned())
return false;
if (isMarginBottomCollapsedThrough(layoutContext, layoutBox))
return false;
ASSERT(layoutBox.parent());
auto& parent = *layoutBox.parent();
if (parent.lastInFlowChild() != &layoutBox)
return false;
if (parent.establishesBlockFormattingContext())
return false;
if (parent.isDocumentBox() || parent.isInitialContainingBlock())
return false;
auto& parentDisplayBox = *layoutContext.displayBoxForLayoutBox(parent);
if (parentDisplayBox.borderTop())
return false;
if (parentDisplayBox.paddingTop())
return false;
if (!parent.style().height().isAuto())
return false;
return true;
}
bool BlockFormattingContext::MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(const Box&)
{
return false;
}
LayoutUnit BlockFormattingContext::MarginCollapse::collapsedMarginBottomFromLastChild(const LayoutContext& layoutContext, const Box& layoutBox)
{
if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
return 0;
auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild();
if (!isMarginBottomCollapsedWithParent(layoutContext, lastInFlowChild))
return 0;
return marginValue(computedNonCollapsedMarginBottom(layoutContext, lastInFlowChild), collapsedMarginBottomFromLastChild(layoutContext, lastInFlowChild));
}
LayoutUnit BlockFormattingContext::MarginCollapse::nonCollapsedMarginBottom(const LayoutContext& layoutContext, const Box& layoutBox)
{
return marginValue(computedNonCollapsedMarginBottom(layoutContext, layoutBox), collapsedMarginBottomFromLastChild(layoutContext, layoutBox));
}
}
}
#endif