FormattingContextGeometry.cpp [plain text]
#include "config.h"
#include "FormattingContext.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "BlockFormattingState.h"
#include "FlexFormattingState.h"
#include "FloatingContext.h"
#include "FloatingState.h"
#include "InlineFormattingState.h"
#include "LayoutContext.h"
#include "LayoutInitialContainingBlock.h"
#include "LayoutReplacedBox.h"
#include "Logging.h"
#include "TableFormattingState.h"
namespace WebCore {
namespace Layout {
static inline bool isHeightAuto(const Box& layoutBox)
{
auto height = layoutBox.style().logicalHeight();
if (height.isAuto())
return true;
if (height.isPercent()) {
if (layoutBox.isOutOfFlowPositioned())
return false;
return !layoutBox.containingBlock().style().logicalHeight().isFixed();
}
return false;
}
Optional<LayoutUnit> FormattingContext::Geometry::computedHeightValue(const Box& layoutBox, HeightType heightType, Optional<LayoutUnit> containingBlockHeight) const
{
auto& style = layoutBox.style();
auto height = heightType == HeightType::Normal ? style.logicalHeight() : heightType == HeightType::Min ? style.logicalMinHeight() : style.logicalMaxHeight();
if (height.isUndefined() || height.isAuto() || height.isMaxContent() || height.isMinContent() || height.isFitContent())
return { };
if (height.isFixed())
return LayoutUnit { height.value() };
if (!containingBlockHeight) {
if (layoutState().inQuirksMode())
containingBlockHeight = formattingContext().quirks().heightValueOfNearestContainingBlockWithFixedHeight(layoutBox);
else {
auto nonAnonymousContainingBlockLogicalHeight = [&] {
auto& initialContainingBlock = layoutBox.initialContainingBlock();
for (auto* containingBlock = &layoutBox.containingBlock(); containingBlock != &initialContainingBlock; containingBlock = &containingBlock->containingBlock()) {
if (containingBlock->isAnonymous())
continue;
return containingBlock->style().logicalHeight();
}
return initialContainingBlock.style().logicalHeight();
};
containingBlockHeight = fixedValue(nonAnonymousContainingBlockLogicalHeight());
}
}
if (!containingBlockHeight)
return { };
return valueForLength(height, *containingBlockHeight);
}
Optional<LayoutUnit> FormattingContext::Geometry::computedHeight(const Box& layoutBox, Optional<LayoutUnit> containingBlockHeight) const
{
if (auto height = computedHeightValue(layoutBox, HeightType::Normal, containingBlockHeight)) {
if (layoutBox.style().boxSizing() == BoxSizing::ContentBox)
return height;
auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
return *height - (boxGeometry.verticalBorder() + boxGeometry.verticalPadding().valueOr(0));
}
return { };
}
Optional<LayoutUnit> FormattingContext::Geometry::computedWidthValue(const Box& layoutBox, WidthType widthType, LayoutUnit containingBlockWidth)
{
ASSERT(!layoutBox.isInlineBox() || layoutBox.isOutOfFlowPositioned());
auto width = [&] {
auto& style = layoutBox.style();
switch (widthType) {
case WidthType::Normal:
return style.logicalWidth();
case WidthType::Min:
return style.logicalMinWidth();
case WidthType::Max:
return style.logicalMaxWidth();
}
ASSERT_NOT_REACHED();
return style.logicalWidth();
}();
if (auto computedValue = this->computedValue(width, containingBlockWidth))
return computedValue;
if (width.isMinContent() || width.isMaxContent() || width.isFitContent()) {
if (!is<ContainerBox>(layoutBox))
return { };
auto& containerBox = downcast<ContainerBox>(layoutBox);
auto intrinsicWidthConstraints = [&] {
if (!containerBox.hasInFlowOrFloatingChild())
return IntrinsicWidthConstraints { 0_lu, containingBlockWidth };
ASSERT(containerBox.establishesFormattingContext());
auto& formattingState = layoutState().ensureFormattingState(containerBox);
if (auto intrinsicWidthConstraints = formattingState.intrinsicWidthConstraints())
return *intrinsicWidthConstraints;
return LayoutContext::createFormattingContext(containerBox, layoutState())->computedIntrinsicWidthConstraints();
}();
if (width.isMinContent())
return intrinsicWidthConstraints.minimum;
if (width.isMaxContent())
return intrinsicWidthConstraints.maximum;
ASSERT(width.isFitContent());
return std::min(intrinsicWidthConstraints.maximum, std::max(intrinsicWidthConstraints.minimum, containingBlockWidth));
}
return { };
}
Optional<LayoutUnit> FormattingContext::Geometry::computedWidth(const Box& layoutBox, LayoutUnit containingBlockWidth)
{
if (auto computedWidth = computedWidthValue(layoutBox, WidthType::Normal, containingBlockWidth)) {
auto& style = layoutBox.style();
if (style.boxSizing() == BoxSizing::ContentBox || style.width().isIntrinsicOrAuto())
return computedWidth;
auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
return *computedWidth - (boxGeometry.horizontalBorder() + boxGeometry.horizontalPadding().valueOr(0));
}
return { };
}
LayoutUnit FormattingContext::Geometry::contentHeightForFormattingContextRoot(const ContainerBox& formattingContextRoot) const
{
ASSERT(formattingContextRoot.establishesFormattingContext());
ASSERT(isHeightAuto(formattingContextRoot) || formattingContextRoot.establishesTableFormattingContext() || formattingContextRoot.isTableCell());
if (!formattingContextRoot.hasInFlowOrFloatingChild())
return { };
return LayoutContext::createFormattingContext(formattingContextRoot, const_cast<LayoutState&>(layoutState()))->usedContentHeight();
}
Optional<LayoutUnit> FormattingContext::Geometry::computedValue(const Length& geometryProperty, LayoutUnit containingBlockWidth) const
{
if (geometryProperty.isFixed() || geometryProperty.isPercent() || geometryProperty.isCalculated())
return valueForLength(geometryProperty, containingBlockWidth);
return { };
}
Optional<LayoutUnit> FormattingContext::Geometry::fixedValue(const Length& geometryProperty) const
{
if (!geometryProperty.isFixed())
return { };
return LayoutUnit { geometryProperty.value() };
}
Optional<LayoutUnit> FormattingContext::Geometry::computedMaxHeight(const Box& layoutBox, Optional<LayoutUnit> containingBlockHeight) const
{
return computedHeightValue(layoutBox, HeightType::Max, containingBlockHeight);
}
Optional<LayoutUnit> FormattingContext::Geometry::computedMinHeight(const Box& layoutBox, Optional<LayoutUnit> containingBlockHeight) const
{
return computedHeightValue(layoutBox, HeightType::Min, containingBlockHeight);
}
Optional<LayoutUnit> FormattingContext::Geometry::computedMinWidth(const Box& layoutBox, LayoutUnit containingBlockWidth)
{
return computedWidthValue(layoutBox, WidthType::Min, containingBlockWidth);
}
Optional<LayoutUnit> FormattingContext::Geometry::computedMaxWidth(const Box& layoutBox, LayoutUnit containingBlockWidth)
{
return computedWidthValue(layoutBox, WidthType::Max, containingBlockWidth);
}
LayoutUnit FormattingContext::Geometry::staticVerticalPositionForOutOfFlowPositioned(const Box& layoutBox, const VerticalConstraints& verticalConstraints) const
{
ASSERT(layoutBox.isOutOfFlowPositioned());
auto& formattingContext = this->formattingContext();
LayoutUnit top;
if (layoutBox.previousInFlowSibling() && layoutBox.previousInFlowSibling()->isBlockLevelBox()) {
auto& previousInFlowSibling = *layoutBox.previousInFlowSibling();
auto& previousInFlowBoxGeometry = formattingContext.geometryForBox(previousInFlowSibling, EscapeReason::OutOfFlowBoxNeedsInFlowGeometry);
auto& formattingState = downcast<BlockFormattingState>(layoutState().formattingStateForBox(previousInFlowSibling));
auto usedVerticalMarginForPreviousBox = formattingState.usedVerticalMargin(previousInFlowSibling);
top += BoxGeometry::borderBoxRect(previousInFlowBoxGeometry).bottom() + usedVerticalMarginForPreviousBox.nonCollapsedValues.after;
} else
top = formattingContext.geometryForBox(layoutBox.parent(), EscapeReason::OutOfFlowBoxNeedsInFlowGeometry).contentBoxTop();
auto& containingBlock = layoutBox.containingBlock();
for (auto* ancestor = &layoutBox.parent(); ancestor != &containingBlock; ancestor = &ancestor->containingBlock()) {
auto& boxGeometry = formattingContext.geometryForBox(*ancestor, EscapeReason::OutOfFlowBoxNeedsInFlowGeometry);
top += BoxGeometry::borderBoxTop(boxGeometry);
ASSERT(!ancestor->isPositioned() || layoutBox.isFixedPositioned());
}
return top - verticalConstraints.logicalTop;
}
LayoutUnit FormattingContext::Geometry::staticHorizontalPositionForOutOfFlowPositioned(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
{
ASSERT(layoutBox.isOutOfFlowPositioned());
auto& formattingContext = this->formattingContext();
auto left = formattingContext.geometryForBox(layoutBox.parent(), EscapeReason::OutOfFlowBoxNeedsInFlowGeometry).contentBoxLeft();
auto& containingBlock = layoutBox.containingBlock();
for (auto* ancestor = &layoutBox.parent(); ancestor != &containingBlock; ancestor = &ancestor->containingBlock()) {
auto& boxGeometry = formattingContext.geometryForBox(*ancestor, EscapeReason::OutOfFlowBoxNeedsInFlowGeometry);
left += BoxGeometry::borderBoxLeft(boxGeometry);
ASSERT(!ancestor->isPositioned() || layoutBox.isFixedPositioned());
}
return left - horizontalConstraints.logicalLeft;
}
LayoutUnit FormattingContext::Geometry::shrinkToFitWidth(const Box& formattingRoot, LayoutUnit availableWidth)
{
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width] -> shrink to fit -> unsupported -> width(" << LayoutUnit { } << "px) layoutBox: " << &formattingRoot << ")");
ASSERT(formattingRoot.establishesFormattingContext());
auto intrinsicWidthConstraints = IntrinsicWidthConstraints { };
if (is<ContainerBox>(formattingRoot) && downcast<ContainerBox>(formattingRoot).hasInFlowOrFloatingChild()) {
auto& root = downcast<ContainerBox>(formattingRoot);
auto& formattingStateForRoot = layoutState().ensureFormattingState(root);
auto precomputedIntrinsicWidthConstraints = formattingStateForRoot.intrinsicWidthConstraints();
if (!precomputedIntrinsicWidthConstraints)
intrinsicWidthConstraints = LayoutContext::createFormattingContext(root, layoutState())->computedIntrinsicWidthConstraints();
else
intrinsicWidthConstraints = *precomputedIntrinsicWidthConstraints;
}
return std::min(std::max(intrinsicWidthConstraints.minimum, availableWidth), intrinsicWidthConstraints.maximum);
}
VerticalGeometry FormattingContext::Geometry::outOfFlowNonReplacedVerticalGeometry(const ContainerBox& layoutBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverriddenVerticalValues& overriddenVerticalValues) const
{
ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.isReplacedBox());
ASSERT(verticalConstraints.logicalHeight);
auto& formattingContext = this->formattingContext();
auto& style = layoutBox.style();
auto& boxGeometry = formattingContext.geometryForBox(layoutBox);
auto containingBlockHeight = *verticalConstraints.logicalHeight;
auto containingBlockWidth = horizontalConstraints.logicalWidth;
auto top = computedValue(style.logicalTop(), containingBlockWidth);
auto bottom = computedValue(style.logicalBottom(), containingBlockWidth);
auto height = overriddenVerticalValues.height ? overriddenVerticalValues.height.value() : computedHeight(layoutBox, containingBlockHeight);
auto computedVerticalMargin = Geometry::computedVerticalMargin(layoutBox, horizontalConstraints);
UsedVerticalMargin::NonCollapsedValues usedVerticalMargin;
auto paddingTop = boxGeometry.paddingTop().valueOr(0);
auto paddingBottom = boxGeometry.paddingBottom().valueOr(0);
auto borderTop = boxGeometry.borderTop();
auto borderBottom = boxGeometry.borderBottom();
if (!top && !height && !bottom)
top = staticVerticalPositionForOutOfFlowPositioned(layoutBox, verticalConstraints);
if (top && height && bottom) {
if (!computedVerticalMargin.before && !computedVerticalMargin.after) {
auto marginBeforeAndAfter = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
usedVerticalMargin = { marginBeforeAndAfter / 2, marginBeforeAndAfter / 2 };
} else if (!computedVerticalMargin.before) {
usedVerticalMargin.after = *computedVerticalMargin.after;
usedVerticalMargin.before = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom);
} else if (!computedVerticalMargin.after) {
usedVerticalMargin.before = *computedVerticalMargin.before;
usedVerticalMargin.after = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
} else
usedVerticalMargin = { *computedVerticalMargin.before, *computedVerticalMargin.after };
auto boxHeight = *top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom;
if (boxHeight != containingBlockHeight)
bottom = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after);
}
if (!top && !height && bottom) {
height = contentHeightForFormattingContextRoot(layoutBox);
usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
top = containingBlockHeight - (usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom);
}
if (!top && !bottom && height) {
top = staticVerticalPositionForOutOfFlowPositioned(layoutBox, verticalConstraints);
usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
bottom = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after);
}
if (!height && !bottom && top) {
height = contentHeightForFormattingContextRoot(layoutBox);
usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
bottom = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after);
}
if (!top && height && bottom) {
usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
top = containingBlockHeight - (usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom);
}
if (!height && top && bottom) {
usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
height = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom);
}
if (!bottom && top && height) {
usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
bottom = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after);
}
ASSERT(top);
ASSERT(bottom);
ASSERT(height);
auto containingBlockPaddingVerticalEdge = verticalConstraints.logicalTop;
*top += containingBlockPaddingVerticalEdge;
*bottom += containingBlockPaddingVerticalEdge;
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow non-replaced -> top(" << *top << "px) bottom(" << *bottom << "px) height(" << *height << "px) margin(" << usedVerticalMargin.before << "px, " << usedVerticalMargin.after << "px) layoutBox(" << &layoutBox << ")");
return { *top, *bottom, { *height, usedVerticalMargin } };
}
HorizontalGeometry FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry(const ContainerBox& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
{
ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.isReplacedBox());
auto& formattingContext = this->formattingContext();
auto& style = layoutBox.style();
auto& boxGeometry = formattingContext.geometryForBox(layoutBox);
auto containingBlockWidth = horizontalConstraints.logicalWidth;
auto isLeftToRightDirection = layoutBox.containingBlock().style().isLeftToRightDirection();
auto left = computedValue(style.logicalLeft(), containingBlockWidth);
auto right = computedValue(style.logicalRight(), containingBlockWidth);
auto width = overriddenHorizontalValues.width ? overriddenHorizontalValues.width : computedWidth(layoutBox, containingBlockWidth);
auto computedHorizontalMargin = Geometry::computedHorizontalMargin(layoutBox, horizontalConstraints);
UsedHorizontalMargin usedHorizontalMargin;
auto paddingLeft = boxGeometry.paddingLeft().valueOr(0);
auto paddingRight = boxGeometry.paddingRight().valueOr(0);
auto borderLeft = boxGeometry.borderLeft();
auto borderRight = boxGeometry.borderRight();
if (!left && !width && !right) {
usedHorizontalMargin = { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutBox, horizontalConstraints);
if (isLeftToRightDirection)
left = staticHorizontalPosition;
else
right = staticHorizontalPosition;
} else if (left && width && right) {
if (!computedHorizontalMargin.start && !computedHorizontalMargin.end) {
auto marginStartAndEnd = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
if (marginStartAndEnd >= 0)
usedHorizontalMargin = { marginStartAndEnd / 2, marginStartAndEnd / 2 };
else {
if (isLeftToRightDirection) {
usedHorizontalMargin.start = 0_lu;
usedHorizontalMargin.end = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
} else {
usedHorizontalMargin.end = 0_lu;
usedHorizontalMargin.start = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
}
}
} else if (!computedHorizontalMargin.start) {
usedHorizontalMargin.end = *computedHorizontalMargin.end;
usedHorizontalMargin.start = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
} else if (!computedHorizontalMargin.end) {
usedHorizontalMargin.start = *computedHorizontalMargin.start;
usedHorizontalMargin.end = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
} else {
usedHorizontalMargin = { *computedHorizontalMargin.start, *computedHorizontalMargin.end };
if (isLeftToRightDirection)
right = containingBlockWidth - (usedHorizontalMargin.start + *left + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end);
else
left = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
}
} else {
usedHorizontalMargin = { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
}
if (!left && !width && right) {
left = LayoutUnit { 0 };
auto availableWidth = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end + *right);
width = shrinkToFitWidth(layoutBox, availableWidth);
left = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
} else if (!left && !right && width) {
auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutBox, horizontalConstraints);
if (isLeftToRightDirection) {
left = staticHorizontalPosition;
right = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end);
} else {
right = staticHorizontalPosition;
left = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
}
} else if (!width && !right && left) {
right = LayoutUnit { 0 };
auto availableWidth = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end + *right);
width = shrinkToFitWidth(layoutBox, availableWidth);
right = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end);
} else if (!left && width && right) {
left = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
} else if (!width && left && right) {
width = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end + *right);
} else if (!right && left && width) {
right = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end);
}
ASSERT(left);
ASSERT(right);
ASSERT(width);
auto containingBlockPaddingVerticalEdge = horizontalConstraints.logicalLeft;
*left += containingBlockPaddingVerticalEdge;
*right += containingBlockPaddingVerticalEdge;
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow non-replaced -> left(" << *left << "px) right(" << *right << "px) width(" << *width << "px) margin(" << usedHorizontalMargin.start << "px, " << usedHorizontalMargin.end << "px) layoutBox(" << &layoutBox << ")");
return { *left, *right, { *width, usedHorizontalMargin } };
}
VerticalGeometry FormattingContext::Geometry::outOfFlowReplacedVerticalGeometry(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverriddenVerticalValues& overriddenVerticalValues) const
{
ASSERT(replacedBox.isOutOfFlowPositioned());
ASSERT(verticalConstraints.logicalHeight);
auto& formattingContext = this->formattingContext();
auto& style = replacedBox.style();
auto& boxGeometry = formattingContext.geometryForBox(replacedBox);
auto containingBlockHeight = *verticalConstraints.logicalHeight;
auto containingBlockWidth = horizontalConstraints.logicalWidth;
auto top = computedValue(style.logicalTop(), containingBlockWidth);
auto bottom = computedValue(style.logicalBottom(), containingBlockWidth);
auto height = inlineReplacedContentHeightAndMargin(replacedBox, horizontalConstraints, verticalConstraints, overriddenVerticalValues).contentHeight;
auto computedVerticalMargin = Geometry::computedVerticalMargin(replacedBox, horizontalConstraints);
Optional<LayoutUnit> usedMarginBefore = computedVerticalMargin.before;
Optional<LayoutUnit> usedMarginAfter = computedVerticalMargin.after;
auto paddingTop = boxGeometry.paddingTop().valueOr(0);
auto paddingBottom = boxGeometry.paddingBottom().valueOr(0);
auto borderTop = boxGeometry.borderTop();
auto borderBottom = boxGeometry.borderBottom();
if (!top && !bottom) {
top = staticVerticalPositionForOutOfFlowPositioned(replacedBox, verticalConstraints);
}
if (!bottom) {
usedMarginBefore = computedVerticalMargin.before.valueOr(0);
usedMarginAfter = usedMarginBefore;
}
if (!usedMarginBefore && !usedMarginAfter) {
auto marginBeforeAndAfter = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
usedMarginBefore = marginBeforeAndAfter / 2;
usedMarginAfter = usedMarginBefore;
}
if (!top)
top = containingBlockHeight - (*usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter + *bottom);
if (!bottom)
bottom = containingBlockHeight - (*top + *usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter);
if (!usedMarginBefore)
usedMarginBefore = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter + *bottom);
if (!usedMarginAfter)
usedMarginAfter = containingBlockHeight - (*top + *usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
auto boxHeight = *top + *usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter + *bottom;
if (boxHeight > containingBlockHeight)
bottom = containingBlockHeight - (*top + *usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter);
auto containingBlockPaddingVerticalEdge = verticalConstraints.logicalTop;
*top += containingBlockPaddingVerticalEdge;
*bottom += containingBlockPaddingVerticalEdge;
ASSERT(top);
ASSERT(bottom);
ASSERT(usedMarginBefore);
ASSERT(usedMarginAfter);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow replaced -> top(" << *top << "px) bottom(" << *bottom << "px) height(" << height << "px) margin(" << *usedMarginBefore << "px, " << *usedMarginAfter << "px) layoutBox(" << &replacedBox << ")");
return { *top, *bottom, { height, { *usedMarginBefore, *usedMarginAfter } } };
}
HorizontalGeometry FormattingContext::Geometry::outOfFlowReplacedHorizontalGeometry(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
{
ASSERT(replacedBox.isOutOfFlowPositioned());
auto& formattingContext = this->formattingContext();
auto& style = replacedBox.style();
auto& boxGeometry = formattingContext.geometryForBox(replacedBox);
auto containingBlockWidth = horizontalConstraints.logicalWidth;
auto isLeftToRightDirection = replacedBox.containingBlock().style().isLeftToRightDirection();
auto left = computedValue(style.logicalLeft(), containingBlockWidth);
auto right = computedValue(style.logicalRight(), containingBlockWidth);
auto computedHorizontalMargin = Geometry::computedHorizontalMargin(replacedBox, horizontalConstraints);
Optional<LayoutUnit> usedMarginStart = computedHorizontalMargin.start;
Optional<LayoutUnit> usedMarginEnd = computedHorizontalMargin.end;
auto width = inlineReplacedContentWidthAndMargin(replacedBox, horizontalConstraints, verticalConstraints, overriddenHorizontalValues).contentWidth;
auto paddingLeft = boxGeometry.paddingLeft().valueOr(0);
auto paddingRight = boxGeometry.paddingRight().valueOr(0);
auto borderLeft = boxGeometry.borderLeft();
auto borderRight = boxGeometry.borderRight();
if (!left && !right) {
auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(replacedBox, horizontalConstraints);
if (isLeftToRightDirection)
left = staticHorizontalPosition;
else
right = staticHorizontalPosition;
}
if (!left || !right) {
usedMarginStart = computedHorizontalMargin.start.valueOr(0);
usedMarginEnd = computedHorizontalMargin.end.valueOr(0);
}
if (!usedMarginStart && !usedMarginEnd) {
auto marginStartAndEnd = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
if (marginStartAndEnd >= 0) {
usedMarginStart = marginStartAndEnd / 2;
usedMarginEnd = usedMarginStart;
} else {
if (isLeftToRightDirection) {
usedMarginStart = 0_lu;
usedMarginEnd = containingBlockWidth - (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
} else {
usedMarginEnd = 0_lu;
usedMarginStart = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
}
}
}
if (!left)
left = containingBlockWidth - (*usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
if (!right)
right = containingBlockWidth - (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd);
if (!usedMarginStart)
usedMarginStart = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
if (!usedMarginEnd)
usedMarginEnd = containingBlockWidth - (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
auto boxWidth = (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
if (boxWidth > containingBlockWidth) {
if (isLeftToRightDirection)
right = containingBlockWidth - (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd);
else
left = containingBlockWidth - (*usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
}
ASSERT(left);
ASSERT(right);
ASSERT(usedMarginStart);
ASSERT(usedMarginEnd);
auto containingBlockPaddingVerticalEdge = horizontalConstraints.logicalLeft;
*left += containingBlockPaddingVerticalEdge;
*right += containingBlockPaddingVerticalEdge;
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow replaced -> left(" << *left << "px) right(" << *right << "px) width(" << width << "px) margin(" << *usedMarginStart << "px, " << *usedMarginEnd << "px) layoutBox(" << &replacedBox << ")");
return { *left, *right, { width, { *usedMarginStart, *usedMarginEnd } } };
}
ContentHeightAndMargin FormattingContext::Geometry::complicatedCases(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenVerticalValues& overriddenVerticalValues) const
{
ASSERT(!layoutBox.isReplacedBox());
ASSERT((layoutBox.isBlockLevelBox() && layoutBox.isInFlow() && !layoutBox.isOverflowVisible()) || layoutBox.isInlineBlockBox() || layoutBox.isFloatingPositioned() || layoutBox.isDocumentBox() || layoutBox.isTableBox());
auto height = overriddenVerticalValues.height ? overriddenVerticalValues.height.value() : computedHeight(layoutBox);
auto computedVerticalMargin = Geometry::computedVerticalMargin(layoutBox, horizontalConstraints);
auto usedVerticalMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
if (!height) {
ASSERT(isHeightAuto(layoutBox));
if (!is<ContainerBox>(layoutBox) || !downcast<ContainerBox>(layoutBox).hasInFlowOrFloatingChild())
height = 0_lu;
else if (layoutBox.isDocumentBox() && !layoutBox.establishesFormattingContext()) {
auto& documentBox = downcast<ContainerBox>(layoutBox);
auto top = BoxGeometry::marginBoxRect(formattingContext().geometryForBox(*documentBox.firstInFlowChild())).top();
auto bottom = BoxGeometry::marginBoxRect(formattingContext().geometryForBox(*documentBox.lastInFlowChild())).bottom();
auto& initialContainingBlock = documentBox.formattingContextRoot();
auto floatingContext = FloatingContext { formattingContext(), layoutState().establishedFormattingState(initialContainingBlock).floatingState() };
if (auto floatBottom = floatingContext.bottom()) {
bottom = std::max<LayoutUnit>(*floatBottom, bottom);
auto floatTop = floatingContext.top();
ASSERT(floatTop);
top = std::min<LayoutUnit>(*floatTop, top);
}
height = bottom - top;
} else {
ASSERT(layoutBox.establishesFormattingContext());
height = contentHeightForFormattingContextRoot(downcast<ContainerBox>(layoutBox));
}
}
ASSERT(height);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating non-replaced -> height(" << *height << "px) margin(" << usedVerticalMargin.before << "px, " << usedVerticalMargin.after << "px) -> layoutBox(" << &layoutBox << ")");
return ContentHeightAndMargin { *height, usedVerticalMargin };
}
ContentWidthAndMargin FormattingContext::Geometry::floatingNonReplacedContentWidthAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
{
ASSERT(layoutBox.isFloatingPositioned() && !layoutBox.isReplacedBox());
auto computedHorizontalMargin = Geometry::computedHorizontalMargin(layoutBox, horizontalConstraints);
auto usedHorizontallMargin = UsedHorizontalMargin { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
auto width = overriddenHorizontalValues.width ? overriddenHorizontalValues.width : computedWidth(layoutBox, horizontalConstraints.logicalWidth);
if (!width)
width = shrinkToFitWidth(layoutBox, horizontalConstraints.logicalWidth);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> floating non-replaced -> width(" << *width << "px) margin(" << usedHorizontallMargin.start << "px, " << usedHorizontallMargin.end << "px) -> layoutBox(" << &layoutBox << ")");
return ContentWidthAndMargin { *width, usedHorizontallMargin };
}
ContentHeightAndMargin FormattingContext::Geometry::floatingReplacedContentHeightAndMargin(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, const OverriddenVerticalValues& overriddenVerticalValues) const
{
ASSERT(replacedBox.isFloatingPositioned());
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
return inlineReplacedContentHeightAndMargin(replacedBox, horizontalConstraints, { }, overriddenVerticalValues);
}
ContentWidthAndMargin FormattingContext::Geometry::floatingReplacedContentWidthAndMargin(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
{
ASSERT(replacedBox.isFloatingPositioned());
auto computedHorizontalMargin = Geometry::computedHorizontalMargin(replacedBox, horizontalConstraints);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
auto usedMargin = UsedHorizontalMargin { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
return inlineReplacedContentWidthAndMargin(replacedBox, horizontalConstraints, { }, { overriddenHorizontalValues.width, usedMargin });
}
VerticalGeometry FormattingContext::Geometry::outOfFlowVerticalGeometry(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverriddenVerticalValues& overriddenVerticalValues) const
{
ASSERT(layoutBox.isOutOfFlowPositioned());
if (!layoutBox.isReplacedBox())
return outOfFlowNonReplacedVerticalGeometry(downcast<ContainerBox>(layoutBox), horizontalConstraints, verticalConstraints, overriddenVerticalValues);
return outOfFlowReplacedVerticalGeometry(downcast<ReplacedBox>(layoutBox), horizontalConstraints, verticalConstraints, overriddenVerticalValues);
}
HorizontalGeometry FormattingContext::Geometry::outOfFlowHorizontalGeometry(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
{
ASSERT(layoutBox.isOutOfFlowPositioned());
if (!layoutBox.isReplacedBox())
return outOfFlowNonReplacedHorizontalGeometry(downcast<ContainerBox>(layoutBox), horizontalConstraints, overriddenHorizontalValues);
return outOfFlowReplacedHorizontalGeometry(downcast<ReplacedBox>(layoutBox), horizontalConstraints, verticalConstraints, overriddenHorizontalValues);
}
ContentHeightAndMargin FormattingContext::Geometry::floatingContentHeightAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenVerticalValues& overriddenVerticalValues) const
{
ASSERT(layoutBox.isFloatingPositioned());
if (!layoutBox.isReplacedBox())
return complicatedCases(layoutBox, horizontalConstraints, overriddenVerticalValues);
return floatingReplacedContentHeightAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, overriddenVerticalValues);
}
ContentWidthAndMargin FormattingContext::Geometry::floatingContentWidthAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
{
ASSERT(layoutBox.isFloatingPositioned());
if (!layoutBox.isReplacedBox())
return floatingNonReplacedContentWidthAndMargin(layoutBox, horizontalConstraints, overriddenHorizontalValues);
return floatingReplacedContentWidthAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, overriddenHorizontalValues);
}
ContentHeightAndMargin FormattingContext::Geometry::inlineReplacedContentHeightAndMargin(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, Optional<VerticalConstraints> verticalConstraints, const OverriddenVerticalValues& overriddenVerticalValues) const
{
auto& formattingContext = this->formattingContext();
auto computedVerticalMargin = Geometry::computedVerticalMargin(replacedBox, horizontalConstraints);
auto usedVerticalMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
auto& style = replacedBox.style();
auto height = overriddenVerticalValues.height ? overriddenVerticalValues.height.value() : computedHeight(replacedBox, verticalConstraints ? verticalConstraints->logicalHeight : WTF::nullopt);
auto heightIsAuto = !overriddenVerticalValues.height && isHeightAuto(replacedBox);
auto widthIsAuto = style.logicalWidth().isAuto();
if (heightIsAuto && widthIsAuto && replacedBox.hasIntrinsicHeight()) {
height = replacedBox.intrinsicHeight();
} else if (heightIsAuto && replacedBox.hasIntrinsicRatio()) {
auto usedWidth = formattingContext.geometryForBox(replacedBox).contentBoxWidth();
height = usedWidth / replacedBox.intrinsicRatio();
} else if (heightIsAuto && replacedBox.hasIntrinsicHeight()) {
height = replacedBox.intrinsicHeight();
} else if (heightIsAuto) {
height = { 150 };
}
ASSERT(height);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow replaced -> height(" << *height << "px) margin(" << usedVerticalMargin.before << "px, " << usedVerticalMargin.after << "px) -> layoutBox(" << &replacedBox << ")");
return { *height, usedVerticalMargin };
}
ContentWidthAndMargin FormattingContext::Geometry::inlineReplacedContentWidthAndMargin(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, Optional<VerticalConstraints> verticalConstraints, const OverriddenHorizontalValues& overriddenHorizontalValues)
{
auto computedHorizontalMargin = Geometry::computedHorizontalMargin(replacedBox, horizontalConstraints);
auto usedMarginStart = [&] {
if (overriddenHorizontalValues.margin)
return overriddenHorizontalValues.margin->start;
return computedHorizontalMargin.start.valueOr(0_lu);
};
auto usedMarginEnd = [&] {
if (overriddenHorizontalValues.margin)
return overriddenHorizontalValues.margin->end;
return computedHorizontalMargin.end.valueOr(0_lu);
};
auto width = overriddenHorizontalValues.width ? overriddenHorizontalValues.width : computedWidth(replacedBox, horizontalConstraints.logicalWidth);
auto heightIsAuto = isHeightAuto(replacedBox);
auto height = computedHeight(replacedBox, verticalConstraints ? verticalConstraints->logicalHeight : WTF::nullopt);
if (!width && heightIsAuto && replacedBox.hasIntrinsicWidth()) {
width = replacedBox.intrinsicWidth();
} else if ((!width && heightIsAuto && !replacedBox.hasIntrinsicWidth() && replacedBox.hasIntrinsicHeight() && replacedBox.hasIntrinsicRatio())
|| (!width && height && replacedBox.hasIntrinsicRatio())) {
width = height.valueOr(replacedBox.hasIntrinsicHeight()) * replacedBox.intrinsicRatio();
} else if (!width && heightIsAuto && replacedBox.hasIntrinsicRatio() && !replacedBox.hasIntrinsicWidth() && !replacedBox.hasIntrinsicHeight()) {
ASSERT_NOT_IMPLEMENTED_YET();
} else if (!width && replacedBox.hasIntrinsicWidth()) {
width = replacedBox.intrinsicWidth();
} else if (!width) {
width = { 300 };
}
ASSERT(width);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow replaced -> width(" << *width << "px) margin(" << usedMarginStart() << "px, " << usedMarginEnd() << "px) -> layoutBox(" << &replacedBox << ")");
return { *width, { usedMarginStart(), usedMarginEnd() } };
}
LayoutSize FormattingContext::Geometry::inFlowPositionedPositionOffset(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
{
ASSERT(layoutBox.isInFlowPositioned());
auto& style = layoutBox.style();
auto containingBlockWidth = horizontalConstraints.logicalWidth;
auto top = computedValue(style.logicalTop(), containingBlockWidth);
auto bottom = computedValue(style.logicalBottom(), containingBlockWidth);
if (!top && !bottom) {
top = bottom = { 0 };
} else if (!top) {
top = -*bottom;
} else if (!bottom) {
bottom = -*top;
} else {
bottom = WTF::nullopt;
}
auto left = computedValue(style.logicalLeft(), containingBlockWidth);
auto right = computedValue(style.logicalRight(), containingBlockWidth);
if (!left && !right) {
left = right = { 0 };
} else if (!left) {
left = -*right;
} else if (!right) {
right = -*left;
} else {
auto isLeftToRightDirection = layoutBox.containingBlock().style().isLeftToRightDirection();
if (isLeftToRightDirection)
right = -*left;
else
left = WTF::nullopt;
}
ASSERT(!bottom || *top == -*bottom);
ASSERT(!left || *left == -*right);
auto topPositionOffset = *top;
auto leftPositionOffset = left.valueOr(-*right);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position] -> positioned inflow -> top offset(" << topPositionOffset << "px) left offset(" << leftPositionOffset << "px) layoutBox(" << &layoutBox << ")");
return { leftPositionOffset, topPositionOffset };
}
inline static WritingMode usedWritingMode(const Box& layoutBox)
{
return layoutBox.isInlineLevelBox() ? layoutBox.parent().style().writingMode() : layoutBox.containingBlock().style().writingMode();
}
Edges FormattingContext::Geometry::computedBorder(const Box& layoutBox) const
{
auto& style = layoutBox.style();
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Border] -> layoutBox: " << &layoutBox);
return {
{ LayoutUnit(style.borderLeftWidth()), LayoutUnit(style.borderRightWidth()) },
{ LayoutUnit(style.borderTopWidth()), LayoutUnit(style.borderBottomWidth()) }
};
}
Optional<Edges> FormattingContext::Geometry::computedPadding(const Box& layoutBox, const LayoutUnit containingBlockWidth) const
{
if (!layoutBox.isPaddingApplicable())
return WTF::nullopt;
auto& style = layoutBox.style();
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Padding] -> layoutBox: " << &layoutBox);
return Edges {
{ valueForLength(style.paddingLeft(), containingBlockWidth), valueForLength(style.paddingRight(), containingBlockWidth) },
{ valueForLength(style.paddingTop(), containingBlockWidth), valueForLength(style.paddingBottom(), containingBlockWidth) }
};
}
ComputedHorizontalMargin FormattingContext::Geometry::computedHorizontalMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
{
auto& style = layoutBox.style();
auto containingBlockWidth = horizontalConstraints.logicalWidth;
if (isHorizontalWritingMode(usedWritingMode(layoutBox)))
return { computedValue(style.marginLeft(), containingBlockWidth), computedValue(style.marginRight(), containingBlockWidth) };
return { computedValue(style.marginTop(), containingBlockWidth), computedValue(style.marginBottom(), containingBlockWidth) };
}
ComputedVerticalMargin FormattingContext::Geometry::computedVerticalMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
{
auto& style = layoutBox.style();
auto containingBlockWidth = horizontalConstraints.logicalWidth;
if (isHorizontalWritingMode(usedWritingMode(layoutBox)))
return { computedValue(style.marginTop(), containingBlockWidth), computedValue(style.marginBottom(), containingBlockWidth) };
return { computedValue(style.marginLeft(), containingBlockWidth), computedValue(style.marginRight(), containingBlockWidth) };
}
FormattingContext::IntrinsicWidthConstraints FormattingContext::Geometry::constrainByMinMaxWidth(const Box& layoutBox, IntrinsicWidthConstraints intrinsicWidth) const
{
auto& style = layoutBox.style();
auto minWidth = fixedValue(style.logicalMinWidth());
auto maxWidth = fixedValue(style.logicalMaxWidth());
if (!minWidth && !maxWidth)
return intrinsicWidth;
if (maxWidth) {
intrinsicWidth.minimum = std::min(*maxWidth, intrinsicWidth.minimum);
intrinsicWidth.maximum = std::min(*maxWidth, intrinsicWidth.maximum);
}
if (minWidth) {
intrinsicWidth.minimum = std::max(*minWidth, intrinsicWidth.minimum);
intrinsicWidth.maximum = std::max(*minWidth, intrinsicWidth.maximum);
}
ASSERT(intrinsicWidth.minimum <= intrinsicWidth.maximum);
return intrinsicWidth;
}
FormattingContext::ConstraintsForOutOfFlowContent FormattingContext::Geometry::constraintsForOutOfFlowContent(const ContainerBox& containerBox)
{
auto& boxGeometry = formattingContext().geometryForBox(containerBox);
return {
{ boxGeometry.paddingBoxLeft(), boxGeometry.paddingBoxWidth() },
{ boxGeometry.paddingBoxTop(), boxGeometry.paddingBoxHeight() },
boxGeometry.contentBoxWidth() };
}
FormattingContext::ConstraintsForInFlowContent FormattingContext::Geometry::constraintsForInFlowContent(const ContainerBox& containerBox, Optional<EscapeReason> escapeReason)
{
auto& boxGeometry = formattingContext().geometryForBox(containerBox, escapeReason);
return { { boxGeometry.contentBoxLeft(), boxGeometry.contentBoxWidth() }, { boxGeometry.contentBoxTop(), computedHeight(containerBox) } };
}
}
}
#endif