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
*/
2023-02-10 17:29:38 +03:00
# include <AK/TemporaryChange.h>
2020-11-22 13:38:18 +01:00
# include <LibWeb/CSS/Length.h>
# include <LibWeb/DOM/Node.h>
2022-09-27 15:29:17 +02:00
# include <LibWeb/Dump.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>
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>
2023-05-20 17:44:18 +02:00
# include <LibWeb/Layout/SVGSVGBox.h>
2023-01-14 14:48:20 +01:00
# include <LibWeb/Layout/TableWrapper.h>
2023-02-25 11:04:29 +01:00
# include <LibWeb/Layout/Viewport.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
}
2023-03-19 09:57:31 +01:00
CSSPixels BlockFormattingContext : : automatic_content_width ( ) const
{
return greatest_child_width ( root ( ) ) ;
}
2022-11-23 17:46:10 +00:00
CSSPixels BlockFormattingContext : : automatic_content_height ( ) const
2022-09-24 13:39:43 +02:00
{
2022-09-27 15:29:17 +02:00
return compute_auto_height_for_block_formatting_context_root ( root ( ) ) ;
2022-09-24 13:39:43 +02:00
}
2023-04-27 04:34:32 +03:00
static bool margins_collapse_through ( Box const & box , LayoutState & state )
{
// FIXME: A box's own margins collapse if the 'min-height' property is zero, and it has neither top or bottom borders
// nor top or bottom padding, and it has a 'height' of either 0 or 'auto', and it does not contain a line box, and
// all of its in-flow children's margins (if any) collapse.
// https://www.w3.org/TR/CSS22/box.html#collapsing-margins
return state . get ( box ) . border_box_height ( ) = = 0 ;
}
2022-09-27 15:29:17 +02:00
void BlockFormattingContext : : run ( Box const & , LayoutMode layout_mode , AvailableSpace const & available_space )
2020-11-22 13:38:18 +01:00
{
2023-02-25 11:04:29 +01:00
if ( is < Viewport > ( root ( ) ) ) {
layout_viewport ( layout_mode , available_space ) ;
2020-11-22 13:38:18 +01:00
return ;
}
2022-02-12 19:54:09 +01:00
if ( root ( ) . children_are_inline ( ) )
2022-09-27 15:29:17 +02:00
layout_inline_children ( root ( ) , layout_mode , available_space ) ;
2022-02-12 19:54:09 +01:00
else
2022-09-27 15:29:17 +02:00
layout_block_level_children ( root ( ) , layout_mode , available_space ) ;
2022-12-28 21:42:46 +03:00
// Assign collapsed margin left after children layout of formatting context to the last child box
2022-11-04 17:37:12 +00:00
if ( m_margin_state . current_collapsed_margin ( ) ! = 0 ) {
2022-12-28 21:42:46 +03:00
for ( auto * child_box = root ( ) . last_child_of_type < Box > ( ) ; child_box ; child_box = child_box - > previous_sibling_of_type < Box > ( ) ) {
if ( child_box - > is_absolutely_positioned ( ) | | child_box - > is_floating ( ) )
continue ;
2023-04-27 04:34:32 +03:00
if ( margins_collapse_through ( * child_box , m_state ) )
continue ;
2022-11-04 17:37:12 +00:00
m_state . get_mutable ( * child_box ) . margin_bottom = m_margin_state . current_collapsed_margin ( ) . value ( ) ;
2022-12-28 21:42:46 +03:00
break ;
}
}
2022-02-12 19:54:09 +01:00
}
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 ) ;
2022-11-04 17:37:12 +00:00
box_state . set_content_x ( floating_box - > offset_from_edge . value ( ) ) ;
2022-03-18 14:44:36 +01:00
}
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-11-04 17:37:12 +00: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-11-04 17:37:12 +00:00
box_state . set_content_x ( ( float_containing_block_width - floating_box - > offset_from_edge ) . value ( ) ) ;
2022-03-18 14:44:36 +01:00
}
// We can also layout absolutely positioned boxes within this BFC.
2022-09-27 15:29:17 +02:00
for ( auto & box : m_absolutely_positioned_boxes ) {
2023-02-26 16:09:02 -07:00
auto & cb_state = m_state . get ( * box - > containing_block ( ) ) ;
2022-09-27 15:29:17 +02:00
auto available_width = AvailableSize : : make_definite ( cb_state . content_width ( ) + cb_state . padding_left + cb_state . padding_right ) ;
auto available_height = AvailableSize : : make_definite ( cb_state . content_height ( ) + cb_state . padding_top + cb_state . padding_bottom ) ;
layout_absolutely_positioned_element ( box , AvailableSpace ( available_width , available_height ) ) ;
}
2020-11-22 13:38:18 +01:00
}
2022-09-27 15:29:17 +02:00
void BlockFormattingContext : : compute_width ( Box const & box , AvailableSpace const & available_space , LayoutMode )
2020-11-22 13:38:18 +01:00
{
2021-01-06 18:18:46 +01:00
if ( box . is_absolutely_positioned ( ) ) {
2022-09-27 15:29:17 +02:00
compute_width_for_absolutely_positioned_element ( box , available_space ) ;
2021-01-06 18:18:46 +01:00
return ;
}
2023-05-04 06:34:35 +03:00
auto remaining_available_space = available_space ;
if ( available_space . width . is_definite ( ) & & creates_block_formatting_context ( box ) ) {
// 9.5 Floats
// The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a
// new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin
// box of any floats in the same block formatting context as the element itself. If necessary, implementations should
// clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is
// sufficient space. They may even make the border box of said element narrower than defined by section 10.3.3.
// CSS2 does not define when a UA may put said element next to the float or by how much said element may
// become narrower.
2023-05-16 09:46:45 +02:00
auto intrusion = intrusion_by_floats_into_box ( box , 0 ) ;
auto remaining_width = available_space . width . to_px ( ) - intrusion . left - intrusion . right ;
2023-05-04 06:34:35 +03:00
remaining_available_space . width = AvailableSize : : make_definite ( remaining_width ) ;
}
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 ( ) ;
2023-05-04 06:34:35 +03:00
compute_width_for_block_level_replaced_element_in_normal_flow ( replaced , remaining_available_space ) ;
2023-05-26 13:47:40 +00:00
if ( box . is_floating ( ) ) {
// 10.3.6 Floating, replaced elements:
// https://www.w3.org/TR/CSS22/visudet.html#float-replaced-width
return ;
}
2020-11-22 13:38:18 +01:00
}
2020-12-06 01:08:14 +01:00
if ( box . is_floating ( ) ) {
2023-05-26 13:47:40 +00:00
// 10.3.5 Floating, non-replaced elements:
// https://www.w3.org/TR/CSS22/visudet.html#float-width
2022-09-27 15:29:17 +02:00
compute_width_for_floating_box ( box , available_space ) ;
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-09-27 15:29:17 +02:00
2023-05-04 06:34:35 +03:00
auto width_of_containing_block = remaining_available_space . width . to_px ( ) ;
auto width_of_containing_block_as_length_for_resolve = remaining_available_space . width . is_definite ( ) ? CSS : : Length : : make_px ( width_of_containing_block ) : CSS : : Length : : make_px ( 0 ) ;
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 ( ) ;
2023-05-06 17:20:14 +02:00
auto const padding_left = computed_values . padding ( ) . left ( ) . to_px ( box , width_of_containing_block ) ;
auto const padding_right = computed_values . padding ( ) . right ( ) . to_px ( box , width_of_containing_block ) ;
2022-10-26 12:51:33 +02:00
auto & box_state = m_state . get_mutable ( box ) ;
box_state . border_left = computed_values . border_left ( ) . width ;
box_state . border_right = computed_values . border_right ( ) . width ;
2023-05-06 17:20:14 +02:00
box_state . padding_left = padding_left ;
box_state . padding_right = padding_right ;
2022-10-26 12:51:33 +02:00
2023-04-06 14:37:54 +02:00
// NOTE: If we are calculating the min-content or max-content width of this box,
// and the width should be treated as auto, then we can simply return here,
// as the preferred width and min/max constraints are irrelevant for intrinsic sizing.
if ( box_state . width_constraint ! = SizeConstraint : : None )
2022-10-26 12:51:33 +02:00
return ;
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 ;
2023-05-06 18:41:03 +02:00
margin_left = computed_values . margin ( ) . left ( ) . resolved ( box , width_of_containing_block_as_length_for_resolve ) ;
margin_right = computed_values . margin ( ) . right ( ) . resolved ( box , width_of_containing_block_as_length_for_resolve ) ;
2022-11-04 17:37:12 +00:00
CSSPixels total_px = computed_values . border_left ( ) . width + computed_values . border_right ( ) . width ;
2023-05-06 17:20:14 +02:00
for ( auto & value : { margin_left , CSS : : Length : : make_px ( padding_left ) , width , CSS : : Length : : make_px ( padding_right ) , margin_right } ) {
2020-11-22 13:38:18 +01:00
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.
2023-05-05 02:58:49 +02:00
if ( ! width . is_auto ( ) & & total_px > width_of_containing_block ) {
2020-11-22 13:38:18 +01:00
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 ;
2023-05-02 11:19:29 +03:00
if ( available_space . width . is_intrinsic_sizing_constraint ( ) )
2022-09-27 15:29:17 +02:00
underflow_px = 0 ;
2020-11-22 13:38:18 +01:00
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
2022-09-27 15:29:17 +02:00
if ( available_space . width . is_definite ( ) ) {
2022-04-03 12:51:32 +02:00
if ( underflow_px > = 0 ) {
2022-11-04 17:37:12 +00:00
width = CSS : : Length : : make_px ( underflow_px ) ;
2022-04-03 12:51:32 +02:00
} else {
width = zero_value ;
2022-11-04 17:37:12 +00:00
margin_right = CSS : : Length : : make_px ( margin_right . to_px ( box ) + underflow_px . value ( ) ) ;
2022-04-03 12:51:32 +02:00
}
2020-11-22 13:38:18 +01:00
}
} else {
if ( ! margin_left . is_auto ( ) & & ! margin_right . is_auto ( ) ) {
2022-11-04 17:37:12 +00:00
margin_right = CSS : : Length : : make_px ( margin_right . to_px ( box ) + underflow_px . value ( ) ) ;
2020-11-22 13:38:18 +01:00
} else if ( ! margin_left . is_auto ( ) & & margin_right . is_auto ( ) ) {
2022-11-04 17:37:12 +00:00
margin_right = CSS : : Length : : make_px ( underflow_px ) ;
2020-11-22 13:38:18 +01:00
} else if ( margin_left . is_auto ( ) & & ! margin_right . is_auto ( ) ) {
2022-11-04 17:37:12 +00:00
margin_left = CSS : : Length : : make_px ( underflow_px ) ;
2020-11-22 13:38:18 +01:00
} else { // margin_left.is_auto() && margin_right.is_auto()
2022-11-04 17:37:12 +00:00
auto half_of_the_underflow = CSS : : Length : : make_px ( underflow_px / 2 ) ;
2020-11-22 13:38:18 +01:00
margin_left = half_of_the_underflow ;
margin_right = half_of_the_underflow ;
}
}
}
return width ;
} ;
2022-09-13 10:47:44 +02:00
auto input_width = [ & ] {
2023-04-29 20:18:01 +02:00
if ( is < ReplacedBox > ( box ) ) {
// NOTE: Replaced elements had their width calculated independently above.
// We use that width as the input here to ensure that margins get resolved.
return CSS : : Length : : make_px ( box_state . content_width ( ) ) ;
}
2023-01-14 14:48:20 +01:00
if ( is < TableWrapper > ( box ) )
2023-05-07 02:59:39 +03:00
return CSS : : Length : : make_px ( compute_table_box_width_inside_table_wrapper ( box , remaining_available_space ) ) ;
2023-05-04 06:34:35 +03:00
if ( should_treat_width_as_auto ( box , remaining_available_space ) )
2022-09-13 10:47:44 +02:00
return CSS : : Length : : make_auto ( ) ;
2023-05-04 06:34:35 +03:00
return calculate_inner_width ( box , remaining_available_space . width , computed_values . width ( ) ) ;
2022-09-13 10:47:44 +02:00
} ( ) ;
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-09-25 15:48:23 +02:00
if ( ! computed_values . max_width ( ) . is_none ( ) ) {
2023-05-04 06:34:35 +03:00
auto max_width = calculate_inner_width ( box , remaining_available_space . width , computed_values . max_width ( ) ) ;
auto used_width_px = used_width . is_auto ( ) ? remaining_available_space . width . to_px ( ) : used_width . to_px ( box ) ;
2023-05-02 17:20:10 +03:00
if ( used_width_px > max_width . to_px ( box ) ) {
2022-09-25 15:48:23 +02:00
used_width = try_compute_width ( max_width ) ;
2020-11-22 13:38:18 +01: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-09-25 15:48:23 +02:00
if ( ! computed_values . min_width ( ) . is_auto ( ) ) {
2023-05-04 06:34:35 +03:00
auto min_width = calculate_inner_width ( box , remaining_available_space . width , computed_values . min_width ( ) ) ;
auto used_width_px = used_width . is_auto ( ) ? remaining_available_space . width . to_px ( ) : used_width . to_px ( box ) ;
2023-05-02 17:20:10 +03:00
if ( used_width_px < min_width . to_px ( box ) ) {
2022-09-25 15:48:23 +02:00
used_width = try_compute_width ( min_width ) ;
2020-11-22 13:38:18 +01:00
}
}
2022-09-27 15:29:17 +02:00
if ( ! is < ReplacedBox > ( box ) & & ! used_width . is_auto ( ) )
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 ) ;
2020-11-22 13:38:18 +01:00
}
2022-09-27 15:29:17 +02:00
void BlockFormattingContext : : compute_width_for_floating_box ( Box const & box , AvailableSpace const & available_space )
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
2020-12-06 01:08:14 +01:00
auto zero_value = CSS : : Length : : make_px ( 0 ) ;
2022-11-04 17:37:12 +00:00
auto width_of_containing_block = available_space . width . to_px ( ) ;
2022-09-27 15:29:17 +02:00
auto width_of_containing_block_as_length_for_resolve = CSS : : Length : : make_px ( width_of_containing_block ) ;
if ( ! available_space . width . is_definite ( ) )
width_of_containing_block_as_length_for_resolve = CSS : : Length : : make_px ( 0 ) ;
2020-12-06 01:08:14 +01:00
2023-05-06 18:41:03 +02:00
auto margin_left = computed_values . margin ( ) . left ( ) . resolved ( box , width_of_containing_block_as_length_for_resolve ) ;
auto margin_right = computed_values . margin ( ) . right ( ) . resolved ( box , width_of_containing_block_as_length_for_resolve ) ;
2023-05-06 17:20:14 +02:00
auto const padding_left = computed_values . padding ( ) . left ( ) . to_px ( box , width_of_containing_block ) ;
auto const padding_right = computed_values . padding ( ) . right ( ) . to_px ( box , width_of_containing_block ) ;
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.
2022-11-04 17:37:12 +00:00
auto available_width = width_of_containing_block
2023-05-06 17:20:14 +02:00
- margin_left . to_px ( box ) - computed_values . border_left ( ) . width - padding_left
- padding_right - 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).
2022-11-04 17:37:12 +00:00
width = CSS : : Length : : make_px ( min ( max ( result . preferred_minimum_width , available_width ) , result . preferred_width ) ) ;
2022-06-05 11:45:46 +02:00
}
return width ;
} ;
2022-09-13 10:47:44 +02:00
auto input_width = [ & ] {
2022-09-27 15:29:17 +02:00
if ( should_treat_width_as_auto ( box , available_space ) )
2022-09-13 10:47:44 +02:00
return CSS : : Length : : make_auto ( ) ;
2022-11-21 22:17:24 +03:00
return calculate_inner_width ( box , available_space . width , computed_values . width ( ) ) ;
2022-09-13 10:47:44 +02:00
} ( ) ;
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-09-25 15:48:23 +02:00
if ( ! computed_values . max_width ( ) . is_none ( ) ) {
2022-11-21 22:17:24 +03:00
auto max_width = calculate_inner_width ( box , available_space . width , computed_values . max_width ( ) ) ;
2022-09-25 15:48:23 +02:00
if ( width . to_px ( box ) > max_width . to_px ( box ) )
width = compute_width ( max_width ) ;
2022-06-05 11:45:46 +02:00
}
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-09-25 15:48:23 +02:00
if ( ! computed_values . min_width ( ) . is_auto ( ) ) {
2022-11-21 22:17:24 +03:00
auto min_width = calculate_inner_width ( box , available_space . width , computed_values . min_width ( ) ) ;
2022-09-25 15:48:23 +02:00
if ( width . to_px ( box ) < min_width . to_px ( box ) )
width = compute_width ( 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 ;
2023-05-06 17:20:14 +02:00
box_state . padding_left = padding_left ;
box_state . padding_right = padding_right ;
2020-12-06 01:08:14 +01:00
}
2022-09-27 15:29:17 +02:00
void BlockFormattingContext : : compute_width_for_block_level_replaced_element_in_normal_flow ( ReplacedBox const & box , AvailableSpace const & available_space )
2020-12-11 22:27:09 +01:00
{
2023-05-30 01:28:28 +00:00
// 10.3.6 Floating, replaced elements
auto & computed_values = box . computed_values ( ) ;
auto zero_value = CSS : : Length : : make_px ( 0 ) ;
auto width_of_containing_block = available_space . width . to_px ( ) ;
auto width_of_containing_block_as_length_for_resolve = CSS : : Length : : make_px ( width_of_containing_block ) ;
if ( ! available_space . width . is_definite ( ) )
width_of_containing_block_as_length_for_resolve = CSS : : Length : : make_px ( 0 ) ;
// 10.3.4 Block-level, replaced elements in normal flow
// The used value of 'width' is determined as for inline replaced elements. Then the rules for
// non-replaced block-level elements are applied to determine the margins.
auto margin_left = computed_values . margin ( ) . left ( ) . resolved ( box , width_of_containing_block_as_length_for_resolve ) ;
auto margin_right = computed_values . margin ( ) . right ( ) . resolved ( box , width_of_containing_block_as_length_for_resolve ) ;
auto const padding_left = computed_values . padding ( ) . left ( ) . to_px ( box , width_of_containing_block ) ;
auto const padding_right = computed_values . padding ( ) . right ( ) . to_px ( box , width_of_containing_block ) ;
// 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 ;
auto & box_state = m_state . get_mutable ( box ) ;
2023-05-31 09:28:03 +02:00
box_state . set_content_width ( compute_width_for_replaced_element ( box , available_space ) ) ;
2023-05-30 01:28:28 +00: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 ;
box_state . padding_right = padding_right ;
2020-12-11 22:27:09 +01:00
}
2023-05-07 02:59:39 +03:00
CSSPixels BlockFormattingContext : : compute_table_box_width_inside_table_wrapper ( Box const & box , AvailableSpace const & available_space )
2023-01-14 14:48:20 +01:00
{
// 17.5.2
// Table wrapper width should be equal to width of table box it contains
2023-05-03 15:26:01 +03:00
auto const & computed_values = box . computed_values ( ) ;
auto width_of_containing_block = available_space . width . to_px ( ) ;
auto width_of_containing_block_as_length_for_resolve = available_space . width . is_definite ( ) ? CSS : : Length : : make_px ( width_of_containing_block ) : CSS : : Length : : make_px ( 0 ) ;
auto zero_value = CSS : : Length : : make_px ( 0 ) ;
2023-05-06 18:41:03 +02:00
auto margin_left = computed_values . margin ( ) . left ( ) . resolved ( box , width_of_containing_block_as_length_for_resolve ) ;
auto margin_right = computed_values . margin ( ) . right ( ) . resolved ( box , width_of_containing_block_as_length_for_resolve ) ;
2023-05-03 15:26:01 +03: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 ;
// table-wrapper can't have borders or paddings but it might have margin taken from table-root.
auto available_width = width_of_containing_block - margin_left . to_px ( box ) - margin_right . to_px ( box ) ;
2023-01-14 14:48:20 +01:00
LayoutState throwaway_state ( & m_state ) ;
2023-05-07 02:53:11 +03:00
throwaway_state . get_mutable ( box ) . set_content_width ( available_width ) ;
2023-01-14 14:48:20 +01:00
auto context = create_independent_formatting_context_if_needed ( throwaway_state , box ) ;
VERIFY ( context ) ;
context - > run ( box , LayoutMode : : IntrinsicSizing , m_state . get ( box ) . available_inner_space_or_constraints_from ( available_space ) ) ;
2023-05-29 14:11:19 +03:00
Optional < Box const & > table_box ;
box . for_each_in_subtree_of_type < Box > ( [ & ] ( Box const & child_box ) {
if ( child_box . display ( ) . is_table_inside ( ) ) {
table_box = child_box ;
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
} ) ;
VERIFY ( table_box . has_value ( ) ) ;
2023-05-03 15:26:01 +03:00
auto table_used_width = throwaway_state . get ( * table_box ) . content_width ( ) ;
return table_used_width > available_width ? available_width : table_used_width ;
2023-01-14 14:48:20 +01:00
}
2022-09-27 15:29:17 +02:00
void BlockFormattingContext : : compute_height ( Box const & box , AvailableSpace const & available_space )
2021-03-08 22:49:34 +01:00
{
2022-02-20 15:51:24 +01:00
auto const & computed_values = box . computed_values ( ) ;
2022-09-27 15:29:17 +02:00
auto containing_block_height = CSS : : Length : : make_px ( available_space . height . to_px ( ) ) ;
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.
2022-11-04 17:37:12 +00:00
CSSPixels height = 0 ;
2021-03-08 22:49:34 +01:00
if ( is < ReplacedBox > ( box ) ) {
2023-05-31 09:28:03 +02:00
height = compute_height_for_replaced_element ( verify_cast < ReplacedBox > ( box ) , available_space ) ;
2021-03-08 22:49:34 +01:00
} else {
2022-09-27 15:29:17 +02:00
if ( should_treat_height_as_auto ( box , available_space ) ) {
2023-03-27 13:48:02 +02:00
height = compute_auto_height_for_block_level_element ( box , m_state . get ( box ) . available_inner_space_or_constraints_from ( available_space ) ) ;
2022-09-09 15:10:11 +02:00
} else {
2022-11-21 22:17:24 +03:00
height = calculate_inner_height ( box , available_space . height , computed_values . 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-09-25 15:48:23 +02:00
if ( ! computed_values . max_height ( ) . is_none ( ) ) {
2022-11-21 22:17:24 +03:00
auto max_height = calculate_inner_height ( box , available_space . height , computed_values . max_height ( ) ) ;
2022-09-25 15:48:23 +02:00
if ( ! max_height . is_auto ( ) )
height = min ( height , max_height . to_px ( box ) ) ;
}
2022-09-27 15:29:17 +02:00
if ( ! computed_values . min_height ( ) . is_auto ( ) ) {
2022-11-21 22:17:24 +03:00
height = max ( height , calculate_inner_height ( box , available_space . height , computed_values . min_height ( ) ) . to_px ( box ) ) ;
2022-09-27 15:29:17 +02:00
}
2021-05-30 21:57:13 +02:00
2023-05-03 19:48:32 +02:00
if ( box . document ( ) . in_quirks_mode ( )
& & box . dom_node ( )
& & box . dom_node ( ) - > is_html_html_element ( )
& & box . computed_values ( ) . height ( ) . is_auto ( ) ) {
// 3.6. The html element fills the viewport quirk
// https://quirks.spec.whatwg.org/#the-html-element-fills-the-viewport-quirk
// FIXME: Handle vertical writing mode.
auto & box_state = m_state . get_mutable ( box ) ;
// 1. Let margins be sum of the used values of the margin-left and margin-right properties of element
// if element has a vertical writing mode, otherwise let margins be the sum of the used values of
// the margin-top and margin-bottom properties of element.
auto margins = box_state . margin_top + box_state . margin_bottom ;
// 2. Let size be the size of the initial containing block in the block flow direction minus margins.
auto size = box_state . content_height ( ) - margins ;
// 3. Return the bigger value of size and the normal border box size the element would have
// according to the CSS specification.
height = max ( size , height ) ;
}
2022-11-04 20:32:50 +00:00
m_state . get_mutable ( box ) . set_content_height ( height ) ;
2021-03-29 12:04:26 -04:00
}
2022-09-27 15:29:17 +02:00
void BlockFormattingContext : : layout_inline_children ( BlockContainer const & block_container , LayoutMode layout_mode , AvailableSpace const & available_space )
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-02-19 20:13:47 +01:00
InlineFormattingContext context ( m_state , block_container , * this ) ;
2022-09-25 19:12:09 +02:00
context . run (
block_container ,
layout_mode ,
2022-09-27 15:29:17 +02:00
available_space ) ;
2022-07-08 22:07:42 +02:00
2022-09-27 15:29:17 +02:00
if ( ! block_container_state . has_definite_width ( ) )
2022-11-04 20:32:50 +00:00
block_container_state . set_content_width ( context . automatic_content_width ( ) ) ;
2022-09-27 15:29:17 +02:00
if ( ! block_container_state . has_definite_height ( ) )
2022-11-04 20:32:50 +00:00
block_container_state . set_content_height ( context . automatic_content_height ( ) ) ;
2020-11-22 13:38:18 +01:00
}
2022-11-04 17:37:12 +00:00
CSSPixels BlockFormattingContext : : compute_auto_height_for_block_level_element ( Box const & box , AvailableSpace const & available_space )
2022-12-28 21:42:46 +03:00
{
if ( creates_block_formatting_context ( box ) ) {
2023-01-23 14:45:52 +01:00
return compute_auto_height_for_block_formatting_context_root ( box ) ;
2022-12-28 21:42:46 +03:00
}
auto const & box_state = m_state . get ( box ) ;
auto display = box . display ( ) ;
if ( display . is_flex_inside ( ) ) {
// https://drafts.csswg.org/css-flexbox-1/#algo-main-container
// NOTE: The automatic block size of a block-level flex container is its max-content size.
2022-11-04 17:37:12 +00:00
return calculate_max_content_height ( box , available_space . width ) ;
2022-12-28 21:42:46 +03:00
}
if ( display . is_grid_inside ( ) ) {
// https://www.w3.org/TR/css-grid-2/#intrinsic-sizes
// In both inline and block formatting contexts, the grid container’ s auto block size is its
// max-content size.
2022-11-04 17:37:12 +00:00
return calculate_max_content_height ( box , available_space . width ) ;
2022-12-28 21:42:46 +03:00
}
2023-01-20 00:59:43 +01:00
if ( display . is_table_inside ( ) ) {
2023-05-08 15:42:29 +03:00
return calculate_max_content_height ( box , available_space . width ) ;
2023-01-20 00:59:43 +01:00
}
2022-12-28 21:42:46 +03:00
// https://www.w3.org/TR/CSS22/visudet.html#normal-block
// 10.6.3 Block-level non-replaced elements in normal flow when 'overflow' computes to 'visible'
// The element's height is the distance from its top content edge to the first applicable of the following:
// 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
if ( box . children_are_inline ( ) & & ! box_state . line_boxes . is_empty ( ) )
2022-11-04 20:32:50 +00:00
return box_state . line_boxes . last ( ) . bottom ( ) . value ( ) ;
2022-12-28 21:42:46 +03:00
// 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin does not collapse with the element's bottom margin
// 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
if ( ! box . children_are_inline ( ) ) {
for ( auto * child_box = box . last_child_of_type < Box > ( ) ; child_box ; child_box = child_box - > previous_sibling_of_type < Box > ( ) ) {
if ( child_box - > is_absolutely_positioned ( ) | | child_box - > is_floating ( ) )
continue ;
// FIXME: This is hack. If the last child is a list-item marker box, we ignore it for purposes of height calculation.
// Perhaps markers should not be considered in-flow(?) Perhaps they should always be the first child of the list-item
// box instead of the last child.
if ( child_box - > is_list_item_marker_box ( ) )
continue ;
auto const & child_box_state = m_state . get ( * child_box ) ;
2023-01-26 13:40:46 +03:00
// Ignore anonymous block containers with no lines. These don't count as in-flow block boxes.
2023-01-29 15:47:37 +03:00
if ( ! child_box - > is_table_wrapper ( ) & & child_box - > is_anonymous ( ) & & child_box - > is_block_container ( ) & & child_box_state . line_boxes . is_empty ( ) )
2022-12-28 21:42:46 +03:00
continue ;
2022-11-04 17:37:12 +00:00
auto margin_bottom = m_margin_state . current_collapsed_margin ( ) ;
2022-12-28 21:42:46 +03:00
if ( box_state . padding_bottom = = 0 & & box_state . border_bottom = = 0 ) {
m_margin_state . box_last_in_flow_child_margin_bottom_collapsed = true ;
margin_bottom = 0 ;
}
2022-11-04 20:32:50 +00:00
return max ( 0.0f , ( child_box_state . offset . y ( ) + child_box_state . content_height ( ) + child_box_state . border_box_bottom ( ) + margin_bottom ) . value ( ) ) ;
2022-12-28 21:42:46 +03:00
}
}
// 4. zero, otherwise
return 0 ;
}
2023-02-10 17:29:38 +03:00
void BlockFormattingContext : : layout_block_level_box ( Box const & box , BlockContainer const & block_container , LayoutMode layout_mode , CSSPixels & bottom_of_lowest_margin_box , AvailableSpace const & available_space )
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
2023-05-31 09:28:03 +02:00
resolve_vertical_box_model_metrics ( box ) ;
2023-05-04 06:27:14 +03:00
2022-07-09 20:14:13 +02:00
if ( box . is_floating ( ) ) {
2023-05-26 18:31:29 +03:00
auto const y = m_y_offset_of_current_block_container . value ( ) ;
2022-12-25 14:01:14 +03:00
auto margin_top = ! m_margin_state . has_block_container_waiting_for_final_y_position ( ) ? m_margin_state . current_collapsed_margin ( ) : 0 ;
2023-02-10 17:29:38 +03:00
layout_floating_box ( box , block_container , layout_mode , available_space , margin_top + y ) ;
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
2023-05-26 18:31:29 +03:00
m_margin_state . add_margin ( box_state . margin_top ) ;
auto introduce_clearance = clear_floating_boxes ( box ) ;
if ( introduce_clearance = = DidIntroduceClearance : : Yes )
m_margin_state . reset ( ) ;
auto const y = m_y_offset_of_current_block_container . value ( ) ;
2022-07-17 18:46:38 +02:00
if ( box_state . has_definite_height ( ) ) {
2022-09-27 15:29:17 +02:00
compute_height ( box , available_space ) ;
2022-07-09 20:14:13 +02:00
}
2021-10-28 19:35:54 +02:00
2023-05-19 13:48:47 +02:00
auto independent_formatting_context = create_independent_formatting_context_if_needed ( m_state , box ) ;
2022-12-25 14:01:14 +03:00
2023-05-22 13:03:00 +03:00
m_margin_state . update_block_waiting_for_final_y_position ( ) ;
CSSPixels margin_top = m_margin_state . current_collapsed_margin ( ) ;
if ( m_margin_state . has_block_container_waiting_for_final_y_position ( ) ) {
// If first child margin top will collapse with margin-top of containing block then margin-top of child is 0
margin_top = 0 ;
}
2023-05-19 13:48:47 +02:00
if ( independent_formatting_context ) {
// Margins of elements that establish new formatting contexts do not collapse with their in-flow children
m_margin_state . reset ( ) ;
2022-12-25 14:01:14 +03:00
}
2022-12-25 17:13:21 +03:00
2023-02-10 17:29:38 +03:00
place_block_level_element_in_normal_flow_vertically ( box , y + margin_top ) ;
2023-05-04 06:27:14 +03:00
compute_width ( box , available_space , layout_mode ) ;
2022-12-25 17:13:21 +03:00
place_block_level_element_in_normal_flow_horizontally ( box , available_space ) ;
2023-05-19 18:14:37 +02:00
if ( box . is_replaced_box ( ) )
compute_height ( box , available_space ) ;
2023-05-19 13:48:47 +02:00
if ( independent_formatting_context ) {
// This box establishes a new formatting context. Pass control to it.
independent_formatting_context - > run ( box , layout_mode , box_state . available_inner_space_or_constraints_from ( available_space ) ) ;
} else {
// This box participates in the current block container's flow.
if ( box . children_are_inline ( ) ) {
layout_inline_children ( verify_cast < BlockContainer > ( box ) , layout_mode , box_state . available_inner_space_or_constraints_from ( available_space ) ) ;
2022-07-09 20:14:13 +02:00
} else {
2023-05-19 13:48:47 +02:00
if ( box_state . border_top > 0 | | box_state . padding_top > 0 ) {
// margin-top of block container can't collapse with it's children if it has non zero border or padding
m_margin_state . reset ( ) ;
} else if ( ! m_margin_state . has_block_container_waiting_for_final_y_position ( ) ) {
// margin-top of block container can be updated during children layout hence it's final y position yet to be determined
m_margin_state . register_block_container_y_position_update_callback ( [ & ] ( CSSPixels margin_top ) {
2023-05-26 18:31:29 +03:00
if ( introduce_clearance = = DidIntroduceClearance : : No ) {
place_block_level_element_in_normal_flow_vertically ( box , margin_top + y ) ;
}
2023-05-19 13:48:47 +02:00
} ) ;
2022-12-25 17:13:21 +03:00
}
2023-05-19 13:48:47 +02:00
layout_block_level_children ( verify_cast < BlockContainer > ( box ) , layout_mode , box_state . available_inner_space_or_constraints_from ( available_space ) ) ;
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-09-27 15:29:17 +02:00
compute_height ( box , available_space ) ;
2022-01-24 01:56:15 +01:00
2023-05-19 13:48:47 +02:00
if ( independent_formatting_context | | ! margins_collapse_through ( box , m_state ) ) {
2022-12-28 21:42:46 +03:00
if ( ! m_margin_state . box_last_in_flow_child_margin_bottom_collapsed ) {
m_margin_state . reset ( ) ;
}
2023-02-10 17:29:38 +03:00
m_y_offset_of_current_block_container = box_state . offset . y ( ) + box_state . content_height ( ) + box_state . border_box_bottom ( ) ;
2022-12-25 17:13:21 +03:00
}
2022-12-28 21:42:46 +03:00
m_margin_state . box_last_in_flow_child_margin_bottom_collapsed = false ;
2022-12-25 17:13:21 +03:00
m_margin_state . add_margin ( box_state . margin_bottom ) ;
2022-12-25 14:01:14 +03:00
m_margin_state . update_block_waiting_for_final_y_position ( ) ;
2022-12-25 17:13:21 +03: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 < 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-09-27 15:29:17 +02:00
void BlockFormattingContext : : layout_block_level_children ( BlockContainer const & block_container , LayoutMode layout_mode , AvailableSpace const & available_space )
2022-07-09 20:14:13 +02:00
{
VERIFY ( ! block_container . children_are_inline ( ) ) ;
2020-11-22 13:38:18 +01:00
2022-11-04 17:37:12 +00:00
CSSPixels bottom_of_lowest_margin_box = 0 ;
2022-02-12 19:54:09 +01:00
2023-02-10 17:29:38 +03:00
TemporaryChange < Optional < CSSPixels > > change { m_y_offset_of_current_block_container , CSSPixels ( 0 ) } ;
2022-07-09 20:14:13 +02:00
block_container . for_each_child_of_type < Box > ( [ & ] ( Box & box ) {
2023-02-10 17:29:38 +03:00
layout_block_level_box ( box , block_container , layout_mode , bottom_of_lowest_margin_box , available_space ) ;
2020-11-22 13:38:18 +01:00
return IterationDecision : : Continue ;
} ) ;
2022-12-28 21:42:46 +03:00
m_margin_state . block_container_y_position_update_callback = { } ;
2022-12-25 17:13:21 +03:00
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-27 15:29:17 +02:00
if ( ! block_container_state . has_definite_width ( ) )
2022-11-04 20:32:50 +00:00
block_container_state . set_content_width ( greatest_child_width ( block_container ) ) ;
2022-09-27 15:29:17 +02:00
if ( ! block_container_state . has_definite_height ( ) )
2022-11-04 20:32:50 +00:00
block_container_state . set_content_height ( bottom_of_lowest_margin_box ) ;
2020-11-22 13:38:18 +01:00
}
}
2023-05-31 09:28:03 +02:00
void BlockFormattingContext : : resolve_vertical_box_model_metrics ( Box const & box )
2022-01-24 01:23:02 +01:00
{
2023-05-31 09:28:03 +02:00
auto & box_state = m_state . get_mutable ( box ) ;
2022-02-20 15:51:24 +01:00
auto const & computed_values = box . computed_values ( ) ;
2023-05-31 09:28:03 +02:00
auto width_of_containing_block = containing_block_width_for ( box ) ;
2022-02-20 15:51:24 +01:00
2023-05-06 16:34:55 +02:00
box_state . margin_top = computed_values . margin ( ) . top ( ) . to_px ( box , width_of_containing_block ) ;
box_state . margin_bottom = computed_values . margin ( ) . bottom ( ) . to_px ( box , width_of_containing_block ) ;
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 ;
2023-05-06 16:34:55 +02:00
box_state . padding_top = computed_values . padding ( ) . top ( ) . to_px ( box , width_of_containing_block ) ;
box_state . padding_bottom = computed_values . padding ( ) . bottom ( ) . to_px ( box , width_of_containing_block ) ;
2022-01-24 01:23:02 +01:00
}
2022-11-04 17:37:12 +00:00
CSSPixels BlockFormattingContext : : BlockMarginState : : current_collapsed_margin ( ) const
2020-11-22 13:38:18 +01:00
{
2022-11-04 17:37:12 +00:00
CSSPixels smallest_margin = 0 ;
CSSPixels largest_margin = 0 ;
2022-12-25 17:13:21 +03:00
size_t negative_margin_count = 0 ;
for ( auto margin : current_collapsible_margins ) {
if ( margin < 0 )
+ + negative_margin_count ;
largest_margin = max ( largest_margin , margin ) ;
smallest_margin = min ( smallest_margin , margin ) ;
}
2020-11-22 13:38:18 +01:00
2022-11-04 17:37:12 +00:00
CSSPixels collapsed_margin = 0 ;
2022-12-25 17:13:21 +03:00
if ( negative_margin_count = = current_collapsible_margins . size ( ) ) {
// When all margins are negative, the size of the collapsed margin is the smallest (most negative) margin.
collapsed_margin = smallest_margin ;
} else if ( negative_margin_count > 0 ) {
// When negative margins are involved, the size of the collapsed margin is the sum of the largest positive margin and the smallest (most negative) negative margin.
collapsed_margin = largest_margin + smallest_margin ;
} else {
// Otherwise, collapse all the adjacent margins by using only the largest one.
collapsed_margin = largest_margin ;
}
return collapsed_margin ;
}
2020-11-22 13:38:18 +01:00
2023-05-26 18:31:29 +03:00
BlockFormattingContext : : DidIntroduceClearance BlockFormattingContext : : clear_floating_boxes ( Box const & child_box )
2022-12-25 17:13:21 +03:00
{
auto const & computed_values = child_box . computed_values ( ) ;
2023-05-26 18:31:29 +03:00
auto result = DidIntroduceClearance : : No ;
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 ( ) ) {
2022-09-22 16:46:48 +02:00
// NOTE: Floating boxes are globally relevant within this BFC, *but* their offset coordinates
// are relative to their containing block.
// This means that we have to first convert to a root-space Y coordinate before clearing,
// and then convert back to a local Y coordinate when assigning the cleared offset to
// the `child_box` layout state.
// First, find the lowest margin box edge on this float side and calculate the Y offset just below it.
2022-11-04 17:37:12 +00:00
CSSPixels clearance_y_in_root = 0 ;
2022-03-18 14:44:36 +01:00
for ( auto const & floating_box : float_side . current_boxes ) {
2023-05-31 10:18:34 +02:00
auto floating_box_rect_in_root = margin_box_rect_in_ancestor_coordinate_space ( floating_box . box , root ( ) ) ;
2023-05-22 00:41:18 +02:00
clearance_y_in_root = max ( clearance_y_in_root , floating_box_rect_in_root . bottom ( ) ) ;
2020-12-06 01:45:51 +01:00
}
2022-09-22 16:46:48 +02:00
// Then, convert the clearance Y to a coordinate relative to the containing block of `child_box`.
2022-11-04 17:37:12 +00:00
CSSPixels clearance_y_in_containing_block = clearance_y_in_root ;
2022-09-22 16:46:48 +02:00
for ( auto * containing_block = child_box . containing_block ( ) ; containing_block & & containing_block ! = & root ( ) ; containing_block = containing_block - > containing_block ( ) )
clearance_y_in_containing_block - = m_state . get ( * containing_block ) . offset . y ( ) ;
2023-05-26 18:31:29 +03:00
if ( clearance_y_in_containing_block > m_y_offset_of_current_block_container . value ( ) ) {
result = DidIntroduceClearance : : Yes ;
2023-02-10 17:31:11 +03:00
m_y_offset_of_current_block_container = clearance_y_in_containing_block ;
2023-05-26 18:31:29 +03:00
}
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
2023-05-26 18:31:29 +03:00
return result ;
}
2023-01-10 08:01:42 +03:00
2023-05-26 18:31:29 +03:00
void BlockFormattingContext : : place_block_level_element_in_normal_flow_vertically ( Box const & child_box , CSSPixels y )
{
auto & box_state = m_state . get_mutable ( child_box ) ;
y + = box_state . border_box_top ( ) ;
2022-11-04 20:32:50 +00:00
box_state . set_content_offset ( CSSPixelPoint { box_state . offset . x ( ) , y . value ( ) } ) ;
2022-01-24 01:56:15 +01:00
}
2022-09-27 15:29:17 +02:00
void BlockFormattingContext : : place_block_level_element_in_normal_flow_horizontally ( Box const & child_box , AvailableSpace const & available_space )
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
2022-11-04 17:37:12 +00:00
CSSPixels x = 0 ;
CSSPixels available_width_within_containing_block = available_space . width . to_px ( ) ;
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 ) ) {
2023-05-31 10:18:34 +02:00
auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space ( child_box , root ( ) ) ;
2022-12-11 03:18:34 +03:00
auto space = space_used_by_floats ( box_in_root_rect . y ( ) ) ;
2022-11-04 17:37:12 +00:00
available_width_within_containing_block - = space . left + space . right ;
x + = space . left ;
2022-03-18 14:44:36 +01:00
}
2022-09-27 15:29:17 +02:00
if ( child_box . 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-11-04 17:37:12 +00:00
box_state . set_content_offset ( { x . value ( ) , box_state . offset . y ( ) } ) ;
2020-11-22 13:38:18 +01:00
}
2023-02-25 11:04:29 +01:00
void BlockFormattingContext : : layout_viewport ( LayoutMode layout_mode , AvailableSpace const & available_space )
2020-11-22 13:38:18 +01:00
{
2023-05-20 17:44:18 +02:00
// NOTE: If we are laying out a standalone SVG document, we give it some special treatment:
// The root <svg> container gets the same size as the viewport,
// and we call directly into the SVG layout code from here.
if ( root ( ) . first_child ( ) & & root ( ) . first_child ( ) - > is_svg_svg_box ( ) ) {
auto const & svg_root = verify_cast < SVGSVGBox > ( * root ( ) . first_child ( ) ) ;
auto svg_formatting_context = create_independent_formatting_context_if_needed ( m_state , svg_root ) ;
svg_formatting_context - > run ( svg_root , layout_mode , available_space ) ;
} else {
if ( root ( ) . children_are_inline ( ) )
layout_inline_children ( root ( ) , layout_mode , available_space ) ;
else
layout_block_level_children ( root ( ) , layout_mode , available_space ) ;
}
2020-11-22 13:38:18 +01:00
}
2022-11-04 17:37:12 +00:00
void BlockFormattingContext : : layout_floating_box ( Box const & box , BlockContainer const & , LayoutMode layout_mode , AvailableSpace const & available_space , CSSPixels y , 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-11-04 17:37:12 +00:00
CSSPixels width_of_containing_block = available_space . width . to_px ( ) ;
2022-02-20 15:51:24 +01:00
2023-05-31 09:28:03 +02:00
resolve_vertical_box_model_metrics ( box ) ;
2023-05-04 06:27:14 +03:00
2022-09-27 15:29:17 +02:00
compute_width ( box , available_space , layout_mode ) ;
2022-10-24 19:46:32 +01:00
auto independent_formatting_context = layout_inside ( box , layout_mode , box_state . available_inner_space_or_constraints_from ( available_space ) ) ;
2022-09-27 15:29:17 +02:00
compute_height ( box , available_space ) ;
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-11-04 20:32:50 +00:00
auto y = line_builder - > y_for_float_to_be_inserted_here ( box ) ;
2022-09-27 15:29:17 +02:00
box_state . set_content_y ( y + box_state . margin_box_top ( ) ) ;
2022-03-22 19:18:05 +01:00
} else {
2023-01-10 08:01:42 +03:00
place_block_level_element_in_normal_flow_vertically ( box , y + box_state . margin_top ) ;
2022-09-27 15:29:17 +02:00
place_block_level_element_in_normal_flow_horizontally ( box , available_space ) ;
2022-03-22 19:18:05 +01:00
}
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-11-04 17:37:12 +00:00
CSSPixels offset_from_edge = 0 ;
2022-03-18 14:44:36 +01:00
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
} ;
2023-05-31 10:18:34 +02:00
auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space ( box , root ( ) ) ;
2022-11-04 17:37:12 +00:00
CSSPixels y_in_root = box_in_root_rect . y ( ) ;
CSSPixels 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-01-23 01:19:28 +01:00
2023-03-11 18:27:11 +01: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.
if ( ! line_builder )
y_in_root + = side_data . y_offset ;
bool did_touch_preceding_float = false ;
bool did_place_next_to_preceding_float = false ;
// Walk all currently tracked floats on the side we're floating towards.
// We're looking for the innermost preceding float that intersects vertically with `box`.
for ( auto & preceding_float : side_data . current_boxes . in_reverse ( ) ) {
2023-05-31 10:18:34 +02:00
auto const preceding_float_rect = margin_box_rect_in_ancestor_coordinate_space ( preceding_float . box , root ( ) ) ;
2023-05-24 16:54:06 +02:00
if ( ! preceding_float_rect . contains_vertically ( y_in_root ) )
2023-03-11 18:27:11 +01:00
continue ;
// We found a preceding float that intersects vertically with the current float.
// Now we need to find out if there's enough inline-axis space to stack them next to each other.
auto const & preceding_float_state = m_state . get ( preceding_float . box ) ;
CSSPixels tentative_offset_from_edge = 0 ;
bool fits_next_to_preceding_float = false ;
if ( side = = FloatSide : : Left ) {
tentative_offset_from_edge = preceding_float . offset_from_edge + preceding_float_state . content_width ( ) + preceding_float_state . margin_box_right ( ) + box_state . margin_box_left ( ) ;
fits_next_to_preceding_float = ( tentative_offset_from_edge + box_state . content_width ( ) + box_state . margin_box_right ( ) ) < = width_of_containing_block ;
} else {
tentative_offset_from_edge = preceding_float . offset_from_edge + preceding_float_state . margin_box_left ( ) + box_state . margin_box_right ( ) + box_state . content_width ( ) ;
fits_next_to_preceding_float = tentative_offset_from_edge > = 0 ;
}
did_touch_preceding_float = true ;
if ( ! fits_next_to_preceding_float )
2023-03-15 10:22:37 +01:00
break ;
2023-03-11 18:27:11 +01:00
offset_from_edge = tentative_offset_from_edge ;
did_place_next_to_preceding_float = true ;
break ;
2022-01-23 01:19:28 +01:00
}
2023-03-15 10:22:37 +01:00
if ( ! did_touch_preceding_float | | ! did_place_next_to_preceding_float ) {
// One of two things happened:
// - This box does not touch another floating box.
// - We ran out of horizontal space on this "float line", and need to break.
// Either way, we float this box all the way to the edge.
2022-03-18 14:44:36 +01:00
float_to_edge ( ) ;
2022-11-04 17:37:12 +00:00
CSSPixels 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.
2022-11-04 17:37:12 +00:00
box_state . set_content_y ( y . value ( ) ) ;
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-10-24 19:46:32 +01:00
if ( independent_formatting_context )
independent_formatting_context - > parent_context_did_dimension_child_root_box ( ) ;
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
2022-11-04 17:37:12 +00:00
CSSPixels image_width = 0 ;
CSSPixels 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
}
2023-03-03 19:32:19 +01:00
CSSPixels default_marker_width = max ( 4 , marker . font ( ) . pixel_size_rounded_up ( ) - 4 ) ;
2022-07-07 01:40:05 +01:00
2022-02-21 01:43:37 +01:00
if ( marker . text ( ) . is_empty ( ) ) {
2022-11-04 17:37:12 +00:00
marker_state . set_content_width ( ( image_width + default_marker_width ) . value ( ) ) ;
2022-02-21 01:43:37 +01:00
} else {
auto text_width = marker . font ( ) . width ( marker . text ( ) ) ;
2022-11-04 17:37:12 +00:00
marker_state . set_content_width ( ( image_width + text_width ) . value ( ) ) ;
2022-02-21 01:43:37 +01:00
}
2023-03-03 19:32:19 +01:00
marker_state . set_content_height ( max ( image_height , marker . font ( ) . pixel_size_rounded_up ( ) + 1 ) . value ( ) ) ;
2022-02-21 01:43:37 +01:00
2023-06-02 23:05:15 +02:00
auto final_marker_width = marker_state . content_width ( ) + default_marker_width ;
if ( marker . list_style_position ( ) = = CSS : : ListStylePosition : : Inside ) {
list_item_state . set_content_offset ( { final_marker_width , list_item_state . offset . y ( ) } ) ;
list_item_state . set_content_width ( list_item_state . content_width ( ) - final_marker_width ) ;
}
marker_state . set_content_offset ( { - final_marker_width , max ( CSSPixels ( 0 ) , ( CSSPixels ( marker . line_height ( ) ) - marker_state . content_height ( ) ) / 2 ) } ) ;
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-11-04 17:37:12 +00:00
BlockFormattingContext : : SpaceUsedByFloats BlockFormattingContext : : space_used_by_floats ( CSSPixels 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.
2023-05-31 10:18:34 +02:00
auto rect = margin_box_rect_in_ancestor_coordinate_space ( floating_box . box , root ( ) ) ;
2023-05-24 16:54:06 +02:00
if ( rect . contains_vertically ( y . value ( ) ) ) {
2023-05-16 09:46:45 +02:00
CSSPixels offset_from_containing_block_chain_margins_between_here_and_root = 0 ;
for ( auto const * containing_block = floating_box . box - > containing_block ( ) ; containing_block & & containing_block ! = & root ( ) ; containing_block = containing_block - > containing_block ( ) ) {
auto const & containing_block_state = m_state . get ( * containing_block ) ;
2023-05-24 18:16:09 +03:00
offset_from_containing_block_chain_margins_between_here_and_root + = containing_block_state . margin_box_left ( ) ;
2023-05-16 09:46:45 +02:00
}
space_used_by_floats . left = offset_from_containing_block_chain_margins_between_here_and_root
+ 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.
2023-05-31 10:18:34 +02:00
auto rect = margin_box_rect_in_ancestor_coordinate_space ( floating_box . box , root ( ) ) ;
2023-05-24 16:54:06 +02:00
if ( rect . contains_vertically ( y . value ( ) ) ) {
2023-05-16 09:46:45 +02:00
CSSPixels offset_from_containing_block_chain_margins_between_here_and_root = 0 ;
for ( auto const * containing_block = floating_box . box - > containing_block ( ) ; containing_block & & containing_block ! = & root ( ) ; containing_block = containing_block - > containing_block ( ) ) {
auto const & containing_block_state = m_state . get ( * containing_block ) ;
2023-05-24 18:16:09 +03:00
offset_from_containing_block_chain_margins_between_here_and_root + = containing_block_state . margin_box_right ( ) ;
2023-05-16 09:46:45 +02:00
}
space_used_by_floats . right = offset_from_containing_block_chain_margins_between_here_and_root
+ floating_box . offset_from_edge
2022-03-18 14:44:36 +01:00
+ 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 ;
}
2023-05-16 09:46:45 +02:00
FormattingContext : : SpaceUsedByFloats BlockFormattingContext : : intrusion_by_floats_into_box ( Box const & box , CSSPixels y_in_box ) const
{
// NOTE: Floats are relative to the BFC root box, not necessarily the containing block of this IFC.
2023-05-31 10:18:34 +02:00
auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space ( box , root ( ) ) ;
2023-05-16 09:46:45 +02:00
CSSPixels y_in_root = box_in_root_rect . y ( ) + y_in_box ;
auto space_used_by_floats_in_root = space_used_by_floats ( y_in_root ) ;
auto left_intrusion = max ( CSSPixels ( 0 ) , space_used_by_floats_in_root . left - box_in_root_rect . x ( ) ) ;
CSSPixels offset_from_containing_block_chain_margins_between_here_and_root = 0 ;
for ( auto const * containing_block = static_cast < Box const * > ( & box ) ; containing_block & & containing_block ! = & root ( ) ; containing_block = containing_block - > containing_block ( ) ) {
auto const & containing_block_state = m_state . get ( * containing_block ) ;
offset_from_containing_block_chain_margins_between_here_and_root = max ( offset_from_containing_block_chain_margins_between_here_and_root , containing_block_state . margin_box_right ( ) ) ;
}
auto right_intrusion = max ( CSSPixels ( 0 ) , space_used_by_floats_in_root . right - offset_from_containing_block_chain_margins_between_here_and_root ) ;
return { left_intrusion , right_intrusion } ;
}
2023-03-19 09:57:31 +01:00
CSSPixels BlockFormattingContext : : greatest_child_width ( Box const & box ) const
2022-03-18 14:44:36 +01:00
{
// Similar to FormattingContext::greatest_child_width()
// but this one takes floats into account!
2022-11-04 17:37:12 +00:00
CSSPixels max_width = m_left_floats . max_width + m_right_floats . max_width ;
2022-03-18 14:44:36 +01:00
if ( box . children_are_inline ( ) ) {
for ( auto const & line_box : m_state . get ( verify_cast < BlockContainer > ( box ) ) . line_boxes ) {
2022-11-04 17:37:12 +00:00
CSSPixels width_here = line_box . width ( ) ;
CSSPixels extra_width_from_left_floats = 0 ;
2022-03-18 14:44:36 +01:00
for ( auto & left_float : m_left_floats . all_boxes ) {
2023-05-03 17:14:15 +02:00
// NOTE: Floats directly affect the automatic size of their containing block, but only indirectly anything above in the tree.
if ( left_float - > box - > containing_block ( ) ! = & box )
continue ;
2022-11-04 17:37:12 +00:00
if ( line_box . baseline ( ) > = left_float - > top_margin_edge . value ( ) | | line_box . baseline ( ) < = left_float - > bottom_margin_edge . value ( ) ) {
2022-03-18 14:44:36 +01:00
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
}
}
2022-11-04 17:37:12 +00:00
CSSPixels extra_width_from_right_floats = 0 ;
2022-03-18 14:44:36 +01:00
for ( auto & right_float : m_right_floats . all_boxes ) {
2023-05-03 17:14:15 +02:00
// NOTE: Floats directly affect the automatic size of their containing block, but only indirectly anything above in the tree.
if ( right_float - > box - > containing_block ( ) ! = & box )
continue ;
2022-11-04 17:37:12 +00:00
if ( line_box . baseline ( ) > = right_float - > top_margin_edge . value ( ) | | line_box . baseline ( ) < = right_float - > bottom_margin_edge . value ( ) ) {
2022-03-18 14:44:36 +01:00
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 ( ) )
2022-11-23 22:15:24 +03:00
max_width = max ( max_width , m_state . get ( child ) . margin_box_width ( ) ) ;
2022-03-18 14:44:36 +01:00
} ) ;
}
return max_width ;
2022-03-17 12:30:30 +01:00
}
2023-05-08 14:48:03 +02:00
void BlockFormattingContext : : determine_width_of_child ( Box const & , AvailableSpace const & )
2022-09-27 15:29:17 +02:00
{
2023-05-08 14:48:03 +02:00
// NOTE: We don't do anything here, since we'll have already determined the width of the child
// before recursing into nested layout within the child.
2022-09-27 15:29:17 +02:00
}
void BlockFormattingContext : : determine_height_of_child ( Box const & box , AvailableSpace const & available_space )
{
compute_height ( box , available_space ) ;
2022-09-13 10:47:44 +02:00
}
2020-11-22 13:38:18 +01:00
}