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

LibWeb: Express intrinsic size layout via size constraints

Previously, we had three layout modes:

- Normal:
    - Everything uses the computed values from CSS.

- MinContent:
    - Containing blocks act as if they have 0 width.
    - All line breaking opportunities are taken.

- MaxContent:
    - Containing blocks act as if they have infinite width.
    - Only forced line breaks are accepted.

The above was based on a set of misunderstandings of CSS sizing.
A major problem with the above was that *all* containing blocks
behaved differently during intrinsic size layout, not just the
relevant one.

With this patch there are only two layout modes:

- Normal:
    - Everything uses the computed values from CSS.

- IntrinsicSizeDetermination:
    - One or more boxes have size constraints applied.

There are two size constraints per layout box, set here:

- FormattingState::NodeState::width_constraint
- FormattingState::NodeState::height_constraint

They are of type SizeConstraint and can be one of None, MinContent,
or MaxContent. The default is None.

When performing an IntrinsicSizeDetermination layout, we now assign
a size constraint to the box we're trying to determine the intrinsic
size of, which is then honored by using two new helpers to query
the dimensions of containing blocks:

- FormattingContext::containing_block_width_for(Box)
- FormattingContext::containing_block_height_for(Box)

If there's a relevant constraint in effect on the Box, the size of
its containing block is adjusted accordingly.

This is essentially an implementation of the "available space"
constraints from CSS-SIZING-3. I'm sure some things will break from
this, and we'll have to deal with that separately.

Spec: https://drafts.csswg.org/css-sizing-3/#available
This commit is contained in:
Andreas Kling 2022-07-09 15:17:47 +02:00
parent 66d08d2417
commit 64959a8504
13 changed files with 213 additions and 174 deletions

View file

@ -64,7 +64,7 @@ void BlockFormattingContext::parent_context_did_dimension_child_root_box()
// Right-side floats: offset_from_edge is from right edge (float_containing_block_width) to the left content edge of floating_box.
for (auto& floating_box : m_right_floats.all_boxes) {
auto float_containing_block_width = m_state.get(*floating_box->box.containing_block()).content_width;
auto float_containing_block_width = containing_block_width_for(floating_box->box);
auto& box_state = m_state.get_mutable(floating_box->box);
box_state.offset.set_x(float_containing_block_width - floating_box->offset_from_edge);
}
@ -96,19 +96,7 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
}
auto const& computed_values = box.computed_values();
float width_of_containing_block;
switch (layout_mode) {
case LayoutMode::Normal:
width_of_containing_block = m_state.get(*box.containing_block()).content_width;
break;
case LayoutMode::MinContent:
width_of_containing_block = 0;
break;
case LayoutMode::MaxContent:
width_of_containing_block = INFINITY;
break;
}
float width_of_containing_block = containing_block_width_for(box);
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto zero_value = CSS::Length::make_px(0);
@ -244,26 +232,12 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
box_state.padding_right = padding_right.to_px(box);
}
void BlockFormattingContext::compute_width_for_floating_box(Box const& box, LayoutMode layout_mode)
void BlockFormattingContext::compute_width_for_floating_box(Box const& box, LayoutMode)
{
// 10.3.5 Floating, non-replaced elements
auto& computed_values = box.computed_values();
auto& containing_block = *box.containing_block();
float width_of_containing_block = 0;
switch (layout_mode) {
case LayoutMode::Normal:
width_of_containing_block = m_state.get(containing_block).content_width;
break;
case LayoutMode::MinContent:
width_of_containing_block = 0;
break;
case LayoutMode::MaxContent:
width_of_containing_block = INFINITY;
break;
}
float width_of_containing_block = containing_block_width_for(box);
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto zero_value = CSS::Length::make_px(0);
@ -338,8 +312,7 @@ float BlockFormattingContext::compute_theoretical_height(FormattingState const&
{
auto const& computed_values = box.computed_values();
auto const& containing_block = *box.containing_block();
auto const& containing_block_state = state.get(containing_block);
auto containing_block_height = CSS::Length::make_px(containing_block_state.content_height);
auto containing_block_height = CSS::Length::make_px(containing_block_height_for(box, state));
auto is_absolute = [](Optional<CSS::LengthPercentage> const& length_percentage) {
return length_percentage.has_value() && length_percentage->is_length() && length_percentage->length().is_absolute();
@ -372,8 +345,7 @@ float BlockFormattingContext::compute_theoretical_height(FormattingState const&
void BlockFormattingContext::compute_height(Box const& box, FormattingState& state)
{
auto const& computed_values = box.computed_values();
auto const& containing_block = *box.containing_block();
auto width_of_containing_block_as_length = CSS::Length::make_px(state.get(containing_block).content_width);
auto width_of_containing_block_as_length = CSS::Length::make_px(containing_block_width_for(box, state));
// First, resolve the top/bottom parts of the surrounding box model.
@ -394,23 +366,29 @@ void BlockFormattingContext::layout_inline_children(BlockContainer const& block_
{
VERIFY(block_container.children_are_inline());
auto& block_container_state = m_state.get_mutable(block_container);
if (layout_mode == LayoutMode::IntrinsicSizeDetermination) {
block_container_state.content_width = containing_block_width_for(block_container);
block_container_state.content_height = containing_block_height_for(block_container);
}
InlineFormattingContext context(m_state, block_container, *this);
context.run(block_container, layout_mode);
float max_line_width = 0;
float content_height = 0;
auto& block_container_state = m_state.get_mutable(block_container);
for (auto& line_box : block_container_state.line_boxes) {
max_line_width = max(max_line_width, line_box.width());
content_height += line_box.height();
}
if (layout_mode != LayoutMode::Normal) {
if (layout_mode == LayoutMode::IntrinsicSizeDetermination) {
block_container_state.content_width = max_line_width;
}
// FIXME: This is weird. Figure out a way to make callers responsible for setting the content height.
block_container_state.content_height = content_height;
}
@ -420,6 +398,16 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer const& b
float content_height = 0;
float original_width = 0;
float original_height = 0;
if (layout_mode == LayoutMode::IntrinsicSizeDetermination) {
auto& block_container_state = m_state.get_mutable(block_container);
original_width = block_container_state.content_width;
original_height = block_container_state.content_height;
block_container_state.content_width = containing_block_width_for(block_container);
block_container_state.content_height = containing_block_height_for(block_container);
}
block_container.for_each_child_of_type<Box>([&](Box& child_box) {
auto& box_state = m_state.get_mutable(child_box);
@ -478,12 +466,16 @@ void BlockFormattingContext::layout_block_level_children(BlockContainer const& b
return IterationDecision::Continue;
});
if (layout_mode != LayoutMode::Normal) {
auto& width = block_container.computed_values().width();
if (width.is_auto()) {
auto& block_container_state = m_state.get_mutable(block_container);
if (layout_mode == LayoutMode::IntrinsicSizeDetermination) {
auto& block_container_state = m_state.get_mutable(block_container);
if (block_container_state.width_constraint != SizeConstraint::None)
block_container_state.content_width = greatest_child_width(block_container);
}
else
block_container_state.content_width = original_width;
if (block_container_state.height_constraint != SizeConstraint::None)
block_container_state.content_height = content_height;
else
block_container_state.content_height = original_height;
}
}
@ -491,7 +483,7 @@ void BlockFormattingContext::compute_vertical_box_model_metrics(Box const& box,
{
auto& box_state = m_state.get_mutable(box);
auto const& computed_values = box.computed_values();
auto width_of_containing_block = CSS::Length::make_px(m_state.get(containing_block).content_width);
auto width_of_containing_block = CSS::Length::make_px(containing_block_width_for(box));
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);
@ -584,10 +576,9 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically
void BlockFormattingContext::place_block_level_element_in_normal_flow_horizontally(Box const& child_box, BlockContainer const& containing_block)
{
auto& box_state = m_state.get_mutable(child_box);
auto const& containing_block_state = m_state.get(containing_block);
float x = 0;
float available_width_within_containing_block = containing_block_state.content_width;
float available_width_within_containing_block = containing_block_width_for(child_box);
if ((!m_left_floats.current_boxes.is_empty() || !m_right_floats.current_boxes.is_empty())
&& creates_block_formatting_context(child_box)) {
@ -652,19 +643,7 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer
VERIFY(box.is_floating());
auto& box_state = m_state.get_mutable(box);
float width_of_containing_block = 0;
switch (layout_mode) {
case LayoutMode::Normal:
width_of_containing_block = m_state.get(containing_block).content_width;
break;
case LayoutMode::MinContent:
width_of_containing_block = 0;
break;
case LayoutMode::MaxContent:
width_of_containing_block = INFINITY;
break;
}
float width_of_containing_block = containing_block_width_for(box);
compute_width(box, layout_mode);
(void)layout_inside(box, layout_mode);
@ -674,7 +653,7 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer
// If we have a LineBuilder, we're in the middle of inline layout, otherwise this is block layout.
if (line_builder) {
float y_offset = box_state.margin_box_top();
line_builder->break_if_needed(layout_mode, box_state.margin_box_width());
line_builder->break_if_needed(box_state.margin_box_width());
box_state.offset.set_y(line_builder->current_y() + y_offset);
line_builder->adjust_last_line_after_inserting_floating_box({}, box.computed_values().float_(), box_state.margin_box_width());
} else {