1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 19:07:35 +00:00

LibWeb: Make FormattingContext::run() take available space as input

Instead of formatting contexts flailing around to figure out from the
"inside" how much space is available on the "outside", we should
provide the amount of available space in both axes as an input to run().

This basically means that when something creates a nested formatting
context, the parent context is responsible for telling the nested context
how much space is available for layout. This information is provided
immediately when invoking run().

Note that this commit doesn't pass accurate values in all cases yet.
This first step just makes it build, and passes available values in some
cases where getting them was trivial.
This commit is contained in:
Andreas Kling 2022-09-25 19:12:09 +02:00
parent 6b2ce2ccc3
commit f161e20e57
17 changed files with 163 additions and 41 deletions

View file

@ -809,7 +809,11 @@ void Document::update_layout()
icb_state.set_content_width(viewport_rect.width()); icb_state.set_content_width(viewport_rect.width());
icb_state.set_content_height(viewport_rect.height()); icb_state.set_content_height(viewport_rect.height());
root_formatting_context.run(*m_layout_root, Layout::LayoutMode::Normal); root_formatting_context.run(
*m_layout_root,
Layout::LayoutMode::Normal,
Layout::AvailableSpace::make_definite(viewport_rect.width()),
Layout::AvailableSpace::make_definite(viewport_rect.height()));
} }
layout_state.commit(); layout_state.commit();

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Layout/AvailableSpace.h>
#include <math.h>
namespace Web::Layout {
AvailableSpace AvailableSpace::make_definite(float value)
{
return AvailableSpace { Type::Definite, value };
}
AvailableSpace AvailableSpace::make_indefinite()
{
return AvailableSpace { Type::Indefinite, INFINITY };
}
AvailableSpace AvailableSpace::make_min_content()
{
return AvailableSpace { Type::MinContent, 0 };
}
AvailableSpace AvailableSpace::make_max_content()
{
return AvailableSpace { Type::MaxContent, INFINITY };
}
String AvailableSpace::to_string() const
{
switch (m_type) {
case Type::Definite:
return String::formatted("definite({})", m_value);
case Type::Indefinite:
return "indefinite";
case Type::MinContent:
return "min-content";
case Type::MaxContent:
return "max-content";
}
VERIFY_NOT_REACHED();
}
AvailableSpace::AvailableSpace(Type type, float value)
: m_type(type)
, m_value(value)
{
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Format.h>
#include <AK/String.h>
#include <LibWeb/Forward.h>
namespace Web::Layout {
class AvailableSpace {
public:
enum class Type {
Definite,
Indefinite,
MinContent,
MaxContent,
};
static AvailableSpace make_definite(float);
static AvailableSpace make_indefinite();
static AvailableSpace make_min_content();
static AvailableSpace make_max_content();
bool is_definite() const { return m_type == Type::Definite; }
bool is_indefinite() const { return m_type == Type::Indefinite; }
bool is_min_content() const { return m_type == Type::MinContent; }
bool is_max_content() const { return m_type == Type::MaxContent; }
bool is_intrinsic_sizing_constraint() const { return is_min_content() || is_max_content(); }
float definite_value() const
{
VERIFY(is_definite());
return m_value;
}
float to_px() const
{
return m_value;
}
String to_string() const;
private:
AvailableSpace(Type type, float);
Type m_type {};
float m_value {};
};
}
template<>
struct AK::Formatter<Web::Layout::AvailableSpace> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Web::Layout::AvailableSpace const& available_space)
{
return Formatter<StringView>::format(builder, available_space.to_string());
}
};

View file

@ -44,7 +44,7 @@ float BlockFormattingContext::automatic_content_height() const
return compute_auto_height_for_block_formatting_context_root(m_state, root()); return compute_auto_height_for_block_formatting_context_root(m_state, root());
} }
void BlockFormattingContext::run(Box const&, LayoutMode layout_mode) void BlockFormattingContext::run(Box const&, LayoutMode layout_mode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
{ {
if (is_initial()) { if (is_initial()) {
layout_initial_containing_block(layout_mode); layout_initial_containing_block(layout_mode);
@ -366,7 +366,11 @@ void BlockFormattingContext::layout_inline_children(BlockContainer const& block_
} }
InlineFormattingContext context(m_state, block_container, *this); InlineFormattingContext context(m_state, block_container, *this);
context.run(block_container, layout_mode); context.run(
block_container,
layout_mode,
AvailableSpace::make_definite(containing_block_width_for(block_container)),
AvailableSpace::make_definite(containing_block_height_for(block_container)));
float max_line_width = 0; float max_line_width = 0;
float content_height = 0; float content_height = 0;
@ -419,7 +423,7 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
} else { } else {
independent_formatting_context = create_independent_formatting_context_if_needed(m_state, box); independent_formatting_context = create_independent_formatting_context_if_needed(m_state, box);
if (independent_formatting_context) if (independent_formatting_context)
independent_formatting_context->run(box, layout_mode); independent_formatting_context->run(box, layout_mode, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
else else
layout_block_level_children(verify_cast<BlockContainer>(box), layout_mode); layout_block_level_children(verify_cast<BlockContainer>(box), layout_mode);
} }
@ -442,19 +446,6 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
independent_formatting_context->parent_context_did_dimension_child_root_box(); independent_formatting_context->parent_context_did_dimension_child_root_box();
} }
void BlockFormattingContext::run_intrinsic_sizing(Box const& box)
{
auto& box_state = m_state.get_mutable(box);
if (box_state.has_definite_width())
box_state.set_content_width(box.computed_values().width().resolved(box, CSS::Length::make_px(containing_block_width_for(box))).to_px(box));
if (box_state.has_definite_height())
box_state.set_content_height(box.computed_values().height().resolved(box, CSS::Length::make_px(containing_block_height_for(box))).to_px(box));
run(box, LayoutMode::IntrinsicSizing);
}
void BlockFormattingContext::layout_block_level_children(BlockContainer const& block_container, LayoutMode layout_mode) void BlockFormattingContext::layout_block_level_children(BlockContainer const& block_container, LayoutMode layout_mode)
{ {
VERIFY(!block_container.children_are_inline()); VERIFY(!block_container.children_are_inline());

View file

@ -21,8 +21,7 @@ public:
explicit BlockFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent); explicit BlockFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent);
~BlockFormattingContext(); ~BlockFormattingContext();
virtual void run(Box const&, LayoutMode) override; virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
virtual void run_intrinsic_sizing(Box const&) override;
virtual float automatic_content_height() const override; virtual float automatic_content_height() const override;
bool is_initial() const; bool is_initial() const;

View file

@ -67,7 +67,7 @@ float FlexFormattingContext::automatic_content_height() const
return m_state.get(flex_container()).content_height(); return m_state.get(flex_container()).content_height();
} }
void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode) void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
{ {
VERIFY(&run_box == &flex_container()); VERIFY(&run_box == &flex_container());
@ -598,7 +598,7 @@ void FlexFormattingContext::determine_available_main_and_cross_space(bool& main_
cross_available_space = cross_max_size; cross_available_space = cross_max_size;
} }
m_available_space = AvailableSpace { .main = main_available_space, .cross = cross_available_space }; m_available_space = AvailableSpaceForItems { .main = main_available_space, .cross = cross_available_space };
} }
float FlexFormattingContext::calculate_indefinite_main_size(FlexItem const& item) float FlexFormattingContext::calculate_indefinite_main_size(FlexItem const& item)
@ -635,7 +635,7 @@ float FlexFormattingContext::calculate_indefinite_main_size(FlexItem const& item
VERIFY(independent_formatting_context); VERIFY(independent_formatting_context);
box_state.set_content_width(fit_content_cross_size); box_state.set_content_width(fit_content_cross_size);
independent_formatting_context->run(item.box, LayoutMode::Normal); independent_formatting_context->run(item.box, LayoutMode::Normal, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
return independent_formatting_context->automatic_content_height(); return independent_formatting_context->automatic_content_height();
} }
@ -1100,7 +1100,7 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem&
// NOTE: Flex items should always create an independent formatting context! // NOTE: Flex items should always create an independent formatting context!
VERIFY(independent_formatting_context); VERIFY(independent_formatting_context);
independent_formatting_context->run(item.box, LayoutMode::Normal); independent_formatting_context->run(item.box, LayoutMode::Normal, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
auto automatic_cross_size = is_row_layout() ? independent_formatting_context->automatic_content_height() auto automatic_cross_size = is_row_layout() ? independent_formatting_context->automatic_content_height()
: box_state.content_width(); : box_state.content_width();

View file

@ -18,7 +18,7 @@ public:
virtual bool inhibits_floating() const override { return true; } virtual bool inhibits_floating() const override { return true; }
virtual void run(Box const&, LayoutMode) override; virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
virtual float automatic_content_height() const override; virtual float automatic_content_height() const override;
Box const& flex_container() const { return context_box(); } Box const& flex_container() const { return context_box(); }
@ -190,11 +190,11 @@ private:
Vector<FlexItem> m_flex_items; Vector<FlexItem> m_flex_items;
CSS::FlexDirection m_flex_direction {}; CSS::FlexDirection m_flex_direction {};
struct AvailableSpace { struct AvailableSpaceForItems {
Optional<float> main; Optional<float> main;
Optional<float> cross; Optional<float> cross;
}; };
Optional<AvailableSpace> m_available_space; Optional<AvailableSpaceForItems> m_available_space;
}; };
} }

View file

@ -39,7 +39,18 @@ void FormattingContext::run_intrinsic_sizing(Box const& box)
if (box_state.has_definite_height()) if (box_state.has_definite_height())
box_state.set_content_height(box.computed_values().height().resolved(box, CSS::Length::make_px(containing_block_height_for(box))).to_px(box)); box_state.set_content_height(box.computed_values().height().resolved(box, CSS::Length::make_px(containing_block_height_for(box))).to_px(box));
run(box, LayoutMode::IntrinsicSizing); auto to_available_space = [&](SizeConstraint constraint) {
if (constraint == SizeConstraint::MinContent)
return AvailableSpace::make_min_content();
if (constraint == SizeConstraint::MaxContent)
return AvailableSpace::make_max_content();
return AvailableSpace::make_indefinite();
};
auto available_width = to_available_space(box_state.width_constraint);
auto available_height = to_available_space(box_state.height_constraint);
run(box, LayoutMode::IntrinsicSizing, available_width, available_height);
} }
bool FormattingContext::creates_block_formatting_context(Box const& box) bool FormattingContext::creates_block_formatting_context(Box const& box)
@ -104,7 +115,7 @@ OwnPtr<FormattingContext> FormattingContext::create_independent_formatting_conte
{ {
} }
virtual float automatic_content_height() const override { return 0; }; virtual float automatic_content_height() const override { return 0; };
virtual void run(Box const&, LayoutMode) override { } virtual void run(Box const&, LayoutMode, AvailableSpace const&, AvailableSpace const&) override { }
}; };
return make<ReplacedFormattingContext>(state, child_box); return make<ReplacedFormattingContext>(state, child_box);
} }
@ -146,7 +157,7 @@ OwnPtr<FormattingContext> FormattingContext::create_independent_formatting_conte
{ {
} }
virtual float automatic_content_height() const override { return 0; }; virtual float automatic_content_height() const override { return 0; };
virtual void run(Box const&, LayoutMode) override { } virtual void run(Box const&, LayoutMode, AvailableSpace const&, AvailableSpace const&) override { }
}; };
return make<DummyFormattingContext>(state, child_box); return make<DummyFormattingContext>(state, child_box);
} }
@ -176,9 +187,9 @@ OwnPtr<FormattingContext> FormattingContext::layout_inside(Box const& child_box,
auto independent_formatting_context = create_independent_formatting_context_if_needed(m_state, child_box); auto independent_formatting_context = create_independent_formatting_context_if_needed(m_state, child_box);
if (independent_formatting_context) if (independent_formatting_context)
independent_formatting_context->run(child_box, layout_mode); independent_formatting_context->run(child_box, layout_mode, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
else else
run(child_box, layout_mode); run(child_box, layout_mode, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
return independent_formatting_context; return independent_formatting_context;
} }

View file

@ -8,6 +8,7 @@
#include <AK/OwnPtr.h> #include <AK/OwnPtr.h>
#include <LibWeb/Forward.h> #include <LibWeb/Forward.h>
#include <LibWeb/Layout/AvailableSpace.h>
#include <LibWeb/Layout/LayoutState.h> #include <LibWeb/Layout/LayoutState.h>
namespace Web::Layout { namespace Web::Layout {
@ -24,7 +25,7 @@ public:
SVG, SVG,
}; };
virtual void run(Box const&, LayoutMode) = 0; virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) = 0;
// This function returns the automatic content height of the context's root box. // This function returns the automatic content height of the context's root box.
virtual float automatic_content_height() const = 0; virtual float automatic_content_height() const = 0;
@ -64,7 +65,7 @@ public:
static float containing_block_width_for(Box const&, LayoutState const&); static float containing_block_width_for(Box const&, LayoutState const&);
static float containing_block_height_for(Box const&, LayoutState const&); static float containing_block_height_for(Box const&, LayoutState const&);
virtual void run_intrinsic_sizing(Box const&); void run_intrinsic_sizing(Box const&);
float compute_box_y_position_with_respect_to_siblings(Box const&, LayoutState::UsedValues const&); float compute_box_y_position_with_respect_to_siblings(Box const&, LayoutState::UsedValues const&);

View file

@ -17,7 +17,7 @@ GridFormattingContext::GridFormattingContext(LayoutState& state, BlockContainer
GridFormattingContext::~GridFormattingContext() = default; GridFormattingContext::~GridFormattingContext() = default;
void GridFormattingContext::run(Box const& box, LayoutMode) void GridFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
{ {
auto should_skip_is_anonymous_text_run = [&](Box& child_box) -> bool { auto should_skip_is_anonymous_text_run = [&](Box& child_box) -> bool {
if (child_box.is_anonymous() && !child_box.first_child_of_type<BlockContainer>()) { if (child_box.is_anonymous() && !child_box.first_child_of_type<BlockContainer>()) {

View file

@ -17,7 +17,7 @@ public:
explicit GridFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent); explicit GridFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent);
~GridFormattingContext(); ~GridFormattingContext();
virtual void run(Box const&, LayoutMode) override; virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
virtual float automatic_content_height() const override; virtual float automatic_content_height() const override;
private: private:

View file

@ -83,7 +83,7 @@ float InlineFormattingContext::automatic_content_height() const
return compute_auto_height_for_block_formatting_context_root(m_state, containing_block()); return compute_auto_height_for_block_formatting_context_root(m_state, containing_block());
} }
void InlineFormattingContext::run(Box const&, LayoutMode layout_mode) void InlineFormattingContext::run(Box const&, LayoutMode layout_mode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
{ {
VERIFY(containing_block().children_are_inline()); VERIFY(containing_block().children_are_inline());
generate_line_boxes(layout_mode); generate_line_boxes(layout_mode);

View file

@ -23,7 +23,7 @@ public:
BlockContainer const& containing_block() const { return static_cast<BlockContainer const&>(context_box()); } BlockContainer const& containing_block() const { return static_cast<BlockContainer const&>(context_box()); }
virtual void run(Box const&, LayoutMode) override; virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
virtual float automatic_content_height() const override; virtual float automatic_content_height() const override;
void dimension_box_on_line(Box const&, LayoutMode); void dimension_box_on_line(Box const&, LayoutMode);

View file

@ -25,7 +25,7 @@ float SVGFormattingContext::automatic_content_height() const
return 0; return 0;
} }
void SVGFormattingContext::run(Box const& box, LayoutMode) void SVGFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
{ {
auto& svg_svg_element = verify_cast<SVG::SVGSVGElement>(*box.dom_node()); auto& svg_svg_element = verify_cast<SVG::SVGSVGElement>(*box.dom_node());

View file

@ -16,7 +16,7 @@ public:
explicit SVGFormattingContext(LayoutState&, Box const&, FormattingContext* parent); explicit SVGFormattingContext(LayoutState&, Box const&, FormattingContext* parent);
~SVGFormattingContext(); ~SVGFormattingContext();
virtual void run(Box const&, LayoutMode) override; virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
virtual float automatic_content_height() const override; virtual float automatic_content_height() const override;
}; };

View file

@ -23,7 +23,7 @@ TableFormattingContext::TableFormattingContext(LayoutState& state, BlockContaine
TableFormattingContext::~TableFormattingContext() = default; TableFormattingContext::~TableFormattingContext() = default;
void TableFormattingContext::run(Box const& box, LayoutMode) void TableFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
{ {
auto& box_state = m_state.get_mutable(box); auto& box_state = m_state.get_mutable(box);

View file

@ -23,7 +23,7 @@ public:
explicit TableFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent); explicit TableFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent);
~TableFormattingContext(); ~TableFormattingContext();
virtual void run(Box const&, LayoutMode) override; virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
virtual float automatic_content_height() const override; virtual float automatic_content_height() const override;
private: private: