InlineFormattingContext.cpp [plain text]
#include "config.h"
#include "InlineFormattingContext.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "FloatingContext.h"
#include "FontCascade.h"
#include "InlineFormattingState.h"
#include "InlineLineBox.h"
#include "InlineLineRun.h"
#include "InlineTextItem.h"
#include "InvalidationState.h"
#include "LayoutBox.h"
#include "LayoutContainerBox.h"
#include "LayoutContext.h"
#include "LayoutInitialContainingBlock.h"
#include "LayoutInlineTextBox.h"
#include "LayoutLineBreakBox.h"
#include "LayoutReplacedBox.h"
#include "LayoutState.h"
#include "Logging.h"
#include "RuntimeEnabledFeatures.h"
#include "TextUtil.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/text/TextStream.h>
namespace WebCore {
namespace Layout {
WTF_MAKE_ISO_ALLOCATED_IMPL(InlineFormattingContext);
InlineFormattingContext::InlineFormattingContext(const ContainerBox& formattingContextRoot, InlineFormattingState& formattingState)
: FormattingContext(formattingContextRoot, formattingState)
{
}
static inline const Box* nextInlineLevelBoxToLayout(const Box& layoutBox, const ContainerBox& stayWithin)
{
if (layoutBox.isInlineBox()) {
if (is<ContainerBox>(layoutBox) && downcast<ContainerBox>(layoutBox).hasInFlowOrFloatingChild()) {
ASSERT(!layoutBox.isInlineTextBox() && !layoutBox.isLineBreakBox());
return downcast<ContainerBox>(layoutBox).firstInFlowOrFloatingChild();
}
}
for (auto* nextInPreOrder = &layoutBox; nextInPreOrder && nextInPreOrder != &stayWithin; nextInPreOrder = &nextInPreOrder->parent()) {
if (auto* nextSibling = nextInPreOrder->nextInFlowOrFloatingSibling())
return nextSibling;
}
return nullptr;
}
void InlineFormattingContext::layoutInFlowContent(InvalidationState& invalidationState, const ConstraintsForInFlowContent& constraints)
{
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> inline formatting context -> formatting root(" << &root() << ")");
ASSERT(root().hasInFlowOrFloatingChild());
invalidateFormattingState(invalidationState);
auto* layoutBox = root().firstInFlowOrFloatingChild();
while (layoutBox) {
ASSERT(layoutBox->isInlineLevelBox() || layoutBox->isFloatingPositioned());
if (layoutBox->isAtomicInlineLevelBox() || layoutBox->isFloatingPositioned()) {
if (is<ContainerBox>(layoutBox) && layoutBox->establishesFormattingContext()) {
ASSERT(layoutBox->isInlineBlockBox() || layoutBox->isInlineTableBox() || layoutBox->isFloatingPositioned());
auto& formattingRoot = downcast<ContainerBox>(*layoutBox);
computeBorderAndPadding(formattingRoot, constraints.horizontal);
computeWidthAndMargin(formattingRoot, constraints.horizontal);
if (formattingRoot.hasChild()) {
auto formattingContext = LayoutContext::createFormattingContext(formattingRoot, layoutState());
if (formattingRoot.hasInFlowOrFloatingChild())
formattingContext->layoutInFlowContent(invalidationState, geometry().constraintsForInFlowContent(formattingRoot));
computeHeightAndMargin(formattingRoot, constraints.horizontal);
formattingContext->layoutOutOfFlowContent(invalidationState, geometry().constraintsForOutOfFlowContent(formattingRoot));
} else
computeHeightAndMargin(formattingRoot, constraints.horizontal);
} else {
computeBorderAndPadding(*layoutBox, constraints.horizontal);
computeWidthAndMargin(*layoutBox, constraints.horizontal);
computeHeightAndMargin(*layoutBox, constraints.horizontal);
}
} else if (layoutBox->isLineBreakBox()) {
auto& boxGeometry = formattingState().boxGeometry(*layoutBox);
boxGeometry.setHorizontalMargin({ });
boxGeometry.setBorder({ });
boxGeometry.setPadding({ });
boxGeometry.setContentBoxWidth({ });
boxGeometry.setVerticalMargin({ });
} else if (layoutBox->isInlineBox()) {
if (!layoutBox->isInlineTextBox()) {
computeBorderAndPadding(*layoutBox, constraints.horizontal);
computeHorizontalMargin(*layoutBox, constraints.horizontal);
formattingState().boxGeometry(*layoutBox).setVerticalMargin({ });
}
} else
ASSERT_NOT_REACHED();
layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
}
collectInlineContentIfNeeded();
auto& inlineItems = formattingState().inlineItems();
lineLayout(inlineItems, { 0, inlineItems.size() }, constraints);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root() << ")");
}
void InlineFormattingContext::lineLayoutForIntergration(InvalidationState& invalidationState, const ConstraintsForInFlowContent& constraints)
{
invalidateFormattingState(invalidationState);
collectInlineContentIfNeeded();
auto& inlineItems = formattingState().inlineItems();
lineLayout(inlineItems, { 0, inlineItems.size() }, constraints);
}
LayoutUnit InlineFormattingContext::usedContentHeight() const
{
auto& lines = formattingState().lines();
ASSERT(!lines.isEmpty());
auto top = LayoutUnit { lines.first().lineBoxLogicalRect().top() };
auto bottom = LayoutUnit { lines.last().lineBoxLogicalRect().bottom() + formattingState().clearGapAfterLastLine() };
auto floatingContext = FloatingContext { *this, formattingState().floatingState() };
if (auto floatBottom = floatingContext.bottom()) {
bottom = std::max(*floatBottom, bottom);
top = std::min(*floatingContext.top(), top);
}
return bottom - top;
}
void InlineFormattingContext::lineLayout(InlineItems& inlineItems, LineBuilder::InlineItemRange needsLayoutRange, const ConstraintsForInFlowContent& constraints)
{
auto& formattingState = this->formattingState();
formattingState.lineRuns().reserveInitialCapacity(formattingState.inlineItems().size());
InlineLayoutUnit lineLogicalTop = constraints.vertical.logicalTop;
struct PreviousLine {
LineBuilder::InlineItemRange range;
size_t overflowContentLength { 0 };
Optional<InlineLayoutUnit> overflowLogicalWidth;
};
Optional<PreviousLine> previousLine;
auto& floatingState = formattingState.floatingState();
auto floatingContext = FloatingContext { *this, floatingState };
auto isFirstLine = formattingState.lines().isEmpty();
auto lineBuilder = LineBuilder { *this, floatingState, constraints.horizontal, inlineItems };
while (!needsLayoutRange.isEmpty()) {
auto partialLeadingContentLength = previousLine ? previousLine->overflowContentLength : 0;
auto leadingLogicalWidth = previousLine ? previousLine->overflowLogicalWidth : WTF::nullopt;
auto initialLineConstraints = InlineRect { lineLogicalTop, constraints.horizontal.logicalLeft, constraints.horizontal.logicalWidth, quirks().initialLineHeight() };
auto lineContent = lineBuilder.layoutInlineContent(needsLayoutRange, partialLeadingContentLength, leadingLogicalWidth, initialLineConstraints, isFirstLine);
auto lineLogicalRect = computeGeometryForLineContent(lineContent, constraints.horizontal);
auto lineContentRange = lineContent.inlineItemRange;
if (!lineContentRange.isEmpty()) {
ASSERT(needsLayoutRange.start < lineContentRange.end);
isFirstLine = false;
lineLogicalTop = geometry().logicalTopForNextLine(lineContent, lineLogicalRect.bottom(), floatingContext);
if (lineContent.isLastLineWithInlineContent) {
formattingState.setClearGapAfterLastLine(lineLogicalTop - lineLogicalRect.bottom());
}
auto lastInlineItemNeedsPartialLayout = lineContent.partialTrailingContentLength;
if (lastInlineItemNeedsPartialLayout) {
auto lineLayoutHasAdvanced = !previousLine
|| lineContentRange.end > previousLine->range.end
|| (previousLine->overflowContentLength && previousLine->overflowContentLength > lineContent.partialTrailingContentLength);
if (!lineLayoutHasAdvanced) {
ASSERT_NOT_REACHED();
lastInlineItemNeedsPartialLayout = false;
}
}
needsLayoutRange.start = lastInlineItemNeedsPartialLayout ? lineContentRange.end - 1 : lineContentRange.end;
previousLine = PreviousLine { lineContentRange, lineContent.partialTrailingContentLength, lineContent.overflowLogicalWidth };
continue;
}
ASSERT(lineContent.runs.isEmpty());
ASSERT(lineContent.hasIntrusiveFloat);
auto floatConstraints = floatingContext.constraints(toLayoutUnit(lineLogicalTop), toLayoutUnit(lineLogicalRect.bottom()));
ASSERT(floatConstraints.left || floatConstraints.right);
static auto inifitePoint = PointInContextRoot::max();
lineLogicalTop = std::min(floatConstraints.left.valueOr(inifitePoint).y, floatConstraints.right.valueOr(inifitePoint).y);
ASSERT(lineLogicalTop < inifitePoint.y);
}
}
FormattingContext::IntrinsicWidthConstraints InlineFormattingContext::computedIntrinsicWidthConstraints()
{
auto& layoutState = this->layoutState();
ASSERT(!formattingState().intrinsicWidthConstraints());
if (!root().hasInFlowOrFloatingChild()) {
auto constraints = geometry().constrainByMinMaxWidth(root(), { });
formattingState().setIntrinsicWidthConstraints(constraints);
return constraints;
}
Vector<const Box*> formattingContextRootList;
auto horizontalConstraints = HorizontalConstraints { 0_lu, 0_lu };
auto* layoutBox = root().firstInFlowOrFloatingChild();
while (layoutBox) {
if (layoutBox->isInlineTextBox()) {
layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
continue;
}
if (layoutBox->isReplacedBox()) {
computeBorderAndPadding(*layoutBox, horizontalConstraints);
computeWidthAndMargin(*layoutBox, horizontalConstraints);
} else if (layoutBox->isFloatingPositioned() || layoutBox->isAtomicInlineLevelBox()) {
ASSERT(layoutBox->establishesFormattingContext());
formattingContextRootList.append(layoutBox);
computeBorderAndPadding(*layoutBox, horizontalConstraints);
computeHorizontalMargin(*layoutBox, horizontalConstraints);
computeIntrinsicWidthForFormattingRoot(*layoutBox);
} else if (layoutBox->isInlineBox()) {
computeBorderAndPadding(*layoutBox, horizontalConstraints);
computeHorizontalMargin(*layoutBox, horizontalConstraints);
} else
ASSERT_NOT_REACHED();
layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
}
collectInlineContentIfNeeded();
auto maximumLineWidth = [&](auto availableWidth) {
for (auto* formattingRoot : formattingContextRootList) {
auto intrinsicWidths = layoutState.formattingStateForBox(*formattingRoot).intrinsicWidthConstraintsForBox(*formattingRoot);
auto& boxGeometry = formattingState().boxGeometry(*formattingRoot);
auto contentWidth = (availableWidth ? intrinsicWidths->maximum : intrinsicWidths->minimum) - boxGeometry.horizontalMarginBorderAndPadding();
boxGeometry.setContentBoxWidth(contentWidth);
}
return computedIntrinsicWidthForConstraint(availableWidth);
};
auto minimumContentWidth = ceiledLayoutUnit(maximumLineWidth(0));
auto maximumContentWidth = ceiledLayoutUnit(maximumLineWidth(maxInlineLayoutUnit()));
auto constraints = geometry().constrainByMinMaxWidth(root(), { minimumContentWidth, maximumContentWidth });
formattingState().setIntrinsicWidthConstraints(constraints);
return constraints;
}
InlineLayoutUnit InlineFormattingContext::computedIntrinsicWidthForConstraint(InlineLayoutUnit availableWidth) const
{
auto& inlineItems = formattingState().inlineItems();
auto lineBuilder = LineBuilder { *this, inlineItems };
auto layoutRange = LineBuilder::InlineItemRange { 0 , inlineItems.size() };
auto maximumLineWidth = InlineLayoutUnit { };
auto maximumFloatWidth = LayoutUnit { };
while (!layoutRange.isEmpty()) {
auto intrinsicContent = lineBuilder.computedIntrinsicWidth(layoutRange, availableWidth);
layoutRange.start = intrinsicContent.inlineItemRange.end;
maximumLineWidth = std::max(maximumLineWidth, intrinsicContent.logicalWidth);
for (auto* floatBox : intrinsicContent.floats)
maximumFloatWidth += geometryForBox(*floatBox).marginBoxWidth();
}
return maximumLineWidth + maximumFloatWidth;
}
void InlineFormattingContext::computeIntrinsicWidthForFormattingRoot(const Box& formattingRoot)
{
ASSERT(formattingRoot.establishesFormattingContext());
auto constraints = IntrinsicWidthConstraints { };
if (auto fixedWidth = geometry().fixedValue(formattingRoot.style().logicalWidth()))
constraints = { *fixedWidth, *fixedWidth };
else if (is<ContainerBox>(formattingRoot) && downcast<ContainerBox>(formattingRoot).hasInFlowOrFloatingChild())
constraints = LayoutContext::createFormattingContext(downcast<ContainerBox>(formattingRoot), layoutState())->computedIntrinsicWidthConstraints();
constraints = geometry().constrainByMinMaxWidth(formattingRoot, constraints);
constraints.expand(geometryForBox(formattingRoot).horizontalMarginBorderAndPadding());
formattingState().setIntrinsicWidthConstraintsForBox(formattingRoot, constraints);
}
void InlineFormattingContext::computeHorizontalMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints)
{
auto computedHorizontalMargin = geometry().computedHorizontalMargin(layoutBox, horizontalConstraints);
formattingState().boxGeometry(layoutBox).setHorizontalMargin({ computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) });
}
void InlineFormattingContext::computeWidthAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints)
{
auto compute = [&](Optional<LayoutUnit> usedWidth) {
if (layoutBox.isFloatingPositioned())
return geometry().floatingContentWidthAndMargin(layoutBox, horizontalConstraints, { usedWidth, { } });
if (layoutBox.isInlineBlockBox())
return geometry().inlineBlockContentWidthAndMargin(layoutBox, horizontalConstraints, { usedWidth, { } });
if (layoutBox.isReplacedBox())
return geometry().inlineReplacedContentWidthAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, { }, { usedWidth, { } });
ASSERT_NOT_REACHED();
return ContentWidthAndMargin { };
};
auto contentWidthAndMargin = compute({ });
auto availableWidth = horizontalConstraints.logicalWidth;
if (auto maxWidth = geometry().computedMaxWidth(layoutBox, availableWidth)) {
auto maxWidthAndMargin = compute(maxWidth);
if (contentWidthAndMargin.contentWidth > maxWidthAndMargin.contentWidth)
contentWidthAndMargin = maxWidthAndMargin;
}
auto minWidth = geometry().computedMinWidth(layoutBox, availableWidth).valueOr(0);
auto minWidthAndMargin = compute(minWidth);
if (contentWidthAndMargin.contentWidth < minWidthAndMargin.contentWidth)
contentWidthAndMargin = minWidthAndMargin;
auto& boxGeometry = formattingState().boxGeometry(layoutBox);
boxGeometry.setContentBoxWidth(contentWidthAndMargin.contentWidth);
boxGeometry.setHorizontalMargin({ contentWidthAndMargin.usedMargin.start, contentWidthAndMargin.usedMargin.end });
}
void InlineFormattingContext::computeHeightAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints)
{
auto compute = [&](Optional<LayoutUnit> usedHeight) {
if (layoutBox.isFloatingPositioned())
return geometry().floatingContentHeightAndMargin(layoutBox, horizontalConstraints, { usedHeight });
if (layoutBox.isInlineBlockBox())
return geometry().inlineBlockContentHeightAndMargin(layoutBox, horizontalConstraints, { usedHeight });
if (layoutBox.isReplacedBox())
return geometry().inlineReplacedContentHeightAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, { }, { usedHeight });
ASSERT_NOT_REACHED();
return ContentHeightAndMargin { };
};
auto contentHeightAndMargin = compute({ });
if (auto maxHeight = geometry().computedMaxHeight(layoutBox)) {
auto maxHeightAndMargin = compute(maxHeight);
if (contentHeightAndMargin.contentHeight > maxHeightAndMargin.contentHeight)
contentHeightAndMargin = maxHeightAndMargin;
}
if (auto minHeight = geometry().computedMinHeight(layoutBox)) {
auto minHeightAndMargin = compute(minHeight);
if (contentHeightAndMargin.contentHeight < minHeightAndMargin.contentHeight)
contentHeightAndMargin = minHeightAndMargin;
}
auto& boxGeometry = formattingState().boxGeometry(layoutBox);
boxGeometry.setContentBoxHeight(contentHeightAndMargin.contentHeight);
boxGeometry.setVerticalMargin({ contentHeightAndMargin.nonCollapsedMargin.before, contentHeightAndMargin.nonCollapsedMargin.after });
}
void InlineFormattingContext::collectInlineContentIfNeeded()
{
auto& formattingState = this->formattingState();
if (!formattingState.inlineItems().isEmpty())
return;
ASSERT(root().hasInFlowOrFloatingChild());
LayoutQueue layoutQueue;
layoutQueue.append(root().firstInFlowOrFloatingChild());
while (!layoutQueue.isEmpty()) {
while (true) {
auto& layoutBox = *layoutQueue.last();
auto isBoxWithInlineContent = layoutBox.isInlineBox() && !layoutBox.isInlineTextBox() && !layoutBox.isLineBreakBox();
if (!isBoxWithInlineContent)
break;
formattingState.addInlineItem({ layoutBox, InlineItem::Type::InlineBoxStart });
auto& inlineBoxWithInlineContent = downcast<ContainerBox>(layoutBox);
if (!inlineBoxWithInlineContent.hasInFlowOrFloatingChild())
break;
layoutQueue.append(inlineBoxWithInlineContent.firstInFlowOrFloatingChild());
}
while (!layoutQueue.isEmpty()) {
auto& layoutBox = *layoutQueue.takeLast();
if (is<LineBreakBox>(layoutBox)) {
auto& lineBreakBox = downcast<LineBreakBox>(layoutBox);
formattingState.addInlineItem({ layoutBox, lineBreakBox.isOptional() ? InlineItem::Type::WordBreakOpportunity : InlineItem::Type::HardLineBreak });
} else if (layoutBox.isFloatingPositioned())
formattingState.addInlineItem({ layoutBox, InlineItem::Type::Float });
else if (layoutBox.isAtomicInlineLevelBox())
formattingState.addInlineItem({ layoutBox, InlineItem::Type::Box });
else if (layoutBox.isInlineTextBox()) {
InlineTextItem::createAndAppendTextItems(formattingState.inlineItems(), downcast<InlineTextBox>(layoutBox));
} else if (layoutBox.isInlineBox())
formattingState.addInlineItem({ layoutBox, InlineItem::Type::InlineBoxEnd });
else
ASSERT_NOT_REACHED();
if (auto* nextSibling = layoutBox.nextInFlowOrFloatingSibling()) {
layoutQueue.append(nextSibling);
break;
}
}
}
}
InlineRect InlineFormattingContext::computeGeometryForLineContent(const LineBuilder::LineContent& lineContent, const HorizontalConstraints& horizontalConstraints)
{
auto& formattingState = this->formattingState();
auto geometry = this->geometry();
formattingState.addLineBox(geometry.lineBoxForLineContent(lineContent));
const auto& lineBox = formattingState.lineBoxes().last();
auto lineIndex = formattingState.lines().size();
auto& lineBoxLogicalRect = lineBox.logicalRect();
auto rootInlineBoxLogicalRect = lineBox.logicalRectForRootInlineBox();
auto enclosingTopAndBottom = InlineLineGeometry::EnclosingTopAndBottom { lineBoxLogicalRect.top() + rootInlineBoxLogicalRect.top(), lineBoxLogicalRect.top() + rootInlineBoxLogicalRect.bottom() };
HashSet<const Box*> inlineBoxStartSet;
HashSet<const Box*> inlineBoxEndSet;
auto constructLineRunsAndUpdateBoxGeometry = [&] {
for (auto& lineRun : lineContent.runs) {
auto& layoutBox = lineRun.layoutBox();
if (lineRun.isText()) {
formattingState.addLineRun({ lineIndex, layoutBox, lineBox.logicalRectForTextRun(lineRun), lineRun.expansion(), lineRun.textContent() });
continue;
}
if (lineRun.isLineBreak()) {
auto lineBreakBoxRect = lineBox.logicalRectForTextRun(lineRun);
formattingState.addLineRun({ lineIndex, layoutBox, lineBreakBoxRect, lineRun.expansion(), lineRun.textContent() });
if (layoutBox.isLineBreakBox()) {
auto& boxGeometry = formattingState.boxGeometry(layoutBox);
lineBreakBoxRect.moveBy(lineBoxLogicalRect.topLeft());
boxGeometry.setLogicalTopLeft(toLayoutPoint(lineBreakBoxRect.topLeft()));
boxGeometry.setContentBoxHeight(toLayoutUnit(lineBreakBoxRect.height()));
}
continue;
}
if (lineRun.isBox()) {
ASSERT(layoutBox.isAtomicInlineLevelBox());
auto& boxGeometry = formattingState.boxGeometry(layoutBox);
auto logicalMarginRect = lineBox.logicalMarginRectForInlineLevelBox(layoutBox, boxGeometry);
formattingState.addLineRun({ lineIndex, layoutBox, logicalMarginRect, lineRun.expansion(), { } });
auto borderBoxLogicalTopLeft = logicalMarginRect.topLeft();
borderBoxLogicalTopLeft.move(std::max(0_lu, boxGeometry.marginStart()), std::max(0_lu, boxGeometry.marginBefore()));
borderBoxLogicalTopLeft.moveBy(lineBoxLogicalRect.topLeft());
if (layoutBox.isInFlowPositioned())
borderBoxLogicalTopLeft += geometry.inFlowPositionedPositionOffset(layoutBox, horizontalConstraints);
boxGeometry.setLogicalTopLeft(toLayoutPoint(borderBoxLogicalTopLeft));
auto borderBoxTop = borderBoxLogicalTopLeft.y();
auto borderBoxBottom = borderBoxTop + boxGeometry.borderBoxHeight();
enclosingTopAndBottom.top = std::min(enclosingTopAndBottom.top, borderBoxTop);
enclosingTopAndBottom.bottom = std::max(enclosingTopAndBottom.bottom, borderBoxBottom);
continue;
}
if (lineRun.isInlineBoxStart()) {
auto& boxGeometry = formattingState.boxGeometry(layoutBox);
formattingState.addLineRun({ lineIndex, layoutBox, lineBox.logicalMarginRectForInlineLevelBox(layoutBox, boxGeometry), lineRun.expansion(), { } });
inlineBoxStartSet.add(&layoutBox);
continue;
}
if (lineRun.isInlineBoxEnd()) {
inlineBoxEndSet.add(&layoutBox);
continue;
}
ASSERT(lineRun.isWordBreakOpportunity());
}
};
constructLineRunsAndUpdateBoxGeometry();
auto updateBoxGeometryForInlineBoxes = [&] {
if (!lineBox.hasInlineBox())
return;
for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
if (!inlineLevelBox->isInlineBox())
continue;
auto& layoutBox = inlineLevelBox->layoutBox();
auto& boxGeometry = formattingState.boxGeometry(layoutBox);
auto inlineBoxMarginRect = lineBox.logicalMarginRectForInlineLevelBox(layoutBox, boxGeometry);
auto logicalRect = Rect { LayoutPoint { inlineBoxMarginRect.topLeft() }, LayoutSize { inlineBoxMarginRect.size() } };
logicalRect.moveBy(LayoutPoint { lineBoxLogicalRect.topLeft() });
if (inlineBoxStartSet.contains(&layoutBox)) {
logicalRect.moveHorizontally(boxGeometry.marginStart());
boxGeometry.setLogicalTopLeft(logicalRect.topLeft());
boxGeometry.setContentBoxHeight(logicalRect.height());
auto contentBoxWidth = logicalRect.width();
if (inlineBoxEndSet.contains(&layoutBox)) {
contentBoxWidth -= std::max(0_lu, boxGeometry.marginStart()) + std::max(0_lu, boxGeometry.marginEnd());
} else
contentBoxWidth -= std::max(0_lu, boxGeometry.marginStart());
boxGeometry.setContentBoxWidth(contentBoxWidth);
continue;
}
logicalRect.expandHorizontally(-std::max(0_lu, boxGeometry.marginEnd()));
auto enclosingRect = Rect { BoxGeometry::borderBoxTopLeft(boxGeometry), boxGeometry.contentBox().size() };
enclosingRect.expandToContain(logicalRect);
boxGeometry.setLogicalLeft(enclosingRect.left());
boxGeometry.setContentBoxHeight(enclosingRect.height());
boxGeometry.setContentBoxWidth(enclosingRect.width());
}
};
updateBoxGeometryForInlineBoxes();
auto constructLineGeometry = [&] {
formattingState.addLine({ lineBoxLogicalRect, enclosingTopAndBottom, lineBox.alignmentBaseline(), lineBox.horizontalAlignmentOffset().valueOr(InlineLayoutUnit { }), lineContent.contentLogicalWidth });
};
constructLineGeometry();
return lineBoxLogicalRect;
}
void InlineFormattingContext::invalidateFormattingState(const InvalidationState&)
{
formattingState().clearLineAndRuns();
}
}
}
#endif