diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp index 2f8ea7f2db..3baf0a60b0 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp @@ -811,4 +811,82 @@ void FormattingContext::compute_position(Box const& box) } } +FormattingState::IntrinsicSizes FormattingContext::calculate_intrinsic_sizes(Layout::Box const& box) const +{ + // If we have cached intrinsic sizes for this box, use them. + auto it = m_state.intrinsic_sizes.find(&box); + if (it != m_state.intrinsic_sizes.end()) + return it->value; + + // Nothing cached, perform two throwaway layouts to determine the intrinsic sizes. + // FIXME: This should handle replaced elements with "native" intrinsic size properly! + + auto& cached_box_sizes = m_state.intrinsic_sizes.ensure(&box); + auto const& containing_block = *box.containing_block(); + { + auto throwaway_state = m_state; + throwaway_state.get_mutable(containing_block).content_width = INFINITY; + throwaway_state.get_mutable(containing_block).content_height = INFINITY; + auto independent_formatting_context = const_cast(this)->create_independent_formatting_context_if_needed(throwaway_state, box); + VERIFY(independent_formatting_context); + + independent_formatting_context->run(box, LayoutMode::OnlyRequiredLineBreaks); + cached_box_sizes.max_content_size.set_width(greatest_child_width(throwaway_state, box)); + cached_box_sizes.max_content_size.set_height(BlockFormattingContext::compute_theoretical_height(throwaway_state, box)); + } + + { + auto throwaway_state = m_state; + throwaway_state.get_mutable(containing_block).content_width = 0; + throwaway_state.get_mutable(containing_block).content_height = 0; + auto independent_formatting_context = const_cast(this)->create_independent_formatting_context_if_needed(throwaway_state, box); + VERIFY(independent_formatting_context); + independent_formatting_context->run(box, LayoutMode::AllPossibleLineBreaks); + cached_box_sizes.min_content_size.set_width(greatest_child_width(throwaway_state, box)); + cached_box_sizes.min_content_size.set_height(BlockFormattingContext::compute_theoretical_height(throwaway_state, box)); + } + + return cached_box_sizes; +} + +FormattingContext::MinAndMaxContentSize FormattingContext::calculate_min_and_max_content_width(Layout::Box const& box) const +{ + auto const& sizes = calculate_intrinsic_sizes(box); + return { sizes.min_content_size.width(), sizes.max_content_size.width() }; +} + +FormattingContext::MinAndMaxContentSize FormattingContext::calculate_min_and_max_content_height(Layout::Box const& box) const +{ + auto const& sizes = calculate_intrinsic_sizes(box); + return { sizes.min_content_size.height(), sizes.max_content_size.height() }; +} + +float FormattingContext::calculate_fit_content_size(float min_content_size, float max_content_size, Optional available_space) const +{ + // If the available space in a given axis is definite, equal to clamp(min-content size, stretch-fit size, max-content size) + // (i.e. max(min-content size, min(max-content size, stretch-fit size))). + if (available_space.has_value()) { + // FIXME: Compute the real stretch-fit size. + auto stretch_fit_size = *available_space; + auto s = max(min_content_size, min(max_content_size, stretch_fit_size)); + return s; + } + + // FIXME: When sizing under a min-content constraint, equal to the min-content size. + + // Otherwise, equal to the max-content size in that axis. + return max_content_size; +} + +float FormattingContext::calculate_fit_content_width(Layout::Box const& box, Optional available_space) const +{ + auto [min_content_size, max_content_size] = calculate_min_and_max_content_width(box); + return calculate_fit_content_size(min_content_size, max_content_size, available_space); +} + +float FormattingContext::calculate_fit_content_height(Layout::Box const& box, Optional available_space) const +{ + auto [min_content_size, max_content_size] = calculate_min_and_max_content_height(box); + return calculate_fit_content_size(min_content_size, max_content_size, available_space); +} } diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h index c322ad6179..f8f3d7bf58 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h @@ -45,9 +45,23 @@ public: virtual void parent_context_did_dimension_child_root_box() { } + struct MinAndMaxContentSize { + float min_content_size { 0 }; + float max_content_size { 0 }; + }; + + MinAndMaxContentSize calculate_min_and_max_content_width(Layout::Box const&) const; + MinAndMaxContentSize calculate_min_and_max_content_height(Layout::Box const&) const; + + float calculate_fit_content_height(Layout::Box const&, Optional available_height) const; + float calculate_fit_content_width(Layout::Box const&, Optional available_width) const; + protected: FormattingContext(Type, FormattingState&, Box const&, FormattingContext* parent = nullptr); + float calculate_fit_content_size(float min_content_size, float max_content_size, Optional available_space) const; + FormattingState::IntrinsicSizes calculate_intrinsic_sizes(Layout::Box const&) const; + OwnPtr layout_inside(Box const&, LayoutMode); void compute_position(Box const&); diff --git a/Userland/Libraries/LibWeb/Layout/FormattingState.h b/Userland/Libraries/LibWeb/Layout/FormattingState.h index 5e55509b71..2983e2f89c 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingState.h +++ b/Userland/Libraries/LibWeb/Layout/FormattingState.h @@ -90,6 +90,14 @@ struct FormattingState { NodeState const& get(NodeWithStyleAndBoxModelMetrics const&) const; HashMap> nodes; + + // We cache intrinsic sizes once determined, as they will not change over the course of a full layout. + // This avoids computing them several times while performing flex layout. + struct IntrinsicSizes { + Gfx::FloatSize min_content_size; + Gfx::FloatSize max_content_size; + }; + HashMap mutable intrinsic_sizes; }; Gfx::FloatRect absolute_content_rect(Box const&, FormattingState const&);