From f161e20e5782464010290a6d417e6a1e14e6412b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 25 Sep 2022 19:12:09 +0200 Subject: [PATCH] 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. --- Userland/Libraries/LibWeb/DOM/Document.cpp | 6 +- .../LibWeb/Layout/AvailableSpace.cpp | 53 ++++++++++++++++ .../Libraries/LibWeb/Layout/AvailableSpace.h | 63 +++++++++++++++++++ .../LibWeb/Layout/BlockFormattingContext.cpp | 23 +++---- .../LibWeb/Layout/BlockFormattingContext.h | 3 +- .../LibWeb/Layout/FlexFormattingContext.cpp | 8 +-- .../LibWeb/Layout/FlexFormattingContext.h | 6 +- .../LibWeb/Layout/FormattingContext.cpp | 21 +++++-- .../LibWeb/Layout/FormattingContext.h | 5 +- .../LibWeb/Layout/GridFormattingContext.cpp | 2 +- .../LibWeb/Layout/GridFormattingContext.h | 2 +- .../LibWeb/Layout/InlineFormattingContext.cpp | 2 +- .../LibWeb/Layout/InlineFormattingContext.h | 2 +- .../LibWeb/Layout/SVGFormattingContext.cpp | 2 +- .../LibWeb/Layout/SVGFormattingContext.h | 2 +- .../LibWeb/Layout/TableFormattingContext.cpp | 2 +- .../LibWeb/Layout/TableFormattingContext.h | 2 +- 17 files changed, 163 insertions(+), 41 deletions(-) create mode 100644 Userland/Libraries/LibWeb/Layout/AvailableSpace.cpp create mode 100644 Userland/Libraries/LibWeb/Layout/AvailableSpace.h diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 8d2d0b11f38..317fb3163d4 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -809,7 +809,11 @@ void Document::update_layout() icb_state.set_content_width(viewport_rect.width()); 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(); diff --git a/Userland/Libraries/LibWeb/Layout/AvailableSpace.cpp b/Userland/Libraries/LibWeb/Layout/AvailableSpace.cpp new file mode 100644 index 00000000000..026fe903115 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/AvailableSpace.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +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) +{ +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/AvailableSpace.h b/Userland/Libraries/LibWeb/Layout/AvailableSpace.h new file mode 100644 index 00000000000..7f319ab9cb0 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/AvailableSpace.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +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 : Formatter { + ErrorOr format(FormatBuilder& builder, Web::Layout::AvailableSpace const& available_space) + { + return Formatter::format(builder, available_space.to_string()); + } +}; diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 773f352d756..9732a86363c 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -44,7 +44,7 @@ float BlockFormattingContext::automatic_content_height() const 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()) { 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); - 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 content_height = 0; @@ -419,7 +423,7 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain } else { independent_formatting_context = create_independent_formatting_context_if_needed(m_state, box); 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 layout_block_level_children(verify_cast(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(); } -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) { VERIFY(!block_container.children_are_inline()); diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h index 4d4b827ad00..03b1e5ef638 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -21,8 +21,7 @@ public: explicit BlockFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent); ~BlockFormattingContext(); - virtual void run(Box const&, LayoutMode) override; - virtual void run_intrinsic_sizing(Box const&) override; + virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override; virtual float automatic_content_height() const override; bool is_initial() const; diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp index de329ee8f0c..6159f29dc37 100644 --- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp @@ -67,7 +67,7 @@ float FlexFormattingContext::automatic_content_height() const 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()); @@ -598,7 +598,7 @@ void FlexFormattingContext::determine_available_main_and_cross_space(bool& main_ 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) @@ -635,7 +635,7 @@ float FlexFormattingContext::calculate_indefinite_main_size(FlexItem const& item VERIFY(independent_formatting_context); 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(); } @@ -1100,7 +1100,7 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem& // NOTE: Flex items should always create an 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() : box_state.content_width(); diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h index 4a95aa77ad2..c9f786e8281 100644 --- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h @@ -18,7 +18,7 @@ public: 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; Box const& flex_container() const { return context_box(); } @@ -190,11 +190,11 @@ private: Vector m_flex_items; CSS::FlexDirection m_flex_direction {}; - struct AvailableSpace { + struct AvailableSpaceForItems { Optional main; Optional cross; }; - Optional m_available_space; + Optional m_available_space; }; } diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp index 870e6c41832..1fe9effafb0 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp @@ -39,7 +39,18 @@ void FormattingContext::run_intrinsic_sizing(Box const& 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); + 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) @@ -104,7 +115,7 @@ OwnPtr FormattingContext::create_independent_formatting_conte { } 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(state, child_box); } @@ -146,7 +157,7 @@ OwnPtr FormattingContext::create_independent_formatting_conte { } 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(state, child_box); } @@ -176,9 +187,9 @@ OwnPtr FormattingContext::layout_inside(Box const& child_box, auto independent_formatting_context = create_independent_formatting_context_if_needed(m_state, child_box); 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 - run(child_box, layout_mode); + run(child_box, layout_mode, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite()); return independent_formatting_context; } diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h index e091d31c7d8..76b7556a302 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h @@ -8,6 +8,7 @@ #include #include +#include #include namespace Web::Layout { @@ -24,7 +25,7 @@ public: 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. 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_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&); diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp index c9f68f9c2f1..1ac3ee8941d 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp @@ -17,7 +17,7 @@ GridFormattingContext::GridFormattingContext(LayoutState& state, BlockContainer 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 { if (child_box.is_anonymous() && !child_box.first_child_of_type()) { diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h index 05696a3d38c..09a5450ed36 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h @@ -17,7 +17,7 @@ public: explicit GridFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent); ~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; private: diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp index c6f5069232b..351288f67ec 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp @@ -83,7 +83,7 @@ float InlineFormattingContext::automatic_content_height() const 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()); generate_line_boxes(layout_mode); diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h index e5dd3a29edd..8bb801f6582 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h @@ -23,7 +23,7 @@ public: BlockContainer const& containing_block() const { return static_cast(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; void dimension_box_on_line(Box const&, LayoutMode); diff --git a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp index e7cf8757fb0..e7b0f5f1548 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp @@ -25,7 +25,7 @@ float SVGFormattingContext::automatic_content_height() const 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(*box.dom_node()); diff --git a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.h b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.h index bfc561947f3..6c6d011f1bf 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.h @@ -16,7 +16,7 @@ public: explicit SVGFormattingContext(LayoutState&, Box const&, FormattingContext* parent); ~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; }; diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp index 23eca43e107..f391be5efb4 100644 --- a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp @@ -23,7 +23,7 @@ TableFormattingContext::TableFormattingContext(LayoutState& state, BlockContaine 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); diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h index 05690f8e8c1..afef496e96c 100644 --- a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h @@ -23,7 +23,7 @@ public: explicit TableFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent); ~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; private: