mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 05:27:46 +00:00
LibWeb: Reorganize layout algorithms around available space
This is a big and messy change, and here's the gist: - AvaliableSpace is now 2x AvailableSize (width and height) - Layout algorithms are redesigned around the idea of available space - When doing layout across nested formatting contexts, the parent context tells the child context how much space is available for the child's root box in both axes. - "Available space" replaces "containing block width" in most places. - The width and height in a box's UsedValues are considered to be definite after they're assigned to. Marking something as having definite size is no longer a separate step, This probably introduces various regressions, but the big win here is that our layout system now works with available space, just like the specs are written. Fixing issues will be much easier going forward, since you don't need to do nearly as much conversion from "spec logic" to "LibWeb logic" as you previously did.
This commit is contained in:
parent
b55c4ccdf7
commit
9c44634ca5
19 changed files with 651 additions and 652 deletions
|
@ -33,24 +33,18 @@ void FormattingContext::run_intrinsic_sizing(Box const& box)
|
|||
{
|
||||
auto& box_state = m_state.get_mutable(box);
|
||||
|
||||
if (box_state.has_definite_width())
|
||||
box_state.set_content_width(box.computed_values().width().resolved(box, CSS::Length::make_px(containing_block_width_for(box))).to_px(box));
|
||||
|
||||
if (box_state.has_definite_height())
|
||||
box_state.set_content_height(box.computed_values().height().resolved(box, CSS::Length::make_px(containing_block_height_for(box))).to_px(box));
|
||||
|
||||
auto to_available_space = [&](SizeConstraint constraint) {
|
||||
auto to_available_size = [&](SizeConstraint constraint) {
|
||||
if (constraint == SizeConstraint::MinContent)
|
||||
return AvailableSpace::make_min_content();
|
||||
return AvailableSize::make_min_content();
|
||||
if (constraint == SizeConstraint::MaxContent)
|
||||
return AvailableSpace::make_max_content();
|
||||
return AvailableSpace::make_indefinite();
|
||||
return AvailableSize::make_max_content();
|
||||
return AvailableSize::make_indefinite();
|
||||
};
|
||||
|
||||
auto available_width = to_available_space(box_state.width_constraint);
|
||||
auto available_height = to_available_space(box_state.height_constraint);
|
||||
auto available_width = to_available_size(box_state.width_constraint);
|
||||
auto available_height = to_available_size(box_state.height_constraint);
|
||||
|
||||
run(box, LayoutMode::IntrinsicSizing, available_width, available_height);
|
||||
run(box, LayoutMode::IntrinsicSizing, AvailableSpace(available_width, available_height));
|
||||
}
|
||||
|
||||
bool FormattingContext::creates_block_formatting_context(Box const& box)
|
||||
|
@ -117,7 +111,7 @@ OwnPtr<FormattingContext> FormattingContext::create_independent_formatting_conte
|
|||
{
|
||||
}
|
||||
virtual float automatic_content_height() const override { return 0; };
|
||||
virtual void run(Box const&, LayoutMode, AvailableSpace const&, AvailableSpace const&) override { }
|
||||
virtual void run(Box const&, LayoutMode, AvailableSpace const&) override { }
|
||||
};
|
||||
return make<ReplacedFormattingContext>(state, child_box);
|
||||
}
|
||||
|
@ -159,7 +153,7 @@ OwnPtr<FormattingContext> FormattingContext::create_independent_formatting_conte
|
|||
{
|
||||
}
|
||||
virtual float automatic_content_height() const override { return 0; };
|
||||
virtual void run(Box const&, LayoutMode, AvailableSpace const&, AvailableSpace const&) override { }
|
||||
virtual void run(Box const&, LayoutMode, AvailableSpace const&) override { }
|
||||
};
|
||||
return make<DummyFormattingContext>(state, child_box);
|
||||
}
|
||||
|
@ -168,7 +162,7 @@ OwnPtr<FormattingContext> FormattingContext::create_independent_formatting_conte
|
|||
return {};
|
||||
}
|
||||
|
||||
OwnPtr<FormattingContext> FormattingContext::layout_inside(Box const& child_box, LayoutMode layout_mode)
|
||||
OwnPtr<FormattingContext> FormattingContext::layout_inside(Box const& child_box, LayoutMode layout_mode, AvailableSpace const& available_space)
|
||||
{
|
||||
{
|
||||
// OPTIMIZATION: If we're doing intrinsic sizing and `child_box` has definite size in both axes,
|
||||
|
@ -189,9 +183,9 @@ OwnPtr<FormattingContext> FormattingContext::layout_inside(Box const& child_box,
|
|||
|
||||
auto independent_formatting_context = create_independent_formatting_context_if_needed(m_state, child_box);
|
||||
if (independent_formatting_context)
|
||||
independent_formatting_context->run(child_box, layout_mode, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
|
||||
independent_formatting_context->run(child_box, layout_mode, available_space);
|
||||
else
|
||||
run(child_box, layout_mode, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
|
||||
run(child_box, layout_mode, available_space);
|
||||
|
||||
return independent_formatting_context;
|
||||
}
|
||||
|
@ -262,16 +256,19 @@ static Gfx::FloatSize solve_replaced_size_constraint(LayoutState const& state, f
|
|||
return { w, h };
|
||||
}
|
||||
|
||||
float FormattingContext::compute_auto_height_for_block_level_element(LayoutState const& state, Box const& box)
|
||||
float FormattingContext::compute_auto_height_for_block_level_element(Box const& box) const
|
||||
{
|
||||
if (creates_block_formatting_context(box))
|
||||
return compute_auto_height_for_block_formatting_context_root(state, verify_cast<BlockContainer>(box));
|
||||
return compute_auto_height_for_block_formatting_context_root(verify_cast<BlockContainer>(box));
|
||||
|
||||
auto const& box_state = state.get(box);
|
||||
auto const& box_state = m_state.get(box);
|
||||
|
||||
auto display = box.computed_values().display();
|
||||
if (display.is_flex_inside())
|
||||
return box_state.content_height();
|
||||
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.
|
||||
return calculate_max_content_height(box);
|
||||
}
|
||||
|
||||
// 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'
|
||||
|
@ -295,7 +292,7 @@ float FormattingContext::compute_auto_height_for_block_level_element(LayoutState
|
|||
if (child_box->is_list_item_marker_box())
|
||||
continue;
|
||||
|
||||
auto const& child_box_state = state.get(*child_box);
|
||||
auto const& child_box_state = m_state.get(*child_box);
|
||||
|
||||
// Ignore anonymous block containers with no lines. These don't count as in-flow block boxes.
|
||||
if (child_box->is_anonymous() && child_box->is_block_container() && child_box_state.line_boxes.is_empty())
|
||||
|
@ -311,7 +308,7 @@ float FormattingContext::compute_auto_height_for_block_level_element(LayoutState
|
|||
}
|
||||
|
||||
// https://www.w3.org/TR/CSS22/visudet.html#root-height
|
||||
float FormattingContext::compute_auto_height_for_block_formatting_context_root(LayoutState const& state, BlockContainer const& root)
|
||||
float FormattingContext::compute_auto_height_for_block_formatting_context_root(BlockContainer const& root) const
|
||||
{
|
||||
// 10.6.7 'Auto' heights for block formatting context roots
|
||||
Optional<float> top;
|
||||
|
@ -320,7 +317,7 @@ float FormattingContext::compute_auto_height_for_block_formatting_context_root(L
|
|||
if (root.children_are_inline()) {
|
||||
// If it only has inline-level children, the height is the distance between
|
||||
// the top content edge and the bottom of the bottommost line box.
|
||||
auto const& line_boxes = state.get(root).line_boxes;
|
||||
auto const& line_boxes = m_state.get(root).line_boxes;
|
||||
top = 0;
|
||||
if (!line_boxes.is_empty())
|
||||
bottom = line_boxes.last().bottom();
|
||||
|
@ -339,7 +336,7 @@ float FormattingContext::compute_auto_height_for_block_formatting_context_root(L
|
|||
if ((root.computed_values().overflow_y() == CSS::Overflow::Visible) && child_box.is_floating())
|
||||
return IterationDecision::Continue;
|
||||
|
||||
auto const& child_box_state = state.get(child_box);
|
||||
auto const& child_box_state = m_state.get(child_box);
|
||||
|
||||
float child_box_top = child_box_state.offset.y() - child_box_state.margin_box_top();
|
||||
float child_box_bottom = child_box_state.offset.y() + child_box_state.content_height() + child_box_state.margin_box_bottom();
|
||||
|
@ -357,10 +354,10 @@ float FormattingContext::compute_auto_height_for_block_formatting_context_root(L
|
|||
// In addition, if the element has any floating descendants
|
||||
// whose bottom margin edge is below the element's bottom content edge,
|
||||
// then the height is increased to include those edges.
|
||||
for (auto* floating_box : state.get(root).floating_descendants()) {
|
||||
for (auto* floating_box : m_state.get(root).floating_descendants()) {
|
||||
// NOTE: Floating box coordinates are relative to their own containing block,
|
||||
// which may or may not be the BFC root.
|
||||
auto margin_box = margin_box_rect_in_ancestor_coordinate_space(*floating_box, root, state);
|
||||
auto margin_box = margin_box_rect_in_ancestor_coordinate_space(*floating_box, root, m_state);
|
||||
float floating_box_bottom_margin_edge = margin_box.bottom() + 1;
|
||||
if (!bottom.has_value() || floating_box_bottom_margin_edge > bottom.value())
|
||||
bottom = floating_box_bottom_margin_edge;
|
||||
|
@ -370,7 +367,7 @@ float FormattingContext::compute_auto_height_for_block_formatting_context_root(L
|
|||
}
|
||||
|
||||
// 10.3.2 Inline, replaced elements, https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-width
|
||||
float FormattingContext::tentative_width_for_replaced_element(LayoutState const& state, ReplacedBox const& box, CSS::Size const& computed_width)
|
||||
float FormattingContext::tentative_width_for_replaced_element(LayoutState const& state, ReplacedBox const& box, CSS::Size const& computed_width, AvailableSpace const& available_space)
|
||||
{
|
||||
// Treat percentages of indefinite containing block widths as 0 (the initial width).
|
||||
if (computed_width.is_percentage() && !state.get(*box.containing_block()).has_definite_width())
|
||||
|
@ -379,7 +376,7 @@ float FormattingContext::tentative_width_for_replaced_element(LayoutState const&
|
|||
auto height_of_containing_block = CSS::Length::make_px(containing_block_height_for(box, state));
|
||||
auto const& computed_height = box.computed_values().height();
|
||||
|
||||
float used_width = computed_width.resolved(box, CSS::Length::make_px(containing_block_width_for(box, state))).to_px(box);
|
||||
float used_width = computed_width.resolved(box, CSS::Length::make_px(available_space.width.to_px())).to_px(box);
|
||||
|
||||
// If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width,
|
||||
// then that intrinsic width is the used value of 'width'.
|
||||
|
@ -394,7 +391,7 @@ float FormattingContext::tentative_width_for_replaced_element(LayoutState const&
|
|||
// (used height) * (intrinsic ratio)
|
||||
if ((computed_height.is_auto() && computed_width.is_auto() && !box.has_intrinsic_width() && box.has_intrinsic_height() && box.has_intrinsic_aspect_ratio())
|
||||
|| (computed_width.is_auto() && !computed_height.is_auto() && box.has_intrinsic_aspect_ratio())) {
|
||||
return compute_height_for_replaced_element(state, box) * box.intrinsic_aspect_ratio().value();
|
||||
return compute_height_for_replaced_element(state, box, available_space) * box.intrinsic_aspect_ratio().value();
|
||||
}
|
||||
|
||||
// If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width,
|
||||
|
@ -414,29 +411,29 @@ float FormattingContext::tentative_width_for_replaced_element(LayoutState const&
|
|||
return used_width;
|
||||
}
|
||||
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_element(Box const& box)
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
if (is<ReplacedBox>(box))
|
||||
compute_width_for_absolutely_positioned_replaced_element(verify_cast<ReplacedBox>(box));
|
||||
compute_width_for_absolutely_positioned_replaced_element(verify_cast<ReplacedBox>(box), available_space);
|
||||
else
|
||||
compute_width_for_absolutely_positioned_non_replaced_element(box);
|
||||
compute_width_for_absolutely_positioned_non_replaced_element(box, available_space);
|
||||
}
|
||||
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_element(Box const& box)
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
if (is<ReplacedBox>(box))
|
||||
compute_height_for_absolutely_positioned_replaced_element(verify_cast<ReplacedBox>(box));
|
||||
compute_height_for_absolutely_positioned_replaced_element(static_cast<ReplacedBox const&>(box), available_space);
|
||||
else
|
||||
compute_height_for_absolutely_positioned_non_replaced_element(box);
|
||||
compute_height_for_absolutely_positioned_non_replaced_element(box, available_space);
|
||||
}
|
||||
|
||||
float FormattingContext::compute_width_for_replaced_element(LayoutState const& state, ReplacedBox const& box)
|
||||
float FormattingContext::compute_width_for_replaced_element(LayoutState const& state, ReplacedBox const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
// 10.3.4 Block-level, replaced elements in normal flow...
|
||||
// 10.3.2 Inline, replaced elements
|
||||
|
||||
auto zero_value = CSS::Length::make_px(0);
|
||||
auto width_of_containing_block_as_length = CSS::Length::make_px(containing_block_width_for(box, state));
|
||||
auto width_of_containing_block_as_length = CSS::Length::make_px(available_space.width.to_px());
|
||||
|
||||
auto margin_left = box.computed_values().margin().left().resolved(box, width_of_containing_block_as_length).resolved(box);
|
||||
auto margin_right = box.computed_values().margin().right().resolved(box, width_of_containing_block_as_length).resolved(box);
|
||||
|
@ -450,14 +447,14 @@ float FormattingContext::compute_width_for_replaced_element(LayoutState const& s
|
|||
auto computed_width = box.computed_values().width();
|
||||
|
||||
// 1. The tentative used width is calculated (without 'min-width' and 'max-width')
|
||||
auto used_width = tentative_width_for_replaced_element(state, box, computed_width);
|
||||
auto used_width = tentative_width_for_replaced_element(state, box, computed_width, available_space);
|
||||
|
||||
// 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'.
|
||||
auto computed_max_width = box.computed_values().max_width();
|
||||
if (!computed_max_width.is_none()) {
|
||||
if (used_width > computed_max_width.resolved(box, width_of_containing_block_as_length).to_px(box)) {
|
||||
used_width = tentative_width_for_replaced_element(state, box, computed_max_width);
|
||||
used_width = tentative_width_for_replaced_element(state, box, computed_max_width, available_space);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,7 +463,7 @@ float FormattingContext::compute_width_for_replaced_element(LayoutState const& s
|
|||
auto computed_min_width = box.computed_values().min_width();
|
||||
if (!computed_min_width.is_auto()) {
|
||||
if (used_width < computed_min_width.resolved(box, width_of_containing_block_as_length).to_px(box)) {
|
||||
used_width = tentative_width_for_replaced_element(state, box, computed_min_width);
|
||||
used_width = tentative_width_for_replaced_element(state, box, computed_min_width, available_space);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -475,7 +472,7 @@ float FormattingContext::compute_width_for_replaced_element(LayoutState const& s
|
|||
|
||||
// 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' replaced elements in normal flow and floating replaced elements
|
||||
// https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-height
|
||||
float FormattingContext::tentative_height_for_replaced_element(LayoutState const& state, ReplacedBox const& box, CSS::Size const& computed_height)
|
||||
float FormattingContext::tentative_height_for_replaced_element(LayoutState const& state, ReplacedBox const& box, CSS::Size const& computed_height, AvailableSpace const& available_space)
|
||||
{
|
||||
// Treat percentages of indefinite containing block heights as 0 (the initial height).
|
||||
if (computed_height.is_percentage() && !state.get(*box.containing_block()).has_definite_height())
|
||||
|
@ -492,7 +489,7 @@ float FormattingContext::tentative_height_for_replaced_element(LayoutState const
|
|||
//
|
||||
// (used width) / (intrinsic ratio)
|
||||
if (computed_height.is_auto() && box.has_intrinsic_aspect_ratio())
|
||||
return compute_width_for_replaced_element(state, box) / box.intrinsic_aspect_ratio().value();
|
||||
return compute_width_for_replaced_element(state, box, available_space) / box.intrinsic_aspect_ratio().value();
|
||||
|
||||
// Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic height, then that intrinsic height is the used value of 'height'.
|
||||
if (computed_height.is_auto() && box.has_intrinsic_height())
|
||||
|
@ -504,23 +501,23 @@ float FormattingContext::tentative_height_for_replaced_element(LayoutState const
|
|||
if (computed_height.is_auto())
|
||||
return 150;
|
||||
|
||||
return computed_height.resolved(box, CSS::Length::make_px(containing_block_height_for(box, state))).to_px(box);
|
||||
return computed_height.resolved(box, CSS::Length::make_px(available_space.height.to_px())).to_px(box);
|
||||
}
|
||||
|
||||
float FormattingContext::compute_height_for_replaced_element(LayoutState const& state, ReplacedBox const& box)
|
||||
float FormattingContext::compute_height_for_replaced_element(LayoutState const& state, ReplacedBox const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
// 10.6.2 Inline replaced elements, block-level replaced elements in normal flow,
|
||||
// 'inline-block' replaced elements in normal flow and floating replaced elements
|
||||
|
||||
auto width_of_containing_block_as_length = CSS::Length::make_px(containing_block_width_for(box, state));
|
||||
auto height_of_containing_block_as_length = CSS::Length::make_px(containing_block_height_for(box, state));
|
||||
auto width_of_containing_block_as_length = CSS::Length::make_px(available_space.width.to_px());
|
||||
auto height_of_containing_block_as_length = CSS::Length::make_px(available_space.height.to_px());
|
||||
auto computed_width = box.computed_values().width();
|
||||
auto computed_height = box.computed_values().height();
|
||||
|
||||
float used_height = tentative_height_for_replaced_element(state, box, computed_height);
|
||||
float used_height = tentative_height_for_replaced_element(state, box, computed_height, available_space);
|
||||
|
||||
if (computed_width.is_auto() && computed_height.is_auto() && box.has_intrinsic_aspect_ratio()) {
|
||||
float w = tentative_width_for_replaced_element(state, box, computed_width);
|
||||
float w = tentative_width_for_replaced_element(state, box, computed_width, available_space);
|
||||
float h = used_height;
|
||||
used_height = solve_replaced_size_constraint(state, w, h, box).height();
|
||||
}
|
||||
|
@ -528,9 +525,9 @@ float FormattingContext::compute_height_for_replaced_element(LayoutState const&
|
|||
return used_height;
|
||||
}
|
||||
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_element(Box const& box)
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_element(Box const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
auto width_of_containing_block = containing_block_width_for(box);
|
||||
auto width_of_containing_block = available_space.width.to_px();
|
||||
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
|
||||
auto& computed_values = box.computed_values();
|
||||
auto zero_value = CSS::Length::make_px(0);
|
||||
|
@ -670,17 +667,17 @@ void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_ele
|
|||
box_state.padding_right = padding_right;
|
||||
}
|
||||
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(ReplacedBox const& box)
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(ReplacedBox const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
// 10.3.8 Absolutely positioned, replaced elements
|
||||
// The used value of 'width' is determined as for inline replaced elements.
|
||||
// FIXME: This const_cast is gross.
|
||||
const_cast<ReplacedBox&>(box).prepare_for_replaced_layout();
|
||||
m_state.get_mutable(box).set_content_width(compute_width_for_replaced_element(m_state, box));
|
||||
m_state.get_mutable(box).set_content_width(compute_width_for_replaced_element(m_state, box, available_space));
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/CSS22/visudet.html#abs-non-replaced-height
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box const& box)
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
// 10.6.4 Absolutely positioned, non-replaced elements
|
||||
|
||||
|
@ -688,7 +685,7 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
|
|||
auto& computed_values = box.computed_values();
|
||||
|
||||
auto width_of_containing_block = containing_block_width_for(box);
|
||||
auto height_of_containing_block = containing_block_height_for(box);
|
||||
auto height_of_containing_block = available_space.height.to_px();
|
||||
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
|
||||
auto height_of_containing_block_as_length = CSS::Length::make_px(height_of_containing_block);
|
||||
|
||||
|
@ -714,11 +711,11 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
|
|||
box_state.padding_bottom = computed_values.padding().bottom().resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
|
||||
if (computed_height.is_auto() && computed_top.is_auto() && computed_bottom.is_auto()) {
|
||||
tentative_height = CSS::Length(compute_auto_height_for_block_level_element(m_state, box), CSS::Length::Type::Px);
|
||||
tentative_height = CSS::Length(compute_auto_height_for_block_level_element(box), CSS::Length::Type::Px);
|
||||
}
|
||||
|
||||
else if (computed_height.is_auto() && !computed_top.is_auto() && computed_bottom.is_auto()) {
|
||||
tentative_height = CSS::Length(compute_auto_height_for_block_level_element(m_state, box), CSS::Length::Type::Px);
|
||||
tentative_height = CSS::Length(compute_auto_height_for_block_level_element(box), CSS::Length::Type::Px);
|
||||
box_state.inset_bottom = height_of_containing_block - tentative_height.to_px(box) - used_top - box_state.margin_top - box_state.padding_top - box_state.border_top - box_state.margin_bottom - box_state.padding_bottom - box_state.border_bottom;
|
||||
}
|
||||
|
||||
|
@ -735,29 +732,22 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
|
|||
box_state.set_content_height(used_height);
|
||||
}
|
||||
|
||||
void FormattingContext::layout_absolutely_positioned_element(Box const& box)
|
||||
void FormattingContext::layout_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
// https://drafts.csswg.org/css-sizing-3/#definite
|
||||
// Additionally, the size of the containing block of an absolutely positioned element is always definite with respect to that element.
|
||||
auto& containing_block_state = m_state.get_mutable(*box.containing_block());
|
||||
auto containing_block_had_definite_width = containing_block_state.has_definite_width();
|
||||
containing_block_state.set_has_definite_width(true);
|
||||
auto containing_block_definite_width_guard = ScopeGuard([&] {
|
||||
containing_block_state.set_has_definite_width(containing_block_had_definite_width);
|
||||
});
|
||||
auto& box_state = m_state.get_mutable(box);
|
||||
|
||||
auto width_of_containing_block = containing_block_width_for(box);
|
||||
auto height_of_containing_block = containing_block_height_for(box);
|
||||
auto width_of_containing_block = available_space.width.to_px();
|
||||
auto height_of_containing_block = available_space.height.to_px();
|
||||
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
|
||||
auto height_of_containing_block_as_length = CSS::Length::make_px(height_of_containing_block);
|
||||
|
||||
auto specified_width = box.computed_values().width().resolved(box, width_of_containing_block_as_length).resolved(box);
|
||||
|
||||
compute_width_for_absolutely_positioned_element(box);
|
||||
auto independent_formatting_context = layout_inside(box, LayoutMode::Normal);
|
||||
compute_height_for_absolutely_positioned_element(box);
|
||||
compute_width_for_absolutely_positioned_element(box, available_space);
|
||||
auto independent_formatting_context = layout_inside(box, LayoutMode::Normal, box_state.available_inner_space_or_constraints_from(available_space));
|
||||
compute_height_for_absolutely_positioned_element(box, available_space);
|
||||
|
||||
auto& box_state = m_state.get_mutable(box);
|
||||
box_state.margin_left = box.computed_values().margin().left().resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
box_state.margin_top = box.computed_values().margin().top().resolved(box, height_of_containing_block_as_length).to_px(box);
|
||||
box_state.margin_right = box.computed_values().margin().right().resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
|
@ -773,7 +763,7 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box)
|
|||
box_state.inset_right = box.computed_values().inset().right().resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
box_state.inset_bottom = box.computed_values().inset().bottom().resolved(box, height_of_containing_block_as_length).to_px(box);
|
||||
|
||||
if (box.computed_values().inset().left().is_auto() && specified_width.is_auto() && box.computed_values().inset().right().is_auto()) {
|
||||
if (box.computed_values().inset().left().is_auto() && box.computed_values().width().is_auto() && box.computed_values().inset().right().is_auto()) {
|
||||
if (box.computed_values().margin().left().is_auto())
|
||||
box_state.margin_left = 0;
|
||||
if (box.computed_values().margin().right().is_auto())
|
||||
|
@ -826,17 +816,17 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box)
|
|||
// NOTE: Absolutely positioned boxes are relative to the *padding edge* of the containing block.
|
||||
used_offset.translate_by(-containing_block_state.padding_left, -containing_block_state.padding_top);
|
||||
|
||||
box_state.offset = used_offset;
|
||||
box_state.set_content_offset(used_offset);
|
||||
|
||||
if (independent_formatting_context)
|
||||
independent_formatting_context->parent_context_did_dimension_child_root_box();
|
||||
}
|
||||
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const& box)
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
// 10.6.5 Absolutely positioned, replaced elements
|
||||
// The used value of 'height' is determined as for inline replaced elements.
|
||||
m_state.get_mutable(box).set_content_height(compute_height_for_replaced_element(m_state, box));
|
||||
m_state.get_mutable(box).set_content_height(compute_height_for_replaced_element(m_state, box, available_space));
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-position-3/#relpos-insets
|
||||
|
@ -879,49 +869,49 @@ void FormattingContext::compute_inset(Box const& box)
|
|||
resolve_two_opposing_insets(computed_values.inset().top(), computed_values.inset().bottom(), box_state.inset_top, box_state.inset_bottom, containing_block_height_for(box));
|
||||
}
|
||||
|
||||
float FormattingContext::calculate_fit_content_size(float min_content_size, float max_content_size, SizeConstraint constraint, Optional<float> available_space) const
|
||||
float FormattingContext::calculate_fit_content_size(float min_content_size, float max_content_size, AvailableSize const& available_size) const
|
||||
{
|
||||
// If the available space in a given axis is definite, equal to clamp(min-content size, stretch-fit size, max-content size)
|
||||
// (i.e. max(min-content size, min(max-content size, stretch-fit size))).
|
||||
if (available_space.has_value()) {
|
||||
if (available_size.is_definite()) {
|
||||
// FIXME: Compute the real stretch-fit size.
|
||||
auto stretch_fit_size = *available_space;
|
||||
auto stretch_fit_size = available_size.to_px();
|
||||
auto s = max(min_content_size, min(max_content_size, stretch_fit_size));
|
||||
return s;
|
||||
}
|
||||
|
||||
// When sizing under a min-content constraint, equal to the min-content size.
|
||||
if (constraint == SizeConstraint::MinContent)
|
||||
if (available_size.is_min_content())
|
||||
return min_content_size;
|
||||
|
||||
// Otherwise, equal to the max-content size in that axis.
|
||||
return max_content_size;
|
||||
}
|
||||
|
||||
float FormattingContext::calculate_fit_content_width(Layout::Box const& box, SizeConstraint constraint, Optional<float> available_space) const
|
||||
float FormattingContext::calculate_fit_content_width(Layout::Box const& box, AvailableSize const& available_size) const
|
||||
{
|
||||
// When sizing under a min-content constraint, equal to the min-content size.
|
||||
// NOTE: We check this first, to avoid needlessly calculating the max-content size.
|
||||
if (constraint == SizeConstraint::MinContent)
|
||||
if (available_size.is_min_content())
|
||||
return calculate_min_content_width(box);
|
||||
|
||||
if (constraint == SizeConstraint::MaxContent)
|
||||
if (available_size.is_max_content())
|
||||
return calculate_max_content_width(box);
|
||||
|
||||
return calculate_fit_content_size(calculate_min_content_width(box), calculate_max_content_width(box), constraint, available_space);
|
||||
return calculate_fit_content_size(calculate_min_content_width(box), calculate_max_content_width(box), available_size);
|
||||
}
|
||||
|
||||
float FormattingContext::calculate_fit_content_height(Layout::Box const& box, SizeConstraint constraint, Optional<float> available_space) const
|
||||
float FormattingContext::calculate_fit_content_height(Layout::Box const& box, AvailableSize const& available_size) const
|
||||
{
|
||||
// When sizing under a min-content constraint, equal to the min-content size.
|
||||
// NOTE: We check this first, to avoid needlessly calculating the max-content size.
|
||||
if (constraint == SizeConstraint::MinContent)
|
||||
if (available_size.is_min_content())
|
||||
return calculate_min_content_height(box);
|
||||
|
||||
if (constraint == SizeConstraint::MaxContent)
|
||||
if (available_size.is_max_content())
|
||||
return calculate_max_content_height(box);
|
||||
|
||||
return calculate_fit_content_size(calculate_min_content_height(box), calculate_max_content_height(box), constraint, available_space);
|
||||
return calculate_fit_content_size(calculate_min_content_height(box), calculate_max_content_height(box), available_size);
|
||||
}
|
||||
|
||||
float FormattingContext::calculate_min_content_width(Layout::Box const& box) const
|
||||
|
@ -936,14 +926,6 @@ float FormattingContext::calculate_min_content_width(Layout::Box const& box) con
|
|||
return *cache.min_content_width;
|
||||
|
||||
LayoutState throwaway_state(&m_state);
|
||||
auto const& containing_block = *box.containing_block();
|
||||
auto& containing_block_state = throwaway_state.get_mutable(containing_block);
|
||||
containing_block_state.set_content_width(0);
|
||||
|
||||
if (!containing_block_state.has_definite_height())
|
||||
containing_block_state.set_content_height(INFINITY);
|
||||
else if (containing_block.computed_values().height().is_auto())
|
||||
containing_block_state.set_content_height(containing_block_height_for(containing_block));
|
||||
|
||||
auto& box_state = throwaway_state.get_mutable(box);
|
||||
box_state.width_constraint = SizeConstraint::MinContent;
|
||||
|
@ -978,14 +960,6 @@ float FormattingContext::calculate_max_content_width(Layout::Box const& box) con
|
|||
return *cache.max_content_width;
|
||||
|
||||
LayoutState throwaway_state(&m_state);
|
||||
auto const& containing_block = *box.containing_block();
|
||||
auto& containing_block_state = throwaway_state.get_mutable(containing_block);
|
||||
containing_block_state.set_content_width(INFINITY);
|
||||
|
||||
if (!containing_block_state.has_definite_height())
|
||||
containing_block_state.set_content_height(INFINITY);
|
||||
else if (containing_block.computed_values().height().is_auto())
|
||||
containing_block_state.set_content_height(containing_block_height_for(containing_block));
|
||||
|
||||
auto& box_state = throwaway_state.get_mutable(box);
|
||||
box_state.width_constraint = SizeConstraint::MaxContent;
|
||||
|
@ -1020,14 +994,6 @@ float FormattingContext::calculate_min_content_height(Layout::Box const& box) co
|
|||
return *cache.min_content_height;
|
||||
|
||||
LayoutState throwaway_state(&m_state);
|
||||
auto const& containing_block = *box.containing_block();
|
||||
auto& containing_block_state = throwaway_state.get_mutable(containing_block);
|
||||
containing_block_state.set_content_height(0);
|
||||
|
||||
if (!containing_block_state.has_definite_width())
|
||||
containing_block_state.set_content_width(INFINITY);
|
||||
else if (containing_block.computed_values().width().is_auto())
|
||||
containing_block_state.set_content_width(containing_block_width_for(containing_block));
|
||||
|
||||
auto& box_state = throwaway_state.get_mutable(box);
|
||||
box_state.height_constraint = SizeConstraint::MinContent;
|
||||
|
@ -1058,14 +1024,6 @@ float FormattingContext::calculate_max_content_height(Layout::Box const& box) co
|
|||
return *cache.max_content_height;
|
||||
|
||||
LayoutState throwaway_state(&m_state);
|
||||
auto const& containing_block = *box.containing_block();
|
||||
auto& containing_block_state = throwaway_state.get_mutable(containing_block);
|
||||
containing_block_state.set_content_height(INFINITY);
|
||||
|
||||
if (!containing_block_state.has_definite_width())
|
||||
containing_block_state.set_content_width(INFINITY);
|
||||
else if (containing_block.computed_values().width().is_auto())
|
||||
containing_block_state.set_content_width(containing_block_width_for(containing_block));
|
||||
|
||||
auto& box_state = throwaway_state.get_mutable(box);
|
||||
box_state.height_constraint = SizeConstraint::MaxContent;
|
||||
|
@ -1181,7 +1139,7 @@ float FormattingContext::compute_box_y_position_with_respect_to_siblings(Box con
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/css-sizing-3/#stretch-fit-size
|
||||
float FormattingContext::calculate_stretch_fit_width(Box const& box, AvailableSpace const& available_width) const
|
||||
float FormattingContext::calculate_stretch_fit_width(Box const& box, AvailableSize const& available_width) const
|
||||
{
|
||||
// The size a box would take if its outer size filled the available space in the given axis;
|
||||
// in other words, the stretch fit into the available space, if that is definite.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue