LibWeb: Fix "box-sizing: border-box" resolution for abspos items

The `calculate_inner_width()` and `calculate_inner_height()` resolve
percentage paddings using the width returned by
`containing_block_width_for()`. However, this function does not account
for grids where the containing block is defined by the grid area to
which an item belongs.

This change fixes the issue by modifying `calculate_inner_width()` and
`calculate_inner_height()` to use the already resolved paddings from the
layout state. Corresponding changes ensure that paddings are resolved
and saved in the state before box-sizing is handled.

As a side effect, this change also improves abspos layout for BFC where
now paddings are resolved using padding box of containing block instead
of content box of containing block.
This commit is contained in:
Aliaksandr Kalenik 2024-09-16 16:58:44 +02:00 committed by Andreas Kling
parent 805b0fed13
commit 5f74da6ae8
Notes: github-actions[bot] 2024-09-17 05:57:08 +00:00
7 changed files with 105 additions and 31 deletions

View file

@ -0,0 +1,11 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (1,1) content-size 798x330 [BFC] children: not-inline
BlockContainer <body> at (10,10) content-size 780x312 children: not-inline
BlockContainer <div.box> at (31,21) content-size 200x200 positioned children: not-inline
BlockContainer <div.inner> at (66,76) content-size 100x100 positioned [BFC] children: not-inline
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x332]
PaintableWithLines (BlockContainer<BODY>) [9,9 782x314]
PaintableWithLines (BlockContainer<DIV>.box) [10,10 272x312]
PaintableWithLines (BlockContainer<DIV>.inner) [11,21 210x210]

View file

@ -0,0 +1,11 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (1,1) content-size 798x120 [BFC] children: not-inline
BlockContainer <body> at (10,10) content-size 780x102 children: not-inline
Box <div.grid> at (11,11) content-size 200x100 positioned [GFC] children: not-inline
BlockContainer <div.abspos-item> at (122,22) content-size 28x28 positioned [BFC] children: not-inline
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x122]
PaintableWithLines (BlockContainer<BODY>) [9,9 782x104]
PaintableBox (Box<DIV>.grid) [10,10 202x102]
PaintableWithLines (BlockContainer<DIV>.abspos-item) [111,11 50x50]

View file

@ -0,0 +1,22 @@
<!DOCTYPE html><style>
* {
border: 1px solid black;
}
.box {
position: relative;
padding: 10px 50px 100px 20px;
background-color: mediumseagreen;
width: 200px;
height: 200px;
}
.inner {
position: absolute;
left: 0;
width : 100px;
height: 100px;
padding: 20%;
background-color: magenta;
}
</style><div class="box"><div class="inner"></div></div>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html><style>
* {
border: 1px solid black;
}
.grid {
display: grid;
grid-template-columns: 100px 100px;
grid-template-areas: "a b";
position: relative;
width: 200px;
height: 100px;
}
.abspos-item {
position: absolute;
box-sizing: border-box;
padding: 10%;
background-color: magenta;
background-clip: content-box;
top: 0;
left: 0;
width: 50px;
height: 50px;
grid-area: b;
}
</style><div class="grid"><div class="abspos-item"></div></div>

View file

@ -216,6 +216,13 @@ bool FlexFormattingContext::is_direction_reverse() const
void FlexFormattingContext::populate_specified_margins(FlexItem& item, CSS::FlexDirection flex_direction) const
{
auto width_of_containing_block = m_flex_container_state.content_width();
auto& state = m_state.get_mutable(*item.box);
state.padding_left = item.box->computed_values().padding().left().to_px(item.box, width_of_containing_block);
state.padding_right = item.box->computed_values().padding().right().to_px(item.box, width_of_containing_block);
state.padding_top = item.box->computed_values().padding().top().to_px(item.box, width_of_containing_block);
state.padding_bottom = item.box->computed_values().padding().bottom().to_px(item.box, width_of_containing_block);
// FIXME: This should also take reverse-ness into account
if (flex_direction == CSS::FlexDirection::Row || flex_direction == CSS::FlexDirection::RowReverse) {
item.borders.main_before = item.box->computed_values().border_left().width;
@ -243,10 +250,10 @@ void FlexFormattingContext::populate_specified_margins(FlexItem& item, CSS::Flex
item.borders.cross_before = item.box->computed_values().border_left().width;
item.borders.cross_after = item.box->computed_values().border_right().width;
item.padding.main_before = item.box->computed_values().padding().top().to_px(item.box, width_of_containing_block);
item.padding.main_after = item.box->computed_values().padding().bottom().to_px(item.box, width_of_containing_block);
item.padding.cross_before = item.box->computed_values().padding().left().to_px(item.box, width_of_containing_block);
item.padding.cross_after = item.box->computed_values().padding().right().to_px(item.box, width_of_containing_block);
item.padding.main_before = state.padding_top;
item.padding.main_after = state.padding_bottom;
item.padding.cross_before = state.padding_left;
item.padding.cross_after = state.padding_right;
item.margins.main_before = item.box->computed_values().margin().top().to_px(item.box, width_of_containing_block);
item.margins.main_after = item.box->computed_values().margin().bottom().to_px(item.box, width_of_containing_block);
@ -1604,11 +1611,6 @@ void FlexFormattingContext::copy_dimensions_from_flex_items_to_boxes()
for (auto& item : m_flex_items) {
auto const& box = item.box;
item.used_values.padding_left = box->computed_values().padding().left().to_px(box, m_flex_container_state.content_width());
item.used_values.padding_right = box->computed_values().padding().right().to_px(box, m_flex_container_state.content_width());
item.used_values.padding_top = box->computed_values().padding().top().to_px(box, m_flex_container_state.content_width());
item.used_values.padding_bottom = box->computed_values().padding().bottom().to_px(box, m_flex_container_state.content_width());
item.used_values.margin_left = box->computed_values().margin().left().to_px(box, m_flex_container_state.content_width());
item.used_values.margin_right = box->computed_values().margin().right().to_px(box, m_flex_container_state.content_width());
item.used_values.margin_top = box->computed_values().margin().top().to_px(box, m_flex_container_state.content_width());

View file

@ -667,13 +667,14 @@ void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_ele
auto width_of_containing_block = available_space.width.to_px_or_zero();
auto const& computed_values = box.computed_values();
auto zero_value = CSS::Length::make_px(0);
auto& box_state = m_state.get_mutable(box);
auto margin_left = CSS::Length::make_auto();
auto margin_right = CSS::Length::make_auto();
auto const border_left = computed_values.border_left().width;
auto const border_right = computed_values.border_right().width;
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);
auto const padding_left = box_state.padding_left;
auto const padding_right = box_state.padding_right;
auto computed_left = computed_values.inset().left();
auto computed_right = computed_values.inset().right();
@ -835,14 +836,11 @@ void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_ele
}
}
auto& box_state = m_state.get_mutable(box);
box_state.set_content_width(used_width.to_px(box));
box_state.inset_left = left;
box_state.inset_right = right;
box_state.margin_left = margin_left.to_px(box);
box_state.margin_right = margin_right.to_px(box);
box_state.padding_left = padding_left;
box_state.padding_right = padding_right;
}
void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(Box const& box, AvailableSpace const& available_space)
@ -962,7 +960,7 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
auto top = box.computed_values().inset().top();
auto bottom = box.computed_values().inset().bottom();
auto width_of_containing_block = containing_block_width_for(box);
auto width_of_containing_block = available_space.width.to_px_or_zero();
auto height_of_containing_block = available_space.height.to_px_or_zero();
enum class ClampToZero {
@ -970,15 +968,16 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
Yes,
};
auto& state = m_state.get(box);
auto try_compute_height = [&](CSS::Length height) -> CSS::Length {
auto solve_for = [&](CSS::Length length, ClampToZero clamp_to_zero = ClampToZero::No) {
auto unclamped_value = height_of_containing_block
- top.to_px(box, height_of_containing_block)
- margin_top.to_px(box, width_of_containing_block)
- box.computed_values().border_top().width
- box.computed_values().padding().top().to_px(box, width_of_containing_block)
- state.padding_top
- apply_min_max_height_constraints(height).to_px(box)
- box.computed_values().padding().bottom().to_px(box, width_of_containing_block)
- state.padding_bottom
- box.computed_values().border_bottom().width
- margin_bottom.to_px(box, width_of_containing_block)
- bottom.to_px(box, height_of_containing_block)
@ -1157,8 +1156,6 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
box_state.inset_bottom = bottom.to_px(box, height_of_containing_block);
box_state.margin_top = margin_top.to_px(box, width_of_containing_block);
box_state.margin_bottom = margin_bottom.to_px(box, width_of_containing_block);
box_state.padding_top = box.computed_values().padding().top().to_px(box, width_of_containing_block);
box_state.padding_bottom = box.computed_values().padding().bottom().to_px(box, width_of_containing_block);
}
// NOTE: This is different from content_box_rect_in_ancestor_coordinate_space() as this does *not* follow the containing block chain up, but rather the parent() chain.
@ -1241,6 +1238,12 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box, Ava
box_state.border_top = box.computed_values().border_top().width;
box_state.border_bottom = box.computed_values().border_bottom().width;
auto const containing_block_width = available_space.width.to_px_or_zero();
box_state.padding_left = box.computed_values().padding().left().to_px(box, containing_block_width);
box_state.padding_right = box.computed_values().padding().right().to_px(box, containing_block_width);
box_state.padding_top = box.computed_values().padding().top().to_px(box, containing_block_width);
box_state.padding_bottom = box.computed_values().padding().bottom().to_px(box, containing_block_width);
compute_width_for_absolutely_positioned_element(box, available_space);
// NOTE: We compute height before *and* after doing inside layout.
@ -1625,14 +1628,12 @@ CSSPixels FormattingContext::calculate_inner_width(Layout::Box const& box, Avail
auto& computed_values = box.computed_values();
if (computed_values.box_sizing() == CSS::BoxSizing::BorderBox) {
auto const padding_left = computed_values.padding().left().resolved(box, width_of_containing_block);
auto const padding_right = computed_values.padding().right().resolved(box, width_of_containing_block);
auto const& state = m_state.get(box);
auto inner_width = width.to_px(box, width_of_containing_block)
- computed_values.border_left().width
- padding_left.to_px(box)
- state.padding_left
- computed_values.border_right().width
- padding_right.to_px(box);
- state.padding_right;
return max(inner_width, 0);
}
@ -1645,16 +1646,12 @@ CSSPixels FormattingContext::calculate_inner_height(Layout::Box const& box, Avai
auto height_of_containing_block = available_height.to_px_or_zero();
auto& computed_values = box.computed_values();
if (computed_values.box_sizing() == CSS::BoxSizing::BorderBox) {
auto width_of_containing_block = containing_block_width_for(box);
auto const padding_top = computed_values.padding().top().resolved(box, width_of_containing_block);
auto const padding_bottom = computed_values.padding().bottom().resolved(box, width_of_containing_block);
auto const& state = m_state.get(box);
auto inner_height = height.to_px(box, height_of_containing_block)
- computed_values.border_top().width
- padding_top.to_px(box)
- state.padding_top
- computed_values.border_bottom().width
- padding_bottom.to_px(box);
- state.padding_bottom;
return max(inner_height, 0);
}

View file

@ -1923,6 +1923,10 @@ void GridFormattingContext::layout_absolutely_positioned_element(Box const& box)
box_state.border_right = box.computed_values().border_right().width;
box_state.border_top = box.computed_values().border_top().width;
box_state.border_bottom = box.computed_values().border_bottom().width;
box_state.padding_left = box.computed_values().padding().left().to_px(grid_container(), grid_area_rect.width());
box_state.padding_right = box.computed_values().padding().right().to_px(grid_container(), grid_area_rect.width());
box_state.padding_top = box.computed_values().padding().top().to_px(grid_container(), grid_area_rect.width());
box_state.padding_bottom = box.computed_values().padding().bottom().to_px(grid_container(), grid_area_rect.width());
compute_width_for_absolutely_positioned_element(box, available_space);