2020-11-22 13:38:18 +01:00
/*
2022-02-19 16:42:02 +01:00
* Copyright ( c ) 2020 - 2022 , Andreas Kling < kling @ serenityos . org >
2020-11-22 13:38:18 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-11-22 13:38:18 +01:00
*/
# include <LibWeb/CSS/Length.h>
# include <LibWeb/DOM/Node.h>
2021-11-18 15:01:28 +01:00
# include <LibWeb/HTML/BrowsingContext.h>
2021-10-06 20:02:41 +02:00
# include <LibWeb/Layout/BlockContainer.h>
2020-11-22 13:38:18 +01:00
# include <LibWeb/Layout/BlockFormattingContext.h>
2020-11-22 15:53:01 +01:00
# include <LibWeb/Layout/Box.h>
2021-09-08 11:27:46 +02:00
# include <LibWeb/Layout/InitialContainingBlock.h>
2020-11-22 13:38:18 +01:00
# include <LibWeb/Layout/InlineFormattingContext.h>
2022-03-22 19:18:05 +01:00
# include <LibWeb/Layout/LineBuilder.h>
2020-11-22 15:53:01 +01:00
# include <LibWeb/Layout/ListItemBox.h>
2021-10-28 19:35:54 +02:00
# include <LibWeb/Layout/ListItemMarkerBox.h>
2021-02-10 08:58:26 +01:00
# include <LibWeb/Layout/ReplacedBox.h>
2020-11-22 13:38:18 +01:00
namespace Web : : Layout {
2022-07-16 23:30:32 +02:00
BlockFormattingContext : : BlockFormattingContext ( LayoutState & state , BlockContainer const & root , FormattingContext * parent )
2022-02-19 20:13:47 +01:00
: FormattingContext ( Type : : Block , state , root , parent )
2020-11-22 13:38:18 +01:00
{
}
BlockFormattingContext : : ~ BlockFormattingContext ( )
{
2022-02-19 16:42:02 +01:00
if ( ! m_was_notified_after_parent_dimensioned_my_root_box ) {
// HACK: The parent formatting context never notified us after assigning dimensions to our root box.
// Pretend that it did anyway, to make sure absolutely positioned children get laid out.
// FIXME: Get rid of this hack once parent contexts behave properly.
parent_context_did_dimension_child_root_box ( ) ;
}
2020-11-22 13:38:18 +01:00
}
bool BlockFormattingContext : : is_initial ( ) const
{
2021-10-06 19:47:10 +02:00
return is < InitialContainingBlock > ( root ( ) ) ;
2020-11-22 13:38:18 +01:00
}
2022-02-20 15:51:24 +01:00
void BlockFormattingContext : : run ( Box const & , LayoutMode layout_mode )
2020-11-22 13:38:18 +01:00
{
if ( is_initial ( ) ) {
layout_initial_containing_block ( layout_mode ) ;
return ;
}
2022-02-12 19:54:09 +01:00
if ( root ( ) . children_are_inline ( ) )
layout_inline_children ( root ( ) , layout_mode ) ;
else
layout_block_level_children ( root ( ) , layout_mode ) ;
}
2020-12-14 11:33:11 +01:00
2022-02-12 19:54:09 +01:00
void BlockFormattingContext : : parent_context_did_dimension_child_root_box ( )
{
2022-02-19 16:42:02 +01:00
m_was_notified_after_parent_dimensioned_my_root_box = true ;
2022-03-18 14:44:36 +01:00
// Left-side floats: offset_from_edge is from left edge (0) to left content edge of floating_box.
for ( auto & floating_box : m_left_floats . all_boxes ) {
auto & box_state = m_state . get_mutable ( floating_box - > box ) ;
box_state . offset . set_x ( floating_box - > offset_from_edge ) ;
}
2022-03-18 19:28:58 +01:00
// Right-side floats: offset_from_edge is from right edge (float_containing_block_width) to the left content edge of floating_box.
2022-03-18 14:44:36 +01:00
for ( auto & floating_box : m_right_floats . all_boxes ) {
2022-07-09 15:17:47 +02:00
auto float_containing_block_width = containing_block_width_for ( floating_box - > box ) ;
2022-03-18 14:44:36 +01:00
auto & box_state = m_state . get_mutable ( floating_box - > box ) ;
2022-03-18 19:28:58 +01:00
box_state . offset . set_x ( float_containing_block_width - floating_box - > offset_from_edge ) ;
2022-03-18 14:44:36 +01:00
}
// We can also layout absolutely positioned boxes within this BFC.
2022-02-12 19:54:09 +01:00
for ( auto & box : m_absolutely_positioned_boxes )
layout_absolutely_positioned_element ( box ) ;
2020-11-22 13:38:18 +01:00
}
2022-03-18 14:44:36 +01:00
void BlockFormattingContext : : compute_width ( Box const & box , LayoutMode layout_mode )
2020-11-22 13:38:18 +01:00
{
2021-01-06 18:18:46 +01:00
if ( box . is_absolutely_positioned ( ) ) {
compute_width_for_absolutely_positioned_element ( box ) ;
return ;
}
2021-01-01 18:55:47 +01:00
if ( is < ReplacedBox > ( box ) ) {
2020-11-22 15:53:01 +01:00
// FIXME: This should not be done *by* ReplacedBox
2021-06-24 19:53:42 +02:00
auto & replaced = verify_cast < ReplacedBox > ( box ) ;
2022-02-20 15:51:24 +01:00
// FIXME: This const_cast is gross.
const_cast < ReplacedBox & > ( replaced ) . prepare_for_replaced_layout ( ) ;
2020-12-11 22:27:09 +01:00
compute_width_for_block_level_replaced_element_in_normal_flow ( replaced ) ;
2022-04-11 01:04:09 +02:00
// NOTE: We don't return here.
2020-11-22 13:38:18 +01:00
}
2020-12-06 01:08:14 +01:00
if ( box . is_floating ( ) ) {
2022-03-18 14:44:36 +01:00
compute_width_for_floating_box ( box , layout_mode ) ;
2020-12-06 01:08:14 +01:00
return ;
}
2022-02-20 15:51:24 +01:00
auto const & computed_values = box . computed_values ( ) ;
2022-07-09 15:17:47 +02:00
float width_of_containing_block = containing_block_width_for ( box ) ;
2022-01-19 16:19:43 +00:00
auto width_of_containing_block_as_length = CSS : : Length : : make_px ( width_of_containing_block ) ;
2020-11-22 13:38:18 +01:00
auto zero_value = CSS : : Length : : make_px ( 0 ) ;
auto margin_left = CSS : : Length : : make_auto ( ) ;
auto margin_right = CSS : : Length : : make_auto ( ) ;
2022-09-13 17:42:39 +02:00
auto padding_left = computed_values . padding ( ) . left ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
auto padding_right = computed_values . padding ( ) . right ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
2020-11-22 13:38:18 +01:00
2022-04-01 20:58:27 +03:00
auto try_compute_width = [ & ] ( auto const & a_width ) {
2020-11-22 13:38:18 +01:00
CSS : : Length width = a_width ;
2022-09-13 17:42:39 +02:00
margin_left = computed_values . margin ( ) . left ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
margin_right = computed_values . margin ( ) . right ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
2021-01-06 10:34:31 +01:00
float total_px = computed_values . border_left ( ) . width + computed_values . border_right ( ) . width ;
2020-11-22 13:38:18 +01:00
for ( auto & value : { margin_left , padding_left , width , padding_right , margin_right } ) {
total_px + = value . to_px ( box ) ;
}
2020-12-11 22:31:29 +01:00
if ( ! box . is_inline ( ) ) {
2020-11-22 13:38:18 +01:00
// 10.3.3 Block-level, non-replaced elements in normal flow
// If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero.
if ( width . is_auto ( ) & & total_px > width_of_containing_block ) {
if ( margin_left . is_auto ( ) )
margin_left = zero_value ;
if ( margin_right . is_auto ( ) )
margin_right = zero_value ;
}
// 10.3.3 cont'd.
auto underflow_px = width_of_containing_block - total_px ;
if ( width . is_auto ( ) ) {
if ( margin_left . is_auto ( ) )
margin_left = zero_value ;
if ( margin_right . is_auto ( ) )
margin_right = zero_value ;
2022-04-03 12:51:32 +02:00
if ( width_of_containing_block = = INFINITY ) {
// If width of containing block is infinity
// then we might as well behave like we don't have a containing block
// and remove it from the calculation. In that case, our width
// will end up being the sum of margin_*, padding_*, border_*
float sum_of_all = computed_values . border_left ( ) . width + computed_values . border_right ( ) . width ;
for ( const auto & value : { margin_left , padding_left , width , padding_right , margin_right } ) {
sum_of_all + = value . to_px ( box ) ;
}
width = CSS : : Length ( sum_of_all , CSS : : Length : : Type : : Px ) ;
2020-11-22 13:38:18 +01:00
} else {
2022-04-03 12:51:32 +02:00
if ( underflow_px > = 0 ) {
width = CSS : : Length ( underflow_px , CSS : : Length : : Type : : Px ) ;
} else {
width = zero_value ;
margin_right = CSS : : Length ( margin_right . to_px ( box ) + underflow_px , CSS : : Length : : Type : : Px ) ;
}
2020-11-22 13:38:18 +01:00
}
} else {
if ( ! margin_left . is_auto ( ) & & ! margin_right . is_auto ( ) ) {
margin_right = CSS : : Length ( margin_right . to_px ( box ) + underflow_px , CSS : : Length : : Type : : Px ) ;
} else if ( ! margin_left . is_auto ( ) & & margin_right . is_auto ( ) ) {
margin_right = CSS : : Length ( underflow_px , CSS : : Length : : Type : : Px ) ;
} else if ( margin_left . is_auto ( ) & & ! margin_right . is_auto ( ) ) {
margin_left = CSS : : Length ( underflow_px , CSS : : Length : : Type : : Px ) ;
} else { // margin_left.is_auto() && margin_right.is_auto()
auto half_of_the_underflow = CSS : : Length ( underflow_px / 2 , CSS : : Length : : Type : : Px ) ;
margin_left = half_of_the_underflow ;
margin_right = half_of_the_underflow ;
}
}
2020-12-11 22:31:29 +01:00
} else if ( box . is_inline_block ( ) ) {
2020-11-22 13:38:18 +01:00
// 10.3.9 'Inline-block', non-replaced elements in normal flow
// A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
if ( margin_left . is_auto ( ) )
margin_left = zero_value ;
if ( margin_right . is_auto ( ) )
margin_right = zero_value ;
// If 'width' is 'auto', the used value is the shrink-to-fit width as for floating elements.
if ( width . is_auto ( ) ) {
// Find the available width: in this case, this is the width of the containing
// block minus the used values of 'margin-left', 'border-left-width', 'padding-left',
// 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
float available_width = width_of_containing_block
2021-01-06 10:34:31 +01:00
- margin_left . to_px ( box ) - computed_values . border_left ( ) . width - padding_left . to_px ( box )
- padding_right . to_px ( box ) - computed_values . border_right ( ) . width - margin_right . to_px ( box ) ;
2020-11-22 13:38:18 +01:00
auto result = calculate_shrink_to_fit_widths ( box ) ;
// Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
width = CSS : : Length ( min ( max ( result . preferred_minimum_width , available_width ) , result . preferred_width ) , CSS : : Length : : Type : : Px ) ;
}
}
return width ;
} ;
2022-09-13 10:47:44 +02:00
auto input_width = [ & ] {
if ( should_treat_width_as_auto ( box ) )
return CSS : : Length : : make_auto ( ) ;
return computed_values . width ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
} ( ) ;
2020-11-22 13:38:18 +01:00
// 1. The tentative used width is calculated (without 'min-width' and 'max-width')
2022-09-13 10:47:44 +02:00
auto used_width = try_compute_width ( input_width ) ;
2020-11-22 13:38:18 +01:00
// 2. The tentative used width is greater than 'max-width', the rules above are applied again,
// but this time using the computed value of 'max-width' as the computed value for 'width'.
2022-07-07 12:36:02 +02:00
auto specified_max_width = computed_values . max_width ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
2020-11-22 13:38:18 +01:00
if ( ! specified_max_width . is_auto ( ) ) {
if ( used_width . to_px ( box ) > specified_max_width . to_px ( box ) ) {
used_width = try_compute_width ( specified_max_width ) ;
}
}
// 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
// but this time using the value of 'min-width' as the computed value for 'width'.
2022-07-07 12:36:02 +02:00
auto specified_min_width = computed_values . min_width ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
2020-11-22 13:38:18 +01:00
if ( ! specified_min_width . is_auto ( ) ) {
if ( used_width . to_px ( box ) < specified_min_width . to_px ( box ) ) {
used_width = try_compute_width ( specified_min_width ) ;
}
}
2022-02-21 17:42:09 +01:00
auto & box_state = m_state . get_mutable ( box ) ;
2022-04-11 01:04:09 +02:00
if ( ! is < ReplacedBox > ( box ) )
2022-07-17 17:59:02 +02:00
box_state . set_content_width ( used_width . to_px ( box ) ) ;
2022-04-11 01:04:09 +02:00
2022-02-20 15:51:24 +01:00
box_state . margin_left = margin_left . to_px ( box ) ;
box_state . margin_right = margin_right . to_px ( box ) ;
box_state . border_left = computed_values . border_left ( ) . width ;
box_state . border_right = computed_values . border_right ( ) . width ;
box_state . padding_left = padding_left . to_px ( box ) ;
box_state . padding_right = padding_right . to_px ( box ) ;
2020-11-22 13:38:18 +01:00
}
2022-07-09 15:17:47 +02:00
void BlockFormattingContext : : compute_width_for_floating_box ( Box const & box , LayoutMode )
2020-12-06 01:08:14 +01:00
{
// 10.3.5 Floating, non-replaced elements
2021-01-06 10:34:31 +01:00
auto & computed_values = box . computed_values ( ) ;
2022-03-18 14:44:36 +01:00
2022-07-09 15:17:47 +02:00
float width_of_containing_block = containing_block_width_for ( box ) ;
2022-01-19 16:19:43 +00:00
auto width_of_containing_block_as_length = CSS : : Length : : make_px ( width_of_containing_block ) ;
2020-12-06 01:08:14 +01:00
auto zero_value = CSS : : Length : : make_px ( 0 ) ;
2022-09-13 17:42:39 +02:00
auto margin_left = computed_values . margin ( ) . left ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
auto margin_right = computed_values . margin ( ) . right ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
auto const padding_left = computed_values . padding ( ) . left ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
auto const padding_right = computed_values . padding ( ) . right ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
2020-12-06 01:08:14 +01:00
// If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'.
if ( margin_left . is_auto ( ) )
margin_left = zero_value ;
if ( margin_right . is_auto ( ) )
margin_right = zero_value ;
2022-06-05 11:45:46 +02:00
auto compute_width = [ & ] ( auto width ) {
// If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width.
if ( width . is_auto ( ) ) {
2020-12-06 01:08:14 +01:00
2022-06-05 11:45:46 +02:00
// Find the available width: in this case, this is the width of the containing
// block minus the used values of 'margin-left', 'border-left-width', 'padding-left',
// 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
float available_width = width_of_containing_block
- margin_left . to_px ( box ) - computed_values . border_left ( ) . width - padding_left . to_px ( box )
- padding_right . to_px ( box ) - computed_values . border_right ( ) . width - margin_right . to_px ( box ) ;
2020-12-06 01:08:14 +01:00
2022-06-05 11:45:46 +02:00
auto result = calculate_shrink_to_fit_widths ( box ) ;
2020-12-06 01:08:14 +01:00
2022-06-05 11:45:46 +02:00
// Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
width = CSS : : Length ( min ( max ( result . preferred_minimum_width , available_width ) , result . preferred_width ) , CSS : : Length : : Type : : Px ) ;
}
return width ;
} ;
2022-09-13 10:47:44 +02:00
auto input_width = [ & ] {
if ( should_treat_width_as_auto ( box ) )
return CSS : : Length : : make_auto ( ) ;
return computed_values . width ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
} ( ) ;
2022-06-05 11:45:46 +02:00
// 1. The tentative used width is calculated (without 'min-width' and 'max-width')
2022-09-13 10:47:44 +02:00
auto width = compute_width ( input_width ) ;
2022-06-05 11:45:46 +02:00
// 2. The tentative used width is greater than 'max-width', the rules above are applied again,
// but this time using the computed value of 'max-width' as the computed value for 'width'.
2022-07-07 12:36:02 +02:00
auto specified_max_width = computed_values . max_width ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
2022-06-05 11:45:46 +02:00
if ( ! specified_max_width . is_auto ( ) ) {
if ( width . to_px ( box ) > specified_max_width . to_px ( box ) )
width = compute_width ( specified_max_width ) ;
}
2020-12-06 01:08:14 +01:00
2022-06-05 11:45:46 +02:00
// 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
// but this time using the value of 'min-width' as the computed value for 'width'.
2022-07-07 12:36:02 +02:00
auto specified_min_width = computed_values . min_width ( ) . resolved ( box , width_of_containing_block_as_length ) . resolved ( box ) ;
2022-06-05 11:45:46 +02:00
if ( ! specified_min_width . is_auto ( ) ) {
if ( width . to_px ( box ) < specified_min_width . to_px ( box ) )
width = compute_width ( specified_min_width ) ;
2020-12-06 01:08:14 +01:00
}
2022-02-21 17:42:09 +01:00
auto & box_state = m_state . get_mutable ( box ) ;
2022-07-17 17:59:02 +02:00
box_state . set_content_width ( width . to_px ( box ) ) ;
2022-02-20 15:51:24 +01:00
box_state . margin_left = margin_left . to_px ( box ) ;
box_state . margin_right = margin_right . to_px ( box ) ;
box_state . border_left = computed_values . border_left ( ) . width ;
box_state . border_right = computed_values . border_right ( ) . width ;
box_state . padding_left = padding_left . to_px ( box ) ;
box_state . padding_right = padding_right . to_px ( box ) ;
2020-12-06 01:08:14 +01:00
}
2022-02-20 15:51:24 +01:00
void BlockFormattingContext : : compute_width_for_block_level_replaced_element_in_normal_flow ( ReplacedBox const & box )
2020-12-11 22:27:09 +01:00
{
2022-07-17 17:59:02 +02:00
m_state . get_mutable ( box ) . set_content_width ( compute_width_for_replaced_element ( m_state , box ) ) ;
2020-12-11 22:27:09 +01:00
}
2022-07-16 23:30:32 +02:00
float BlockFormattingContext : : compute_theoretical_height ( LayoutState const & state , Box const & box )
2021-03-08 22:49:34 +01:00
{
2022-02-20 15:51:24 +01:00
auto const & computed_values = box . computed_values ( ) ;
2022-07-09 15:17:47 +02:00
auto containing_block_height = CSS : : Length : : make_px ( containing_block_height_for ( box , state ) ) ;
2022-01-19 16:19:43 +00:00
2021-03-08 22:49:34 +01:00
// Then work out what the height is, based on box type and CSS properties.
float height = 0 ;
if ( is < ReplacedBox > ( box ) ) {
2022-02-20 15:51:24 +01:00
height = compute_height_for_replaced_element ( state , verify_cast < ReplacedBox > ( box ) ) ;
2021-03-08 22:49:34 +01:00
} else {
2022-09-13 10:47:44 +02:00
if ( should_treat_height_as_auto ( box , state ) ) {
2022-03-01 18:51:25 +01:00
height = compute_auto_height_for_block_level_element ( state , box ) ;
2022-09-09 15:10:11 +02:00
} else {
2022-07-07 12:36:02 +02:00
height = computed_values . height ( ) . resolved ( box , containing_block_height ) . to_px ( box ) ;
2022-09-09 15:10:11 +02:00
}
2020-11-22 13:38:18 +01:00
}
2021-03-08 22:49:34 +01:00
2022-07-07 12:36:02 +02:00
auto specified_max_height = computed_values . max_height ( ) . resolved ( box , containing_block_height ) . resolved ( box ) ;
2022-08-07 20:22:55 +01:00
if ( ! specified_max_height . is_auto ( ) )
2021-04-29 18:20:56 +03:00
height = min ( height , specified_max_height . to_px ( box ) ) ;
2022-07-07 12:36:02 +02:00
auto specified_min_height = computed_values . min_height ( ) . resolved ( box , containing_block_height ) . resolved ( box ) ;
2022-08-07 20:22:55 +01:00
if ( ! specified_min_height . is_auto ( ) )
2021-04-29 18:20:56 +03:00
height = max ( height , specified_min_height . to_px ( box ) ) ;
2021-05-30 21:57:13 +02:00
return height ;
}
2022-07-16 23:30:32 +02:00
void BlockFormattingContext : : compute_height ( Box const & box , LayoutState & state )
2021-05-30 21:57:13 +02:00
{
2022-09-18 19:10:45 +02:00
resolve_vertical_box_model_metrics ( box , * box . containing_block ( ) , state ) ;
state . get_mutable ( box ) . set_content_height ( compute_theoretical_height ( state , box ) ) ;
2021-03-29 12:04:26 -04:00
}
2022-02-20 15:51:24 +01:00
void BlockFormattingContext : : layout_inline_children ( BlockContainer const & block_container , LayoutMode layout_mode )
2020-11-22 13:38:18 +01:00
{
2022-02-12 19:54:09 +01:00
VERIFY ( block_container . children_are_inline ( ) ) ;
2022-07-09 15:17:47 +02:00
auto & block_container_state = m_state . get_mutable ( block_container ) ;
2022-07-20 18:12:12 +02:00
if ( layout_mode = = LayoutMode : : IntrinsicSizing ) {
2022-09-13 10:47:44 +02:00
if ( should_treat_width_as_auto ( block_container ) | | block_container_state . width_constraint ! = SizeConstraint : : None )
2022-07-17 17:59:02 +02:00
block_container_state . set_content_width ( containing_block_width_for ( block_container ) ) ;
2022-09-13 10:47:44 +02:00
if ( should_treat_height_as_auto ( block_container ) | | block_container_state . height_constraint ! = SizeConstraint : : None )
2022-07-17 17:59:02 +02:00
block_container_state . set_content_height ( containing_block_height_for ( block_container ) ) ;
2022-07-09 15:17:47 +02:00
}
2022-02-19 20:13:47 +01:00
InlineFormattingContext context ( m_state , block_container , * this ) ;
2021-10-06 21:53:25 +02:00
context . run ( block_container , layout_mode ) ;
2022-07-08 22:07:42 +02:00
float max_line_width = 0 ;
float content_height = 0 ;
for ( auto & line_box : block_container_state . line_boxes ) {
max_line_width = max ( max_line_width , line_box . width ( ) ) ;
content_height + = line_box . height ( ) ;
}
2022-07-20 18:12:12 +02:00
if ( layout_mode = = LayoutMode : : IntrinsicSizing ) {
2022-09-13 10:47:44 +02:00
if ( should_treat_width_as_auto ( block_container ) | | block_container_state . width_constraint ! = SizeConstraint : : None )
2022-07-17 17:59:02 +02:00
block_container_state . set_content_width ( max_line_width ) ;
2022-07-08 22:07:42 +02:00
}
2022-07-09 15:17:47 +02:00
// FIXME: This is weird. Figure out a way to make callers responsible for setting the content height.
2022-07-17 17:59:02 +02:00
block_container_state . set_content_height ( content_height ) ;
2020-11-22 13:38:18 +01:00
}
2022-09-08 14:31:35 +02:00
void BlockFormattingContext : : layout_block_level_box ( Box const & box , BlockContainer const & block_container , LayoutMode layout_mode , float & bottom_of_lowest_margin_box )
2020-11-22 13:38:18 +01:00
{
2022-07-09 20:14:13 +02:00
auto & box_state = m_state . get_mutable ( box ) ;
2020-11-22 13:38:18 +01:00
2022-07-09 20:14:13 +02:00
if ( box . is_absolutely_positioned ( ) ) {
m_absolutely_positioned_boxes . append ( box ) ;
return ;
2022-07-09 15:17:47 +02:00
}
2022-07-09 20:14:13 +02:00
// NOTE: ListItemMarkerBoxes are placed by their corresponding ListItemBox.
if ( is < ListItemMarkerBox > ( box ) )
return ;
2022-02-20 15:51:24 +01:00
2022-07-09 20:14:13 +02:00
if ( box . is_floating ( ) ) {
layout_floating_box ( box , block_container , layout_mode ) ;
2022-09-08 14:31:35 +02:00
bottom_of_lowest_margin_box = max ( bottom_of_lowest_margin_box , box_state . offset . y ( ) + box_state . content_height ( ) + box_state . margin_box_bottom ( ) ) ;
2022-07-09 20:14:13 +02:00
return ;
}
2020-12-06 19:48:02 +01:00
2022-07-09 20:14:13 +02:00
compute_width ( box , layout_mode ) ;
if ( is < ReplacedBox > ( box ) | | is < BlockContainer > ( box ) )
place_block_level_element_in_normal_flow_vertically ( box , block_container ) ;
2022-07-17 18:46:38 +02:00
if ( box_state . has_definite_height ( ) ) {
2022-07-09 20:14:13 +02:00
compute_height ( box , m_state ) ;
}
2021-10-28 19:35:54 +02:00
2022-07-09 20:14:13 +02:00
OwnPtr < FormattingContext > independent_formatting_context ;
if ( box . can_have_children ( ) ) {
if ( box . children_are_inline ( ) ) {
layout_inline_children ( verify_cast < BlockContainer > ( box ) , layout_mode ) ;
} else {
independent_formatting_context = create_independent_formatting_context_if_needed ( m_state , box ) ;
if ( independent_formatting_context )
independent_formatting_context - > run ( box , layout_mode ) ;
else
layout_block_level_children ( verify_cast < BlockContainer > ( box ) , layout_mode ) ;
2020-12-12 19:31:46 +01:00
}
2022-07-09 20:14:13 +02:00
}
2020-11-22 13:38:18 +01:00
2022-07-09 20:14:13 +02:00
compute_height ( box , m_state ) ;
2022-01-24 01:56:15 +01:00
2022-07-09 20:14:13 +02:00
compute_inset ( box ) ;
2022-02-15 01:03:39 +01:00
2022-07-09 20:14:13 +02:00
if ( is < ReplacedBox > ( box ) | | is < BlockContainer > ( box ) )
place_block_level_element_in_normal_flow_horizontally ( box , block_container ) ;
2022-02-12 19:54:09 +01:00
2022-07-09 20:14:13 +02:00
if ( is < ListItemBox > ( box ) ) {
layout_list_item_marker ( static_cast < ListItemBox const & > ( box ) ) ;
}
2020-11-22 13:38:18 +01:00
2022-09-08 14:31:35 +02:00
bottom_of_lowest_margin_box = max ( bottom_of_lowest_margin_box , box_state . offset . y ( ) + box_state . content_height ( ) + box_state . margin_box_bottom ( ) ) ;
2021-03-30 15:51:19 -04:00
2022-07-09 20:14:13 +02:00
if ( independent_formatting_context )
independent_formatting_context - > parent_context_did_dimension_child_root_box ( ) ;
}
2020-11-22 13:38:18 +01:00
2022-07-20 18:12:12 +02:00
void BlockFormattingContext : : run_intrinsic_sizing ( Box const & box )
2022-07-10 22:06:20 +02:00
{
auto & box_state = m_state . get_mutable ( box ) ;
2022-07-17 18:46:38 +02:00
if ( box_state . has_definite_width ( ) )
2022-07-17 17:59:02 +02:00
box_state . set_content_width ( box . computed_values ( ) . width ( ) . resolved ( box , CSS : : Length : : make_px ( containing_block_width_for ( box ) ) ) . to_px ( box ) ) ;
2022-07-10 22:06:20 +02:00
2022-07-17 18:46:38 +02:00
if ( box_state . has_definite_height ( ) )
2022-07-17 17:59:02 +02:00
box_state . set_content_height ( box . computed_values ( ) . height ( ) . resolved ( box , CSS : : Length : : make_px ( containing_block_height_for ( box ) ) ) . to_px ( box ) ) ;
2022-07-10 22:06:20 +02:00
2022-07-20 18:12:12 +02:00
run ( box , LayoutMode : : IntrinsicSizing ) ;
2022-07-10 22:06:20 +02:00
}
2022-07-09 20:14:13 +02:00
void BlockFormattingContext : : layout_block_level_children ( BlockContainer const & block_container , LayoutMode layout_mode )
{
VERIFY ( ! block_container . children_are_inline ( ) ) ;
2020-11-22 13:38:18 +01:00
2022-09-08 14:31:35 +02:00
float bottom_of_lowest_margin_box = 0 ;
2022-02-12 19:54:09 +01:00
2022-07-20 18:12:12 +02:00
if ( layout_mode = = LayoutMode : : IntrinsicSizing ) {
2022-07-09 20:14:13 +02:00
auto & block_container_state = m_state . get_mutable ( block_container ) ;
2022-09-13 10:47:44 +02:00
if ( should_treat_width_as_auto ( block_container ) | | block_container_state . width_constraint ! = SizeConstraint : : None )
2022-07-17 17:59:02 +02:00
block_container_state . set_content_width ( containing_block_width_for ( block_container ) ) ;
2022-09-13 10:47:44 +02:00
if ( should_treat_height_as_auto ( block_container ) | | block_container_state . height_constraint ! = SizeConstraint : : None )
2022-07-17 17:59:02 +02:00
block_container_state . set_content_height ( containing_block_height_for ( block_container ) ) ;
2022-07-09 20:14:13 +02:00
}
2022-02-12 19:54:09 +01:00
2022-07-09 20:14:13 +02:00
block_container . for_each_child_of_type < Box > ( [ & ] ( Box & box ) {
2022-09-08 14:31:35 +02:00
layout_block_level_box ( box , block_container , layout_mode , bottom_of_lowest_margin_box ) ;
2020-11-22 13:38:18 +01:00
return IterationDecision : : Continue ;
} ) ;
2022-07-20 18:12:12 +02:00
if ( layout_mode = = LayoutMode : : IntrinsicSizing ) {
2022-07-09 15:17:47 +02:00
auto & block_container_state = m_state . get_mutable ( block_container ) ;
2022-09-13 10:47:44 +02:00
if ( should_treat_width_as_auto ( block_container ) | | block_container_state . width_constraint ! = SizeConstraint : : None )
2022-07-17 17:59:02 +02:00
block_container_state . set_content_width ( greatest_child_width ( block_container ) ) ;
2022-09-13 10:47:44 +02:00
if ( should_treat_height_as_auto ( block_container ) | | block_container_state . height_constraint ! = SizeConstraint : : None )
2022-09-08 14:31:35 +02:00
block_container_state . set_content_height ( bottom_of_lowest_margin_box ) ;
2020-11-22 13:38:18 +01:00
}
}
2022-09-18 19:10:45 +02:00
void BlockFormattingContext : : resolve_vertical_box_model_metrics ( Box const & box , BlockContainer const & containing_block , LayoutState & state )
2022-01-24 01:23:02 +01:00
{
2022-09-18 19:10:45 +02:00
auto & box_state = state . get_mutable ( box ) ;
2022-02-20 15:51:24 +01:00
auto const & computed_values = box . computed_values ( ) ;
2022-09-18 19:10:45 +02:00
auto width_of_containing_block = CSS : : Length : : make_px ( containing_block_width_for ( box , state ) ) ;
2022-02-20 15:51:24 +01:00
2022-09-13 17:42:39 +02:00
box_state . margin_top = computed_values . margin ( ) . top ( ) . resolved ( box , width_of_containing_block ) . resolved ( containing_block ) . to_px ( box ) ;
box_state . margin_bottom = computed_values . margin ( ) . bottom ( ) . resolved ( box , width_of_containing_block ) . resolved ( containing_block ) . to_px ( box ) ;
2022-02-20 15:51:24 +01:00
box_state . border_top = computed_values . border_top ( ) . width ;
box_state . border_bottom = computed_values . border_bottom ( ) . width ;
2022-09-13 17:42:39 +02:00
box_state . padding_top = computed_values . padding ( ) . top ( ) . resolved ( box , width_of_containing_block ) . resolved ( containing_block ) . to_px ( box ) ;
box_state . padding_bottom = computed_values . padding ( ) . bottom ( ) . resolved ( box , width_of_containing_block ) . resolved ( containing_block ) . to_px ( box ) ;
2022-01-24 01:23:02 +01:00
}
2022-02-20 15:51:24 +01:00
void BlockFormattingContext : : place_block_level_element_in_normal_flow_vertically ( Box const & child_box , BlockContainer const & containing_block )
2020-11-22 13:38:18 +01:00
{
2022-02-21 17:42:09 +01:00
auto & box_state = m_state . get_mutable ( child_box ) ;
2022-01-24 01:23:02 +01:00
auto const & computed_values = child_box . computed_values ( ) ;
2020-11-22 13:38:18 +01:00
2022-09-18 19:10:45 +02:00
resolve_vertical_box_model_metrics ( child_box , containing_block , m_state ) ;
2020-11-22 13:38:18 +01:00
2022-08-10 19:19:44 +02:00
auto y = FormattingContext : : compute_box_y_position_with_respect_to_siblings ( child_box , box_state ) ;
2020-11-22 13:38:18 +01:00
2022-01-23 01:19:28 +01:00
auto clear_floating_boxes = [ & ] ( FloatSideData & float_side ) {
2022-03-18 14:44:36 +01:00
if ( ! float_side . current_boxes . is_empty ( ) ) {
2020-12-06 01:45:51 +01:00
float clearance_y = 0 ;
2022-03-18 14:44:36 +01:00
for ( auto const & floating_box : float_side . current_boxes ) {
auto const & floating_box_state = m_state . get ( floating_box . box ) ;
2022-02-20 15:51:24 +01:00
clearance_y = max ( clearance_y , floating_box_state . offset . y ( ) + floating_box_state . border_box_height ( ) ) ;
2020-12-06 01:45:51 +01:00
}
y = max ( y , clearance_y ) ;
2022-03-18 14:44:36 +01:00
float_side . clear ( ) ;
2020-12-06 01:45:51 +01:00
}
2021-05-11 18:04:18 -04:00
} ;
2020-12-06 01:45:51 +01:00
2021-05-29 23:03:05 +02:00
// Flex-items don't float and also don't clear.
if ( ( computed_values . clear ( ) = = CSS : : Clear : : Left | | computed_values . clear ( ) = = CSS : : Clear : : Both ) & & ! child_box . is_flex_item ( ) )
2022-01-22 15:19:18 +01:00
clear_floating_boxes ( m_left_floats ) ;
2021-05-29 23:03:05 +02:00
if ( ( computed_values . clear ( ) = = CSS : : Clear : : Right | | computed_values . clear ( ) = = CSS : : Clear : : Both ) & & ! child_box . is_flex_item ( ) )
2022-01-22 15:19:18 +01:00
clear_floating_boxes ( m_right_floats ) ;
2020-12-06 01:45:51 +01:00
2022-02-20 15:51:24 +01:00
box_state . offset = Gfx : : FloatPoint { box_state . offset . x ( ) , y } ;
2022-01-24 01:56:15 +01:00
}
2022-02-20 15:51:24 +01:00
void BlockFormattingContext : : place_block_level_element_in_normal_flow_horizontally ( Box const & child_box , BlockContainer const & containing_block )
2022-01-24 01:56:15 +01:00
{
2022-02-21 17:42:09 +01:00
auto & box_state = m_state . get_mutable ( child_box ) ;
2022-01-24 01:56:15 +01:00
float x = 0 ;
2022-07-09 15:17:47 +02:00
float available_width_within_containing_block = containing_block_width_for ( child_box ) ;
2022-03-18 14:44:36 +01:00
if ( ( ! m_left_floats . current_boxes . is_empty ( ) | | ! m_right_floats . current_boxes . is_empty ( ) )
& & creates_block_formatting_context ( child_box ) ) {
available_width_within_containing_block - = m_left_floats . current_width + m_right_floats . current_width ;
x + = m_left_floats . current_width ;
}
2022-01-24 01:56:15 +01:00
if ( containing_block . computed_values ( ) . text_align ( ) = = CSS : : TextAlign : : LibwebCenter ) {
2022-07-17 17:59:02 +02:00
x + = ( available_width_within_containing_block / 2 ) - box_state . content_width ( ) / 2 ;
2022-01-24 01:56:15 +01:00
} else {
2022-03-27 15:29:00 +02:00
x + = box_state . margin_box_left ( ) ;
2022-01-24 01:56:15 +01:00
}
2022-02-20 15:51:24 +01:00
box_state . offset = Gfx : : FloatPoint { x , box_state . offset . y ( ) } ;
2020-11-22 13:38:18 +01:00
}
2022-07-16 23:30:32 +02:00
static void measure_scrollable_overflow ( LayoutState const & state , Box const & box , float & bottom_edge , float & right_edge )
2022-04-10 20:01:35 +02:00
{
auto const & child_state = state . get ( box ) ;
auto child_rect = absolute_content_rect ( box , state ) ;
child_rect . inflate ( child_state . border_box_top ( ) , child_state . border_box_right ( ) , child_state . border_box_bottom ( ) , child_state . border_box_left ( ) ) ;
bottom_edge = max ( bottom_edge , child_rect . bottom ( ) ) ;
right_edge = max ( right_edge , child_rect . right ( ) ) ;
if ( box . computed_values ( ) . overflow_x ( ) = = CSS : : Overflow : : Hidden & & box . computed_values ( ) . overflow_y ( ) = = CSS : : Overflow : : Hidden )
return ;
box . for_each_child_of_type < Box > ( [ & ] ( Box const & child ) {
measure_scrollable_overflow ( state , child , bottom_edge , right_edge ) ;
return IterationDecision : : Continue ;
} ) ;
}
2020-11-22 13:38:18 +01:00
void BlockFormattingContext : : layout_initial_containing_block ( LayoutMode layout_mode )
{
2021-10-06 19:47:10 +02:00
auto viewport_rect = root ( ) . browsing_context ( ) . viewport_rect ( ) ;
2020-11-22 13:38:18 +01:00
2021-10-06 19:47:10 +02:00
auto & icb = verify_cast < Layout : : InitialContainingBlock > ( root ( ) ) ;
2022-02-21 17:42:09 +01:00
auto & icb_state = m_state . get_mutable ( icb ) ;
2020-11-22 13:38:18 +01:00
2022-03-27 23:37:45 +02:00
if ( root ( ) . children_are_inline ( ) )
layout_inline_children ( root ( ) , layout_mode ) ;
else
layout_block_level_children ( root ( ) , layout_mode ) ;
2020-11-22 13:38:18 +01:00
2021-10-15 00:10:41 +02:00
float bottom_edge = 0 ;
float right_edge = 0 ;
2022-04-10 20:01:35 +02:00
measure_scrollable_overflow ( m_state , icb , bottom_edge , right_edge ) ;
2020-12-17 00:58:23 +01:00
2021-10-15 00:10:41 +02:00
if ( bottom_edge > = viewport_rect . height ( ) | | right_edge > = viewport_rect . width ( ) ) {
2022-07-16 23:30:32 +02:00
// FIXME: Move overflow data to LayoutState!
2022-02-21 11:22:08 +01:00
auto & overflow_data = icb_state . ensure_overflow_data ( ) ;
2021-10-14 22:23:20 +02:00
overflow_data . scrollable_overflow_rect = viewport_rect . to_type < float > ( ) ;
2021-10-23 16:06:29 +02:00
// NOTE: The edges are *within* the rectangle, so we add 1 to get the width and height.
overflow_data . scrollable_overflow_rect . set_size ( right_edge + 1 , bottom_edge + 1 ) ;
2021-10-14 22:23:20 +02:00
}
2020-11-22 13:38:18 +01:00
}
2022-03-22 19:18:05 +01:00
void BlockFormattingContext : : layout_floating_box ( Box const & box , BlockContainer const & containing_block , LayoutMode layout_mode , LineBuilder * line_builder )
2020-12-05 20:10:39 +01:00
{
2021-02-23 20:42:32 +01:00
VERIFY ( box . is_floating ( ) ) ;
2020-12-05 20:10:39 +01:00
2022-02-21 17:42:09 +01:00
auto & box_state = m_state . get_mutable ( box ) ;
2022-07-09 15:17:47 +02:00
float width_of_containing_block = containing_block_width_for ( box ) ;
2022-02-20 15:51:24 +01:00
2022-03-18 14:44:36 +01:00
compute_width ( box , layout_mode ) ;
( void ) layout_inside ( box , layout_mode ) ;
2022-02-20 15:51:24 +01:00
compute_height ( box , m_state ) ;
2020-12-05 20:10:39 +01:00
2020-12-06 16:51:19 +01:00
// First we place the box normally (to get the right y coordinate.)
2022-03-22 19:18:05 +01:00
// If we have a LineBuilder, we're in the middle of inline layout, otherwise this is block layout.
if ( line_builder ) {
2022-09-16 14:21:52 +02:00
auto y = line_builder - > y_for_float_to_be_inserted_here ( box ) ;
box_state . offset . set_y ( y + box_state . margin_box_top ( ) ) ;
2022-03-22 19:18:05 +01:00
} else {
place_block_level_element_in_normal_flow_vertically ( box , containing_block ) ;
place_block_level_element_in_normal_flow_horizontally ( box , containing_block ) ;
}
2020-12-06 16:51:19 +01:00
2022-03-26 22:10:09 +01:00
// Then we float it to the left or right.
2022-09-16 14:21:52 +02:00
auto float_box = [ & ] ( FloatSide side , FloatSideData & side_data , FloatSideData & other_side_data ) {
2022-03-18 14:44:36 +01:00
float offset_from_edge = 0 ;
auto float_to_edge = [ & ] {
if ( side = = FloatSide : : Left )
offset_from_edge = box_state . margin_box_left ( ) ;
else
2022-07-17 17:59:02 +02:00
offset_from_edge = box_state . content_width ( ) + box_state . margin_box_right ( ) ;
2022-03-18 14:44:36 +01:00
} ;
2022-09-08 13:21:57 +02:00
auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space ( box , root ( ) , m_state ) ;
2022-01-23 01:19:28 +01:00
float y_in_root = box_in_root_rect . y ( ) ;
2022-02-20 15:51:24 +01:00
float y = box_state . offset . y ( ) ;
2022-01-23 01:19:28 +01:00
2022-03-18 14:44:36 +01:00
if ( side_data . current_boxes . is_empty ( ) ) {
2022-01-23 01:19:28 +01:00
// This is the first floating box on this side. Go all the way to the edge.
2022-03-18 14:44:36 +01:00
float_to_edge ( ) ;
2022-01-23 01:19:28 +01:00
side_data . y_offset = 0 ;
2020-12-06 20:57:17 +01:00
} else {
2022-03-18 14:44:36 +01:00
auto & previous_box = side_data . current_boxes . last ( ) ;
2022-01-23 01:19:28 +01:00
2022-03-18 14:44:36 +01:00
float wanted_offset_from_edge = 0 ;
2022-01-23 01:19:28 +01:00
bool fits_on_line = false ;
if ( side = = FloatSide : : Left ) {
2022-03-26 22:10:09 +01:00
wanted_offset_from_edge = side_data . current_width + box_state . margin_box_left ( ) ;
2022-07-17 17:59:02 +02:00
fits_on_line = ( wanted_offset_from_edge + box_state . content_width ( ) + box_state . margin_box_right ( ) ) < = width_of_containing_block ;
2020-12-06 20:57:17 +01:00
} else {
2022-07-17 17:59:02 +02:00
wanted_offset_from_edge = side_data . current_width + box_state . margin_box_right ( ) + box_state . content_width ( ) ;
2022-03-26 22:10:09 +01:00
fits_on_line = ( wanted_offset_from_edge - box_state . margin_box_left ( ) ) > = 0 ;
2022-01-23 01:19:28 +01:00
}
if ( fits_on_line ) {
2022-09-07 13:29:58 +02:00
auto const previous_rect = border_box_rect_in_ancestor_coordinate_space ( previous_box . box , root ( ) , m_state ) ;
2022-01-23 01:19:28 +01:00
if ( previous_rect . contains_vertically ( y_in_root + side_data . y_offset ) ) {
// This box touches another already floating box. Stack after others.
2022-03-18 14:44:36 +01:00
offset_from_edge = wanted_offset_from_edge ;
2022-01-23 01:19:28 +01:00
} else {
2022-02-21 20:39:30 +01:00
// This box does not touch another floating box, go all the way to the edge.
2022-03-18 14:44:36 +01:00
float_to_edge ( ) ;
2022-01-23 01:19:28 +01:00
// Also, forget all previous boxes floated to this side while since they're no longer relevant.
2022-03-18 14:44:36 +01:00
side_data . clear ( ) ;
2022-01-23 01:19:28 +01:00
}
} else {
// We ran out of horizontal space on this "float line", and need to break.
2022-03-18 14:44:36 +01:00
float_to_edge ( ) ;
2022-09-09 11:11:22 +02:00
float lowest_margin_edge = 0 ;
2022-03-18 14:44:36 +01:00
for ( auto const & box : side_data . current_boxes ) {
auto const & box_state = m_state . get ( box . box ) ;
2022-09-09 11:11:22 +02:00
lowest_margin_edge = max ( lowest_margin_edge , box_state . margin_box_height ( ) ) ;
2022-02-20 15:51:24 +01:00
}
2022-01-23 01:19:28 +01:00
2022-09-09 11:11:22 +02:00
side_data . y_offset + = lowest_margin_edge ;
2022-01-23 01:19:28 +01:00
// Also, forget all previous boxes floated to this side while since they're no longer relevant.
2022-03-18 14:44:36 +01:00
side_data . clear ( ) ;
2020-12-06 20:57:17 +01:00
}
2020-12-06 16:51:19 +01:00
}
2022-09-16 14:21:52 +02:00
// NOTE: If we're in inline layout, the LineBuilder has already provided the right Y offset.
// In block layout, we adjust by the side's current Y offset here.
// FIXME: It's annoying that we have different behavior for inline vs block here.
// Find a way to unify the behavior so we don't need to branch here.
if ( ! line_builder )
y + = side_data . y_offset ;
2022-03-18 14:44:36 +01:00
side_data . all_boxes . append ( adopt_own ( * new FloatingBox {
. box = box ,
. offset_from_edge = offset_from_edge ,
. top_margin_edge = y - box_state . margin_box_top ( ) ,
2022-07-17 17:59:02 +02:00
. bottom_margin_edge = y + box_state . content_height ( ) + box_state . margin_box_bottom ( ) ,
2022-03-18 14:44:36 +01:00
} ) ) ;
side_data . current_boxes . append ( * side_data . all_boxes . last ( ) ) ;
if ( side = = FloatSide : : Left ) {
2022-07-17 17:59:02 +02:00
side_data . current_width = offset_from_edge + box_state . content_width ( ) + box_state . margin_box_right ( ) ;
2022-03-18 14:44:36 +01:00
} else {
side_data . current_width = offset_from_edge + box_state . margin_box_left ( ) ;
}
side_data . max_width = max ( side_data . current_width , side_data . max_width ) ;
2020-12-06 16:51:19 +01:00
2022-03-18 14:44:36 +01:00
// NOTE: We don't set the X position here, that happens later, once we know the root block width.
// See parent_context_did_dimension_child_root_box() for that logic.
box_state . offset . set_y ( y ) ;
2022-09-16 14:21:52 +02:00
// If the new box was inserted below the bottom of the opposite side,
// we reset the other side back to its edge.
if ( y > other_side_data . y_offset )
other_side_data . clear ( ) ;
2022-01-23 01:19:28 +01:00
} ;
2020-12-05 20:10:39 +01:00
2022-01-23 01:19:28 +01:00
// Next, float to the left and/or right
if ( box . computed_values ( ) . float_ ( ) = = CSS : : Float : : Left ) {
2022-09-16 14:21:52 +02:00
float_box ( FloatSide : : Left , m_left_floats , m_right_floats ) ;
2022-01-23 01:19:28 +01:00
} else if ( box . computed_values ( ) . float_ ( ) = = CSS : : Float : : Right ) {
2022-09-16 14:21:52 +02:00
float_box ( FloatSide : : Right , m_right_floats , m_left_floats ) ;
2022-01-23 01:19:28 +01:00
}
2022-09-09 23:56:55 +02:00
m_state . get_mutable ( root ( ) ) . add_floating_descendant ( box ) ;
2022-09-16 14:21:52 +02:00
if ( line_builder )
line_builder - > recalculate_available_space ( ) ;
2022-01-23 01:19:28 +01:00
}
2022-02-21 01:43:37 +01:00
void BlockFormattingContext : : layout_list_item_marker ( ListItemBox const & list_item_box )
{
if ( ! list_item_box . marker ( ) )
return ;
auto & marker = * list_item_box . marker ( ) ;
2022-02-21 17:42:09 +01:00
auto & marker_state = m_state . get_mutable ( marker ) ;
auto & list_item_state = m_state . get_mutable ( list_item_box ) ;
2022-02-21 01:43:37 +01:00
int image_width = 0 ;
int image_height = 0 ;
2022-07-31 01:11:59 +01:00
if ( auto const * list_style_image = marker . list_style_image ( ) ) {
image_width = list_style_image - > natural_width ( ) . value_or ( 0 ) ;
image_height = list_style_image - > natural_height ( ) . value_or ( 0 ) ;
2022-02-21 01:43:37 +01:00
}
2022-07-07 01:40:05 +01:00
int default_marker_width = max ( 4 , marker . font ( ) . glyph_height ( ) - 4 ) ;
2022-02-21 01:43:37 +01:00
if ( marker . text ( ) . is_empty ( ) ) {
2022-07-17 17:59:02 +02:00
marker_state . set_content_width ( image_width + default_marker_width ) ;
2022-02-21 01:43:37 +01:00
} else {
auto text_width = marker . font ( ) . width ( marker . text ( ) ) ;
2022-07-17 17:59:02 +02:00
marker_state . set_content_width ( image_width + text_width ) ;
2022-02-21 01:43:37 +01:00
}
2022-07-17 17:59:02 +02:00
marker_state . set_content_height ( max ( image_height , marker . font ( ) . glyph_height ( ) + 1 ) ) ;
2022-02-21 01:43:37 +01:00
2022-07-07 01:40:05 +01:00
marker_state . offset = {
2022-07-17 17:59:02 +02:00
- ( marker_state . content_width ( ) + default_marker_width ) ,
max ( 0.f , ( marker . line_height ( ) - marker_state . content_height ( ) ) / 2.f )
2022-07-07 01:40:05 +01:00
} ;
2022-02-21 01:43:37 +01:00
2022-07-17 17:59:02 +02:00
if ( marker_state . content_height ( ) > list_item_state . content_height ( ) )
list_item_state . set_content_height ( marker_state . content_height ( ) ) ;
2022-02-21 01:43:37 +01:00
}
2022-03-18 14:44:36 +01:00
BlockFormattingContext : : SpaceUsedByFloats BlockFormattingContext : : space_used_by_floats ( float y ) const
2022-03-17 12:30:30 +01:00
{
2022-03-18 14:44:36 +01:00
SpaceUsedByFloats space_used_by_floats ;
2022-03-17 12:30:30 +01:00
2022-09-16 14:21:52 +02:00
for ( auto const & floating_box_ptr : m_left_floats . all_boxes . in_reverse ( ) ) {
auto const & floating_box = * floating_box_ptr ;
2022-03-18 14:44:36 +01:00
auto const & floating_box_state = m_state . get ( floating_box . box ) ;
// NOTE: The floating box is *not* in the final horizontal position yet, but the size and vertical position is valid.
2022-09-07 13:29:58 +02:00
auto rect = border_box_rect_in_ancestor_coordinate_space ( floating_box . box , root ( ) , m_state ) ;
2022-03-17 12:30:30 +01:00
if ( rect . contains_vertically ( y ) ) {
2022-03-18 14:44:36 +01:00
space_used_by_floats . left = floating_box . offset_from_edge
2022-07-17 17:59:02 +02:00
+ floating_box_state . content_width ( )
2022-03-18 14:44:36 +01:00
+ floating_box_state . margin_box_right ( ) ;
2022-03-17 12:30:30 +01:00
break ;
}
}
2022-09-16 14:21:52 +02:00
for ( auto const & floating_box_ptr : m_right_floats . all_boxes . in_reverse ( ) ) {
auto const & floating_box = * floating_box_ptr ;
2022-03-18 14:44:36 +01:00
auto const & floating_box_state = m_state . get ( floating_box . box ) ;
// NOTE: The floating box is *not* in the final horizontal position yet, but the size and vertical position is valid.
2022-09-07 13:29:58 +02:00
auto rect = border_box_rect_in_ancestor_coordinate_space ( floating_box . box , root ( ) , m_state ) ;
2022-03-17 12:30:30 +01:00
if ( rect . contains_vertically ( y ) ) {
2022-03-18 14:44:36 +01:00
space_used_by_floats . right = floating_box . offset_from_edge
+ floating_box_state . margin_box_left ( ) ;
2022-03-17 12:30:30 +01:00
break ;
}
}
2022-03-18 14:44:36 +01:00
return space_used_by_floats ;
}
float BlockFormattingContext : : greatest_child_width ( Box const & box )
{
// Similar to FormattingContext::greatest_child_width()
// but this one takes floats into account!
float max_width = m_left_floats . max_width + m_right_floats . max_width ;
if ( box . children_are_inline ( ) ) {
for ( auto const & line_box : m_state . get ( verify_cast < BlockContainer > ( box ) ) . line_boxes ) {
auto width_here = line_box . width ( ) ;
float extra_width_from_left_floats = 0 ;
for ( auto & left_float : m_left_floats . all_boxes ) {
if ( line_box . baseline ( ) > = left_float - > top_margin_edge | | line_box . baseline ( ) < = left_float - > bottom_margin_edge ) {
auto const & left_float_state = m_state . get ( left_float - > box ) ;
2022-07-17 17:59:02 +02:00
extra_width_from_left_floats = max ( extra_width_from_left_floats , left_float - > offset_from_edge + left_float_state . content_width ( ) + left_float_state . margin_box_right ( ) ) ;
2022-03-18 14:44:36 +01:00
}
}
float extra_width_from_right_floats = 0 ;
for ( auto & right_float : m_right_floats . all_boxes ) {
if ( line_box . baseline ( ) > = right_float - > top_margin_edge | | line_box . baseline ( ) < = right_float - > bottom_margin_edge ) {
auto const & right_float_state = m_state . get ( right_float - > box ) ;
extra_width_from_right_floats = max ( extra_width_from_right_floats , right_float - > offset_from_edge + right_float_state . margin_box_left ( ) ) ;
}
}
width_here + = extra_width_from_left_floats + extra_width_from_right_floats ;
max_width = max ( max_width , width_here ) ;
}
} else {
2022-09-13 18:53:11 +02:00
box . for_each_child_of_type < Box > ( [ & ] ( Box const & child ) {
if ( ! child . is_absolutely_positioned ( ) )
max_width = max ( max_width , m_state . get ( child ) . border_box_width ( ) ) ;
2022-03-18 14:44:36 +01:00
} ) ;
}
return max_width ;
2022-03-17 12:30:30 +01:00
}
2022-09-13 10:47:44 +02:00
bool BlockFormattingContext : : should_treat_width_as_auto ( Box const & box , LayoutState const & state )
{
return box . computed_values ( ) . width ( ) . is_auto ( )
2022-09-14 11:34:36 +02:00
| | ( box . computed_values ( ) . width ( ) . contains_percentage ( ) & & ! state . get ( * box . containing_block ( ) ) . has_definite_width ( ) ) ;
2022-09-13 10:47:44 +02:00
}
bool BlockFormattingContext : : should_treat_height_as_auto ( Box const & box , LayoutState const & state )
{
return box . computed_values ( ) . height ( ) . is_auto ( )
2022-09-14 11:34:36 +02:00
| | ( box . computed_values ( ) . height ( ) . contains_percentage ( ) & & ! state . get ( * box . containing_block ( ) ) . has_definite_height ( ) ) ;
2022-09-13 10:47:44 +02:00
}
2020-11-22 13:38:18 +01:00
}