BlockFormattingContext.cpp [plain text]
#include "config.h"
#include "BlockFormattingContext.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "BlockFormattingState.h"
#include "DisplayBox.h"
#include "FloatingContext.h"
#include "FloatingState.h"
#include "LayoutBox.h"
#include "LayoutContainer.h"
#include "LayoutContext.h"
#include "Logging.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/text/TextStream.h>
namespace WebCore {
namespace Layout {
WTF_MAKE_ISO_ALLOCATED_IMPL(BlockFormattingContext);
BlockFormattingContext::BlockFormattingContext(const Box& formattingContextRoot)
: FormattingContext(formattingContextRoot)
{
}
void BlockFormattingContext::layout(LayoutContext& layoutContext, FormattingState& formattingState) const
{
if (!is<Container>(root()))
return;
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> block formatting context -> layout context(" << &layoutContext << ") formatting root(" << &root() << ")");
auto& formattingRoot = downcast<Container>(root());
LayoutQueue layoutQueue;
FloatingContext floatingContext(formattingState.floatingState());
if (auto* firstChild = formattingRoot.firstInFlowOrFloatingChild())
layoutQueue.append(std::make_unique<LayoutPair>(LayoutPair {*firstChild, layoutContext.createDisplayBox(*firstChild)}));
while (!layoutQueue.isEmpty()) {
while (true) {
auto& layoutPair = *layoutQueue.last();
auto& layoutBox = layoutPair.layoutBox;
auto& displayBox = layoutPair.displayBox;
if (layoutBox.establishesFormattingContext()) {
layoutFormattingContextRoot(layoutContext, formattingState, layoutBox, displayBox);
layoutQueue.removeLast();
if (!layoutBox.nextInFlowOrFloatingSibling())
break;
auto* nextSibling = layoutBox.nextInFlowOrFloatingSibling();
layoutQueue.append(std::make_unique<LayoutPair>(LayoutPair {*nextSibling, layoutContext.createDisplayBox(*nextSibling)}));
continue;
}
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Compute] -> [Position][Border][Padding][Width][Margin] -> for layoutBox(" << &layoutBox << ")");
computeStaticPosition(layoutContext, layoutBox, displayBox);
computeBorderAndPadding(layoutContext, layoutBox, displayBox);
computeWidthAndMargin(layoutContext, layoutBox, displayBox);
if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowOrFloatingChild())
break;
auto& firstChild = *downcast<Container>(layoutBox).firstInFlowOrFloatingChild();
layoutQueue.append(std::make_unique<LayoutPair>(LayoutPair {firstChild, layoutContext.createDisplayBox(firstChild)}));
}
while (!layoutQueue.isEmpty()) {
auto layoutPair = layoutQueue.takeLast();
auto& layoutBox = layoutPair->layoutBox;
auto& displayBox = layoutPair->displayBox;
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Compute] -> [Height][Margin] -> for layoutBox(" << &layoutBox << ")");
ASSERT(!layoutBox.establishesFormattingContext());
computeHeightAndMargin(layoutContext, layoutBox, displayBox);
floatingContext.computePosition(layoutBox, displayBox);
if (!is<Container>(layoutBox))
continue;
auto& container = downcast<Container>(layoutBox);
placeInFlowPositionedChildren(layoutContext, container);
layoutOutOfFlowDescendants(layoutContext, container);
if (auto* nextSibling = container.nextInFlowOrFloatingSibling()) {
layoutQueue.append(std::make_unique<LayoutPair>(LayoutPair {*nextSibling, layoutContext.createDisplayBox(*nextSibling)}));
break;
}
}
}
placeInFlowPositionedChildren(layoutContext, formattingRoot);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> block formatting context -> layout context(" << &layoutContext << ") formatting root(" << &root() << ")");
}
void BlockFormattingContext::layoutFormattingContextRoot(LayoutContext& layoutContext, FormattingState& formattingState, const Box& layoutBox, Display::Box& displayBox) const
{
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Compute] -> [Position][Border][Padding][Width][Margin] -> for layoutBox(" << &layoutBox << ")");
computeStaticPosition(layoutContext, layoutBox, displayBox);
computeBorderAndPadding(layoutContext, layoutBox, displayBox);
computeWidthAndMargin(layoutContext, layoutBox, displayBox);
auto formattingContext = layoutContext.formattingContext(layoutBox);
auto& establishedFormattingState = layoutContext.establishedFormattingState(layoutBox, *formattingContext);
formattingContext->layout(layoutContext, establishedFormattingState);
FloatingContext(formattingState.floatingState()).computePosition(layoutBox, displayBox);
LOG_WITH_STREAM(FormattingContextLayout, stream << "[Compute] -> [Height][Margin] -> for layoutBox(" << &layoutBox << ")");
computeHeightAndMargin(layoutContext, layoutBox, displayBox);
formattingContext->layoutOutOfFlowDescendants(layoutContext, layoutBox);
}
std::unique_ptr<FormattingState> BlockFormattingContext::createFormattingState(Ref<FloatingState>&& floatingState, const LayoutContext& layoutContext) const
{
return std::make_unique<BlockFormattingState>(WTFMove(floatingState), layoutContext);
}
Ref<FloatingState> BlockFormattingContext::createOrFindFloatingState(LayoutContext&) const
{
return FloatingState::create();
}
void BlockFormattingContext::computeStaticPosition(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
{
displayBox.setTopLeft(Geometry::staticPosition(layoutContext, layoutBox));
}
void BlockFormattingContext::computeInFlowPositionedPosition(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
{
displayBox.setTopLeft(Geometry::inFlowPositionedPosition(layoutContext, layoutBox));
}
void BlockFormattingContext::computeWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
{
if (layoutBox.isInFlow())
return computeInFlowWidthAndMargin(layoutContext, layoutBox, displayBox);
if (layoutBox.isFloatingPositioned())
return computeFloatingWidthAndMargin(layoutContext, layoutBox, displayBox);
ASSERT_NOT_REACHED();
}
void BlockFormattingContext::computeHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
{
if (layoutBox.isInFlow())
return computeInFlowHeightAndMargin(layoutContext, layoutBox, displayBox);
if (layoutBox.isFloatingPositioned())
return computeFloatingHeightAndMargin(layoutContext, layoutBox, displayBox);
ASSERT_NOT_REACHED();
}
void BlockFormattingContext::computeInFlowHeightAndMargin(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
{
auto heightAndMargin = Geometry::inFlowHeightAndMargin(layoutContext, layoutBox);
displayBox.setContentBoxHeight(heightAndMargin.height);
displayBox.moveVertically(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin).top);
displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
}
void BlockFormattingContext::computeInFlowWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
{
auto widthAndMargin = Geometry::inFlowWidthAndMargin(layoutContext, layoutBox);
displayBox.setContentBoxWidth(widthAndMargin.width);
displayBox.moveHorizontally(widthAndMargin.margin.left);
displayBox.setHorizontalMargin(widthAndMargin.margin);
}
FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::instrinsicWidthConstraints(LayoutContext& layoutContext, const Box& layoutBox) const
{
auto& formattingState = layoutContext.formattingStateForBox(layoutBox);
ASSERT(formattingState.isBlockFormattingState());
if (auto instrinsicWidthConstraints = formattingState.instrinsicWidthConstraints(layoutBox))
return *instrinsicWidthConstraints;
if (!Geometry::instrinsicWidthConstraintsNeedChildrenWidth(layoutBox)) {
auto instrinsicWidthConstraints = Geometry::instrinsicWidthConstraints(layoutContext, layoutBox);
formattingState.setInstrinsicWidthConstraints(layoutBox, instrinsicWidthConstraints);
return instrinsicWidthConstraints;
}
Vector<const Box*> queue;
ASSERT(is<Container>(layoutBox));
if (auto* firstChild = downcast<Container>(layoutBox).firstInFlowOrFloatingChild())
queue.append(firstChild);
auto& formattingStateForChildren = layoutBox.establishesFormattingContext() ? layoutContext.establishedFormattingState(layoutBox, *this) : formattingState;
while (!queue.isEmpty()) {
while (true) {
auto& childBox = *queue.last();
auto instrinsicWidthConstraints = formattingStateForChildren.instrinsicWidthConstraints(childBox);
if (!instrinsicWidthConstraints && !Geometry::instrinsicWidthConstraintsNeedChildrenWidth(childBox))
instrinsicWidthConstraints = Geometry::instrinsicWidthConstraints(layoutContext, childBox);
if (!instrinsicWidthConstraints && childBox.establishesFormattingContext())
instrinsicWidthConstraints = layoutContext.formattingContext(childBox)->instrinsicWidthConstraints(layoutContext, childBox);
if (instrinsicWidthConstraints) {
formattingStateForChildren.setInstrinsicWidthConstraints(childBox, *instrinsicWidthConstraints);
queue.removeLast();
if (!childBox.nextInFlowOrFloatingSibling())
break;
queue.append(childBox.nextInFlowOrFloatingSibling());
continue;
}
if (!is<Container>(childBox) || !downcast<Container>(childBox).hasInFlowOrFloatingChild())
break;
queue.append(downcast<Container>(childBox).firstInFlowOrFloatingChild());
}
while (!queue.isEmpty()) {
auto& childBox = *queue.takeLast();
formattingStateForChildren.setInstrinsicWidthConstraints(childBox, Geometry::instrinsicWidthConstraints(layoutContext, childBox));
if (!is<Container>(childBox) || !downcast<Container>(childBox).nextInFlowOrFloatingSibling())
continue;
queue.append(downcast<Container>(childBox).nextInFlowOrFloatingSibling());
}
}
auto instrinsicWidthConstraints = Geometry::instrinsicWidthConstraints(layoutContext, layoutBox);
formattingState.setInstrinsicWidthConstraints(layoutBox, instrinsicWidthConstraints);
return instrinsicWidthConstraints;
}
}
}
#endif