FormattingContextGeometry.cpp [plain text]
#include "config.h"
#include "FormattingContext.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
namespace WebCore {
namespace Layout {
static LayoutUnit contentHeightForFormattingContextRoot(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.style().logicalHeight().isAuto() && layoutBox.establishesFormattingContext());
if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowOrFloatingChild())
return 0;
auto& formattingRootContainer = downcast<Container>(layoutBox);
if (formattingRootContainer.establishesInlineFormattingContext())
return 0;
auto* firstDisplayBox = layoutContext.displayBoxForLayoutBox(*formattingRootContainer.firstInFlowChild());
auto* lastDisplayBox = layoutContext.displayBoxForLayoutBox(*formattingRootContainer.lastInFlowChild());
auto top = firstDisplayBox->marginBox().top();
auto bottom = lastDisplayBox->marginBox().bottom();
auto computedHeight = bottom - top;
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height] -> content height for formatting context root -> height(" << computedHeight << "px) layoutBox("<< &layoutBox << ")");
return computedHeight;
}
std::optional<LayoutUnit> FormattingContext::Geometry::computedValueIfNotAuto(const Length& geometryProperty, LayoutUnit containingBlockWidth)
{
if (geometryProperty.isAuto())
return std::nullopt;
return valueForLength(geometryProperty, containingBlockWidth);
}
std::optional<LayoutUnit> FormattingContext::Geometry::fixedValue(const Length& geometryProperty)
{
if (!geometryProperty.isFixed())
return std::nullopt;
return { geometryProperty.value() };
}
static LayoutUnit staticVerticalPositionForOutOfFlowPositioned(const LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isOutOfFlowPositioned());
LayoutUnit top;
if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) {
auto& previousInFlowDisplayBox = *layoutContext.displayBoxForLayoutBox(*previousInFlowSibling);
top += previousInFlowDisplayBox.bottom() + previousInFlowDisplayBox.nonCollapsedMarginBottom();
} else {
ASSERT(layoutBox.parent());
top = layoutContext.displayBoxForLayoutBox(*layoutBox.parent())->contentBoxTop();
}
auto* containingBlock = layoutBox.containingBlock();
for (auto* container = layoutBox.parent(); container != containingBlock; container = container->containingBlock()) {
auto& displayBox = *layoutContext.displayBoxForLayoutBox(*container);
top += displayBox.top();
ASSERT(!container->isPositioned());
}
return top;
}
static LayoutUnit staticHorizontalPositionForOutOfFlowPositioned(const LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isOutOfFlowPositioned());
ASSERT(layoutBox.parent());
auto left = layoutContext.displayBoxForLayoutBox(*layoutBox.parent())->contentBoxLeft();
auto* containingBlock = layoutBox.containingBlock();
for (auto* container = layoutBox.parent(); container != containingBlock; container = container->containingBlock()) {
auto& displayBox = *layoutContext.displayBoxForLayoutBox(*container);
left += displayBox.left();
ASSERT(!container->isPositioned());
}
return left;
}
LayoutUnit FormattingContext::Geometry::shrinkToFitWidth(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
{
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width] -> shrink to fit -> unsupported -> width(" << LayoutUnit { } << "px) layoutBox: " << &layoutBox << ")");
auto availableWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->width();
auto instrinsicWidthConstraints = formattingContext.instrinsicWidthConstraints(layoutContext, layoutBox);
return std::min(std::max(instrinsicWidthConstraints.minimum, availableWidth), instrinsicWidthConstraints.maximum);
}
FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOfFlowNonReplacedVerticalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
auto& style = layoutBox.style();
auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
auto& containingBlockDisplayBox = *layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock());
auto containingBlockHeight = containingBlockDisplayBox.height();
auto containingBlockWidth = containingBlockDisplayBox.width();
auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
auto height = computedValueIfNotAuto(style.logicalHeight(), containingBlockHeight);
auto marginTop = computedValueIfNotAuto(style.marginTop(), containingBlockWidth);
auto marginBottom = computedValueIfNotAuto(style.marginBottom(), containingBlockWidth);
auto paddingTop = displayBox.paddingTop();
auto paddingBottom = displayBox.paddingBottom();
auto borderTop = displayBox.borderTop();
auto borderBottom = displayBox.borderBottom();
if (!top && !height && !bottom)
top = staticVerticalPositionForOutOfFlowPositioned(layoutContext, layoutBox);
if (top && height && bottom) {
if (!marginTop && !marginBottom) {
auto marginTopAndBottom = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
marginTop = marginBottom = marginTopAndBottom / 2;
} else if (!marginTop)
marginTop = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom + *bottom);
else
marginBottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
auto boxHeight = *top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom + *bottom;
if (boxHeight > containingBlockHeight)
bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom);
}
if (!top && !height && bottom) {
height = contentHeightForFormattingContextRoot(layoutContext, layoutBox);
marginTop = marginTop.value_or(0);
marginBottom = marginBottom.value_or(0);
top = containingBlockHeight - (*marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom + *bottom);
}
if (!top && !bottom && height) {
top = staticVerticalPositionForOutOfFlowPositioned(layoutContext, layoutBox);
marginTop = marginTop.value_or(0);
marginBottom = marginBottom.value_or(0);
bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom);
}
if (!height && !bottom && top) {
height = contentHeightForFormattingContextRoot(layoutContext, layoutBox);
marginTop = marginTop.value_or(0);
marginBottom = marginBottom.value_or(0);
bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom);
}
if (!top && height && bottom) {
marginTop = marginTop.value_or(0);
marginBottom = marginBottom.value_or(0);
top = containingBlockHeight - (*marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom + *bottom);
}
if (!height && top && bottom) {
marginTop = marginTop.value_or(0);
marginBottom = marginBottom.value_or(0);
height = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + paddingBottom + borderBottom + *marginBottom + *bottom);
}
if (!bottom && top && height) {
marginTop = marginTop.value_or(0);
marginBottom = marginBottom.value_or(0);
bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom);
}
ASSERT(top);
ASSERT(bottom);
ASSERT(height);
ASSERT(marginTop);
ASSERT(marginBottom);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow non-replaced -> top(" << *top << "px) bottom(" << *bottom << "px) height(" << *height << "px) margin(" << *marginTop << "px, " << *marginBottom << "px) layoutBox(" << &layoutBox << ")");
return { *top, *bottom, { *height, { *marginTop, *marginBottom }, { } } };
}
FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
{
ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
auto& style = layoutBox.style();
auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
auto& containingBlock = *layoutBox.containingBlock();
auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(containingBlock)->contentBoxWidth();
auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
auto width = computedValueIfNotAuto(style.logicalWidth(), containingBlockWidth);
auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth);
auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth);
auto paddingLeft = displayBox.paddingLeft();
auto paddingRight = displayBox.paddingRight();
auto borderLeft = displayBox.borderLeft();
auto borderRight = displayBox.borderRight();
if (!left && !width && !right) {
marginLeft = marginLeft.value_or(0);
marginRight = marginRight.value_or(0);
auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutContext, layoutBox);
if (isLeftToRightDirection)
left = staticHorizontalPosition;
else
right = staticHorizontalPosition;
} else if (left && width && right) {
if (!marginLeft && !marginRight) {
auto marginLeftAndRight = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
if (marginLeftAndRight >= 0)
marginLeft = marginRight = marginLeftAndRight / 2;
else {
if (isLeftToRightDirection) {
marginLeft = LayoutUnit { 0 };
marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
} else {
marginRight = LayoutUnit { 0 };
marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
}
}
} else if (!marginLeft) {
marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
if (*marginLeft < 0) {
if (isLeftToRightDirection)
marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
else
marginLeft = containingBlockWidth - (borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
}
} else if (!marginRight) {
marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
if (*marginRight < 0) {
if (isLeftToRightDirection)
marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight);
else
marginRight = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
}
}
} else {
marginLeft = marginLeft.value_or(0);
marginRight = marginRight.value_or(0);
}
ASSERT(marginLeft);
ASSERT(marginRight);
if (!left && !width && right) {
width = shrinkToFitWidth(layoutContext, formattingContext, layoutBox);
left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
} else if (!left && !right && width) {
auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutContext, layoutBox);
if (isLeftToRightDirection) {
left = staticHorizontalPosition;
right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
} else {
right = staticHorizontalPosition;
left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
}
} else if (!width && !right && left) {
width = shrinkToFitWidth(layoutContext, formattingContext, layoutBox);
right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
} else if (!left && width && right) {
left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
} else if (!width && left && right) {
width = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + paddingRight + borderRight + *marginRight + *right);
} else if (!right && left && width) {
right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
}
ASSERT(left);
ASSERT(right);
ASSERT(width);
ASSERT(marginLeft);
ASSERT(marginRight);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow non-replaced -> left(" << *left << "px) right(" << *right << "px) width(" << *width << "px) margin(" << *marginLeft << "px, " << *marginRight << "px) layoutBox(" << &layoutBox << ")");
return { *left, *right, { *width, { *marginLeft, *marginRight } } };
}
FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOfFlowReplacedVerticalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isOutOfFlowPositioned() && layoutBox.replaced());
auto& style = layoutBox.style();
auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
auto& containingBlockDisplayBox = *layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock());
auto containingBlockHeight = containingBlockDisplayBox.height();
auto containingBlockWidth = containingBlockDisplayBox.width();
auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
auto height = inlineReplacedHeightAndMargin(layoutContext, layoutBox).height;
auto marginTop = computedValueIfNotAuto(style.marginTop(), containingBlockWidth);
auto marginBottom = computedValueIfNotAuto(style.marginBottom(), containingBlockWidth);
auto paddingTop = displayBox.paddingTop();
auto paddingBottom = displayBox.paddingBottom();
auto borderTop = displayBox.borderTop();
auto borderBottom = displayBox.borderBottom();
if (!top && !bottom) {
top = staticVerticalPositionForOutOfFlowPositioned(layoutContext, layoutBox);
}
if (!bottom) {
marginTop = marginTop.value_or(0);
marginBottom = marginBottom.value_or(0);
}
if (!marginTop && !marginBottom) {
auto marginTopAndBottom = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
marginTop = marginBottom = marginTopAndBottom / 2;
}
if (!top)
top = containingBlockHeight - (*marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom + *bottom);
if (!bottom)
bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom);
if (!marginTop)
marginTop = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom + *bottom);
if (!marginBottom)
marginBottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
auto boxHeight = *top + *marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom + *bottom;
if (boxHeight > containingBlockHeight)
bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow replaced -> top(" << *top << "px) bottom(" << *bottom << "px) height(" << height << "px) margin(" << *marginTop << "px, " << *marginBottom << "px) layoutBox(" << &layoutBox << ")");
return { *top, *bottom, { height, { *marginTop, *marginBottom }, { } } };
}
FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowReplacedHorizontalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isOutOfFlowPositioned() && layoutBox.replaced());
auto& style = layoutBox.style();
auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
auto& containingBlock = *layoutBox.containingBlock();
auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(containingBlock)->contentBoxWidth();
auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth);
auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth);
auto width = inlineReplacedWidthAndMargin(layoutContext, layoutBox).width;
auto paddingLeft = displayBox.paddingLeft();
auto paddingRight = displayBox.paddingRight();
auto borderLeft = displayBox.borderLeft();
auto borderRight = displayBox.borderRight();
if (!left && !right) {
auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutContext, layoutBox);
if (isLeftToRightDirection)
left = staticHorizontalPosition;
else
right = staticHorizontalPosition;
}
if (!left || !right) {
marginLeft = marginLeft.value_or(0);
marginRight = marginRight.value_or(0);
}
if (!marginLeft && !marginRight) {
auto marginLeftAndRight = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
if (marginLeftAndRight >= 0)
marginLeft = marginRight = marginLeftAndRight / 2;
else {
if (isLeftToRightDirection) {
marginLeft = LayoutUnit { 0 };
marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
} else {
marginRight = LayoutUnit { 0 };
marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
}
}
}
if (!left)
left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
if (!right)
right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight);
if (!marginLeft)
marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
if (!marginRight)
marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
auto boxWidth = (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
if (boxWidth > containingBlockWidth) {
if (isLeftToRightDirection)
right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight);
else
left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
}
ASSERT(left);
ASSERT(right);
ASSERT(marginLeft);
ASSERT(marginRight);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow replaced -> left(" << *left << "px) right(" << *right << "px) width(" << width << "px) margin(" << *marginLeft << "px, " << *marginRight << "px) layoutBox(" << &layoutBox << ")");
return { *left, *right, { width, { *marginLeft, *marginRight } } };
}
FormattingContext::Geometry::HeightAndMargin FormattingContext::Geometry::floatingNonReplacedHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isFloatingPositioned() && !layoutBox.replaced());
auto& style = layoutBox.style();
auto& containingBlock = *layoutBox.containingBlock();
auto& containingBlockDisplayBox = *layoutContext.displayBoxForLayoutBox(containingBlock);
auto containingBlockHeight = containingBlockDisplayBox.contentBoxHeight();
auto containingBlockWidth = containingBlockDisplayBox.contentBoxWidth();
auto height = computedValueIfNotAuto(style.logicalHeight(), containingBlockHeight);
auto marginTop = computedValueIfNotAuto(style.marginTop(), containingBlockWidth);
auto marginBottom = computedValueIfNotAuto(style.marginBottom(), containingBlockWidth);
marginTop = marginTop.value_or(0);
marginBottom = marginBottom.value_or(0);
if (!height)
height = contentHeightForFormattingContextRoot(layoutContext, layoutBox);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating non-replaced -> height(" << *height << "px) margin(" << *marginTop << "px, " << *marginBottom << "px) -> layoutBox(" << &layoutBox << ")");
return FormattingContext::Geometry::HeightAndMargin { *height, { *marginTop, *marginBottom }, { } };
}
FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::floatingNonReplacedWidthAndMargin(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
{
ASSERT(layoutBox.isFloatingPositioned() && !layoutBox.replaced());
auto& containingBlock = *layoutBox.containingBlock();
auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(containingBlock)->contentBoxWidth();
auto margin = computedNonCollapsedHorizontalMarginValue(layoutContext, layoutBox);
auto width = computedValueIfNotAuto(layoutBox.style().logicalWidth(), containingBlockWidth);
if (!width)
width = shrinkToFitWidth(layoutContext, formattingContext, layoutBox);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> floating non-replaced -> width(" << *width << "px) margin(" << margin.left << "px, " << margin.right << "px) -> layoutBox(" << &layoutBox << ")");
return FormattingContext::Geometry::WidthAndMargin { *width, margin };
}
FormattingContext::Geometry::HeightAndMargin FormattingContext::Geometry::floatingReplacedHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isFloatingPositioned() && layoutBox.replaced());
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
return inlineReplacedHeightAndMargin(layoutContext, layoutBox);
}
FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::floatingReplacedWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isFloatingPositioned() && layoutBox.replaced());
auto margin = computedNonCollapsedHorizontalMarginValue(layoutContext, layoutBox);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
return inlineReplacedWidthAndMargin(layoutContext, layoutBox, margin.left, margin.right);
}
FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOfFlowVerticalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isOutOfFlowPositioned());
if (!layoutBox.replaced())
return outOfFlowNonReplacedVerticalGeometry(layoutContext, layoutBox);
return outOfFlowReplacedVerticalGeometry(layoutContext, layoutBox);
}
FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowHorizontalGeometry(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
{
ASSERT(layoutBox.isOutOfFlowPositioned());
if (!layoutBox.replaced())
return outOfFlowNonReplacedHorizontalGeometry(layoutContext, formattingContext, layoutBox);
return outOfFlowReplacedHorizontalGeometry(layoutContext, layoutBox);
}
FormattingContext::Geometry::HeightAndMargin FormattingContext::Geometry::floatingHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT(layoutBox.isFloatingPositioned());
if (!layoutBox.replaced())
return floatingNonReplacedHeightAndMargin(layoutContext, layoutBox);
return floatingReplacedHeightAndMargin(layoutContext, layoutBox);
}
FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::floatingWidthAndMargin(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
{
ASSERT(layoutBox.isFloatingPositioned());
if (!layoutBox.replaced())
return floatingNonReplacedWidthAndMargin(layoutContext, formattingContext, layoutBox);
return floatingReplacedWidthAndMargin(layoutContext, layoutBox);
}
FormattingContext::Geometry::HeightAndMargin FormattingContext::Geometry::inlineReplacedHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
{
ASSERT((layoutBox.isOutOfFlowPositioned() || layoutBox.isFloatingPositioned() || layoutBox.isInFlow()) && layoutBox.replaced());
auto margin = computedNonCollapsedVerticalMarginValue(layoutContext, layoutBox);
auto& style = layoutBox.style();
auto replaced = layoutBox.replaced();
auto& containingBlockDisplayBox = *layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock());
auto containingBlockHeight = containingBlockDisplayBox.height();
auto containingBlockWidth = containingBlockDisplayBox.width();
auto height = computedValueIfNotAuto(style.logicalHeight(), containingBlockHeight);
auto width = computedValueIfNotAuto(style.logicalWidth(), containingBlockWidth);
if (!height && !width && replaced->hasIntrinsicHeight()) {
height = replaced->intrinsicHeight();
} else if (!height && replaced->hasIntrinsicRatio()) {
height = *width / replaced->intrinsicRatio();
} else if (!height && replaced->hasIntrinsicHeight()) {
height = replaced->intrinsicHeight();
} else if (!height) {
height = { 150 };
}
ASSERT(height);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow replaced -> height(" << *height << "px) margin(" << margin.top << "px, " << margin.bottom << "px) -> layoutBox(" << &layoutBox << ")");
return { *height, margin, { } };
}
FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::inlineReplacedWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox,
std::optional<LayoutUnit> precomputedMarginLeft, std::optional<LayoutUnit> precomputedMarginRight)
{
ASSERT((layoutBox.isOutOfFlowPositioned() || layoutBox.isFloatingPositioned() || layoutBox.isInFlow()) && layoutBox.replaced());
auto& style = layoutBox.style();
auto& containingBlockDisplayBox = *layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock());
auto containingBlockHeight = containingBlockDisplayBox.height();
auto containingBlockWidth = containingBlockDisplayBox.width();
auto computeMarginRight = [&]() {
if (precomputedMarginRight)
return precomputedMarginRight.value();
auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth);
return marginRight.value_or(LayoutUnit { 0 });
};
auto computeMarginLeft = [&]() {
if (precomputedMarginLeft)
return precomputedMarginLeft.value();
auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth);
return marginLeft.value_or(LayoutUnit { 0 });
};
LayoutUnit marginLeft = computeMarginLeft();
LayoutUnit marginRight = computeMarginRight();
auto replaced = layoutBox.replaced();
auto height = computedValueIfNotAuto(style.logicalHeight(), containingBlockHeight);
auto width = computedValueIfNotAuto(style.logicalWidth(), containingBlockWidth);
ASSERT(replaced);
if (!width && !height && replaced->hasIntrinsicWidth()) {
width = replaced->intrinsicWidth();
} else if ((!width && !height && !replaced->hasIntrinsicWidth() && replaced->hasIntrinsicHeight() && replaced->hasIntrinsicRatio())
|| (!width && height && replaced->hasIntrinsicRatio())) {
auto usedHeight = height.value_or(replaced->intrinsicHeight());
width = usedHeight * replaced->intrinsicRatio();
} else if (!width && !height && replaced->hasIntrinsicRatio() && !replaced->hasIntrinsicWidth() && !replaced->hasIntrinsicHeight()) {
ASSERT_NOT_IMPLEMENTED_YET();
} else if (!width && replaced->hasIntrinsicWidth()) {
width = replaced->intrinsicWidth();
} else {
width = { 300 };
}
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow replaced -> width(" << *width << "px) margin(" << marginLeft << "px, " << marginRight << "px) -> layoutBox(" << &layoutBox << ")");
return { *width, { marginLeft, marginRight } };
}
Display::Box::Edges FormattingContext::Geometry::computedBorder(LayoutContext&, const Box& layoutBox)
{
auto& style = layoutBox.style();
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Border] -> layoutBox: " << &layoutBox);
return {
{ style.borderLeft().boxModelWidth(), style.borderRight().boxModelWidth() },
{ style.borderTop().boxModelWidth(), style.borderBottom().boxModelWidth() }
};
}
std::optional<Display::Box::Edges> FormattingContext::Geometry::computedPadding(LayoutContext& layoutContext, const Box& layoutBox)
{
if (!layoutBox.isPaddingApplicable())
return std::nullopt;
auto& style = layoutBox.style();
auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->contentBoxWidth();
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Padding] -> layoutBox: " << &layoutBox);
return Display::Box::Edges {
{ valueForLength(style.paddingLeft(), containingBlockWidth), valueForLength(style.paddingRight(), containingBlockWidth) },
{ valueForLength(style.paddingTop(), containingBlockWidth), valueForLength(style.paddingBottom(), containingBlockWidth) }
};
}
Display::Box::HorizontalEdges FormattingContext::Geometry::computedNonCollapsedHorizontalMarginValue(const LayoutContext& layoutContext, const Box& layoutBox)
{
auto& style = layoutBox.style();
auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->contentBoxWidth();
auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth).value_or(LayoutUnit { 0 });
auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth).value_or(LayoutUnit { 0 });
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Margin] -> non collapsed horizontal -> margin(" << marginLeft << "px, " << marginRight << "px) -> layoutBox: " << &layoutBox);
return { marginLeft, marginRight };
}
Display::Box::VerticalEdges FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(const LayoutContext& layoutContext, const Box& layoutBox)
{
auto& style = layoutBox.style();
auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->contentBoxWidth();
auto marginTop = computedValueIfNotAuto(style.marginTop(), containingBlockWidth).value_or(LayoutUnit { 0 });
auto marginBottom = computedValueIfNotAuto(style.marginBottom(), containingBlockWidth).value_or(LayoutUnit { 0 });
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Margin] -> non collapsed vertical -> margin(" << marginTop << "px, " << marginBottom << "px) -> layoutBox: " << &layoutBox);
return { marginTop, marginBottom };
}
}
}
#endif