BlockMarginCollapse.cpp [plain text]
#include "config.h"
#include "BlockFormattingContext.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "InlineFormattingState.h"
#include "LayoutBox.h"
#include "LayoutContainer.h"
#include "LayoutUnit.h"
#include "RenderStyle.h"
namespace WebCore {
namespace Layout {
static bool hasBorder(const BorderValue& borderValue)
{
if (borderValue.style() == BorderStyle::None || borderValue.style() == BorderStyle::Hidden)
return false;
return !!borderValue.width();
}
static bool hasPadding(const Length& paddingValue)
{
return !paddingValue.isZero();
}
static bool hasBorderBefore(const Box& layoutBox)
{
return hasBorder(layoutBox.style().borderBefore());
}
static bool hasBorderAfter(const Box& layoutBox)
{
return hasBorder(layoutBox.style().borderAfter());
}
static bool hasPaddingBefore(const Box& layoutBox)
{
return hasPadding(layoutBox.style().paddingBefore());
}
static bool hasPaddingAfter(const Box& layoutBox)
{
return hasPadding(layoutBox.style().paddingAfter());
}
static bool hasClearance(const Box& layoutBox)
{
if (!layoutBox.hasFloatClear())
return false;
return false;
}
static bool establishesBlockFormattingContext(const Box& layoutBox)
{
if (layoutBox.isDocumentBox())
return true;
return layoutBox.establishesBlockFormattingContext();
}
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;
}
LayoutUnit BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
{
ASSERT(layoutBox.isBlockLevelBox());
return Geometry::computedVerticalMargin(layoutState, layoutBox).before.valueOr(0);
}
LayoutUnit BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
{
ASSERT(layoutBox.isBlockLevelBox());
return Geometry::computedVerticalMargin(layoutState, layoutBox).after.valueOr(0);
}
LayoutUnit BlockFormattingContext::MarginCollapse::nonCollapsedMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
{
ASSERT(layoutBox.isBlockLevelBox());
return marginValue(computedNonCollapsedMarginBefore(layoutState, layoutBox), collapsedMarginBeforeFromFirstChild(layoutState, layoutBox));
}
LayoutUnit BlockFormattingContext::MarginCollapse::nonCollapsedMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
{
ASSERT(layoutBox.isBlockLevelBox());
return marginValue(computedNonCollapsedMarginAfter(layoutState, layoutBox), collapsedMarginAfterFromLastChild(layoutState, layoutBox));
}
LayoutUnit BlockFormattingContext::MarginCollapse::collapsedMarginBeforeFromFirstChild(const LayoutState& layoutState, const Box& layoutBox)
{
ASSERT(layoutBox.isBlockLevelBox());
if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
return 0;
if (layoutBox.establishesFormattingContext() && !layoutBox.establishesBlockFormattingContextOnly())
return 0;
auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild();
if (!marginBeforeCollapsesWithParentMarginBefore(layoutState, firstInFlowChild))
return 0;
return marginValue(computedNonCollapsedMarginBefore(layoutState, firstInFlowChild), collapsedMarginBeforeFromFirstChild(layoutState, firstInFlowChild));
}
LayoutUnit BlockFormattingContext::MarginCollapse::collapsedMarginAfterFromLastChild(const LayoutState& layoutState, const Box& layoutBox)
{
ASSERT(layoutBox.isBlockLevelBox());
if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
return 0;
if (layoutBox.establishesFormattingContext() && !layoutBox.establishesBlockFormattingContextOnly())
return 0;
auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild();
if (!marginAfterCollapsesWithParentMarginAfter(layoutState, lastInFlowChild))
return 0;
return marginValue(computedNonCollapsedMarginAfter(layoutState, lastInFlowChild), collapsedMarginAfterFromLastChild(layoutState, lastInFlowChild));
}
bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
{
auto* lastInFlowChild = layoutBox.parent()->lastInFlowChild();
for (auto* currentBox = &layoutBox; currentBox; currentBox = currentBox->nextInFlowSibling()) {
if (!marginsCollapseThrough(layoutState, *currentBox))
return false;
if (currentBox == lastInFlowChild)
return marginAfterCollapsesWithParentMarginAfter(layoutState, *currentBox);
if (!marginAfterCollapsesWithNextSibling(*currentBox))
return false;
}
ASSERT_NOT_REACHED();
return false;
}
bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithParentMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
{
if (layoutBox.isAnonymous())
return false;
ASSERT(layoutBox.isBlockLevelBox());
if (layoutBox.isFloatingPositioned())
return false;
if (layoutBox.isOutOfFlowPositioned())
return false;
if (layoutBox.isInlineBlockBox())
return false;
if (layoutBox.previousInFlowSibling())
return false;
auto& parent = *layoutBox.parent();
if (establishesBlockFormattingContext(parent))
return false;
if (hasBorderBefore(parent))
return false;
if (hasPaddingBefore(parent))
return false;
if (hasClearance(layoutBox))
return false;
if (BlockFormattingContext::Quirks::shouldIgnoreMarginBefore(layoutState, layoutBox))
return false;
return true;
}
bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithSiblingMarginBeforeWithClearance(const LayoutState& layoutState, const Box& layoutBox)
{
if (!marginsCollapseThrough(layoutState, layoutBox))
return false;
for (auto* previousSibling = layoutBox.previousInFlowSibling(); previousSibling; previousSibling = previousSibling->previousInFlowSibling()) {
if (!marginsCollapseThrough(layoutState, *previousSibling))
return false;
if (hasClearance(*previousSibling))
return true;
}
return false;
}
bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithParentMarginBefore(const LayoutState& layoutState, const Box& layoutBox)
{
auto* firstInFlowChild = layoutBox.parent()->firstInFlowChild();
for (auto* currentBox = &layoutBox; currentBox; currentBox = currentBox->previousInFlowSibling()) {
if (!marginsCollapseThrough(layoutState, *currentBox))
return false;
if (currentBox == firstInFlowChild)
return marginBeforeCollapsesWithParentMarginBefore(layoutState, *currentBox);
if (!marginBeforeCollapsesWithPreviousSibling(*currentBox))
return false;
}
ASSERT_NOT_REACHED();
return false;
}
bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithParentMarginAfter(const LayoutState& layoutState, const Box& layoutBox)
{
if (layoutBox.isAnonymous())
return false;
ASSERT(layoutBox.isBlockLevelBox());
if (layoutBox.isFloatingPositioned())
return false;
if (layoutBox.isOutOfFlowPositioned())
return false;
if (layoutBox.isInlineBlockBox())
return false;
if (layoutBox.nextInFlowSibling())
return false;
auto& parent = *layoutBox.parent();
if (establishesBlockFormattingContext(parent))
return false;
if (!parent.style().height().isAuto())
return false;
if (hasPaddingBefore(parent))
return false;
if (hasBorderBefore(parent))
return false;
if (marginAfterCollapsesWithSiblingMarginBeforeWithClearance(layoutState, layoutBox))
return false;
auto computedMinHeight = parent.style().logicalMinHeight();
if (!computedMinHeight.isAuto() && computedMinHeight.value() && marginAfterCollapsesWithParentMarginBefore(layoutState, layoutBox))
return false;
return true;
}
bool BlockFormattingContext::MarginCollapse::marginBeforeCollapsesWithPreviousSibling(const Box& layoutBox)
{
ASSERT(layoutBox.isBlockLevelBox());
if (!layoutBox.previousInFlowSibling())
return false;
auto& previousInFlowSibling = *layoutBox.previousInFlowSibling();
if (layoutBox.isFloatingPositioned() || previousInFlowSibling.isFloatingPositioned())
return false;
if ((layoutBox.isOutOfFlowPositioned() && !layoutBox.style().top().isAuto())
|| (previousInFlowSibling.isOutOfFlowPositioned() && !previousInFlowSibling.style().bottom().isAuto()))
return false;
if (layoutBox.isInlineBlockBox() || previousInFlowSibling.isInlineBlockBox())
return false;
if (hasClearance(layoutBox))
return false;
return true;
}
bool BlockFormattingContext::MarginCollapse::marginAfterCollapsesWithNextSibling(const Box& layoutBox)
{
ASSERT(layoutBox.isBlockLevelBox());
if (!layoutBox.nextInFlowSibling())
return false;
return marginBeforeCollapsesWithPreviousSibling(*layoutBox.nextInFlowSibling());
}
bool BlockFormattingContext::MarginCollapse::marginsCollapseThrough(const LayoutState& layoutState, const Box& layoutBox)
{
ASSERT(layoutBox.isBlockLevelBox());
if (hasBorderBefore(layoutBox) || hasBorderAfter(layoutBox))
return false;
if (hasPaddingBefore(layoutBox) || hasPaddingAfter(layoutBox))
return false;
if (!layoutBox.style().height().isAuto())
return false;
if (!layoutBox.style().minHeight().isAuto())
return false;
if (!is<Container>(layoutBox))
return true;
if (layoutBox.establishesInlineFormattingContext()) {
if (downcast<InlineFormattingState>(layoutState.establishedFormattingState(layoutBox)).inlineRuns().isEmpty())
return false;
} else {
for (auto* inflowChild = downcast<Container>(layoutBox).firstInFlowChild(); inflowChild; inflowChild = inflowChild->nextInFlowSibling()) {
if (!marginsCollapseThrough(layoutState, *inflowChild))
return true;
}
}
return true;
}
LayoutUnit BlockFormattingContext::MarginCollapse::marginBefore(const LayoutState& layoutState, const Box& layoutBox)
{
if (layoutBox.isAnonymous())
return 0;
ASSERT(layoutBox.isBlockLevelBox());
if (marginBeforeCollapsesWithParentMarginBefore(layoutState, layoutBox))
return 0;
if (BlockFormattingContext::Quirks::shouldIgnoreMarginBefore(layoutState, layoutBox))
return 0;
if (!marginBeforeCollapsesWithPreviousSibling(layoutBox)) {
if (!marginsCollapseThrough(layoutState, layoutBox))
return nonCollapsedMarginBefore(layoutState, layoutBox);
auto marginBefore = nonCollapsedMarginBefore(layoutState, layoutBox);
auto marginAfter = nonCollapsedMarginAfter(layoutState, layoutBox);
return marginValue(marginBefore, marginAfter);
}
auto* previousInFlowSibling = layoutBox.previousInFlowSibling();
if (!previousInFlowSibling)
return nonCollapsedMarginBefore(layoutState, layoutBox);
auto previousSiblingMarginAfter = nonCollapsedMarginAfter(layoutState, *previousInFlowSibling);
auto marginBefore = nonCollapsedMarginBefore(layoutState, layoutBox);
return marginValue(marginBefore, previousSiblingMarginAfter);
}
LayoutUnit BlockFormattingContext::MarginCollapse::marginAfter(const LayoutState& layoutState, const Box& layoutBox)
{
if (layoutBox.isAnonymous())
return 0;
ASSERT(layoutBox.isBlockLevelBox());
if (marginAfterCollapsesWithParentMarginAfter(layoutState, layoutBox))
return 0;
if (marginsCollapseThrough(layoutState, layoutBox))
return 0;
if (!marginAfterCollapsesWithNextSibling(layoutBox))
return nonCollapsedMarginAfter(layoutState, layoutBox);
if (layoutBox.nextInFlowSibling())
return 0;
return nonCollapsedMarginAfter(layoutState, layoutBox);
}
}
}
#endif