LibWeb: Implement abspos for replaced elements

Added implementation and spec comments for sections 10.3.8 and 10.6.5,
with an implementation similar to the existing ones for non-replaced
elements.
This commit is contained in:
Sebastian Zaha 2023-07-04 22:46:58 +02:00 committed by Andreas Kling
parent 6a66a05809
commit 61fe7c230f
Notes: sideshowbarker 2024-07-17 08:36:27 +09:00

View file

@ -734,12 +734,84 @@ void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_ele
void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(Box 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.
// In this case, section 10.3.7 applies up through and including the constraint equation,
// but the rest of section 10.3.7 is replaced by the following rules:
// 1. The used value of 'width' is determined as for inline replaced elements.
if (is<ReplacedBox>(box)) {
// FIXME: This const_cast is gross.
static_cast<ReplacedBox&>(const_cast<Box&>(box)).prepare_for_replaced_layout();
}
m_state.get_mutable(box).set_content_width(compute_width_for_replaced_element(box, available_space));
auto width = compute_width_for_replaced_element(box, available_space);
auto width_of_containing_block = available_space.width.to_px();
auto available = width_of_containing_block - width;
auto const& computed_values = box.computed_values();
auto left = computed_values.inset().left();
auto margin_left = computed_values.margin().left();
auto right = computed_values.inset().right();
auto margin_right = computed_values.margin().right();
auto static_position = calculate_static_position(box);
auto to_px = [&](const CSS::LengthPercentage& l) {
return l.to_px(box, width_of_containing_block);
};
// If 'margin-left' or 'margin-right' is specified as 'auto' its used value is determined by the rules below.
// 2. If both 'left' and 'right' have the value 'auto', then if the 'direction' property of the
// element establishing the static-position containing block is 'ltr', set 'left' to the static
// position; else if 'direction' is 'rtl', set 'right' to the static position.
if (left.is_auto() && right.is_auto()) {
left = CSS::Length::make_px(static_position.x());
}
// 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' or 'margin-right' with '0'.
if (left.is_auto() || right.is_auto()) {
if (margin_left.is_auto())
margin_left = CSS::Length::make_px(0);
if (margin_right.is_auto())
margin_right = CSS::Length::make_px(0);
}
// 4. If at this point both 'margin-left' and 'margin-right' are still 'auto', solve the equation
// under the extra constraint that the two margins must get equal values, unless this would make
// them negative, in which case when the direction of the containing block is 'ltr' ('rtl'),
// set 'margin-left' ('margin-right') to zero and solve for 'margin-right' ('margin-left').
if (margin_left.is_auto() && margin_right.is_auto()) {
auto remainder = available - to_px(left) - to_px(right);
if (remainder < 0) {
margin_left = CSS::Length::make_px(0);
margin_right = CSS::Length::make_px(0);
} else {
margin_left = CSS::Length::make_px(remainder / 2);
margin_right = CSS::Length::make_px(remainder / 2);
}
}
// 5. If at this point there is an 'auto' left, solve the equation for that value.
if (left.is_auto()) {
left = CSS::Length::make_px(available - to_px(right) - to_px(margin_left) - to_px(margin_right));
} else if (right.is_auto()) {
right = CSS::Length::make_px(available - to_px(left) - to_px(margin_left) - to_px(margin_right));
} else if (margin_left.is_auto()) {
margin_left = CSS::Length::make_px(available - to_px(left) - to_px(right) - to_px(margin_right));
} else if (margin_right.is_auto()) {
margin_right = CSS::Length::make_px(available - to_px(left) - to_px(margin_left) - to_px(right));
}
// 6. If at this point the values are over-constrained, ignore the value for either 'left'
// (in case the 'direction' property of the containing block is 'rtl') or 'right'
// (in case 'direction' is 'ltr') and solve for that value.
if (0 != available - to_px(left) - to_px(right) - to_px(margin_left) - to_px(margin_right)) {
right = CSS::Length::make_px(available - to_px(left) - to_px(margin_left) - to_px(margin_right));
}
auto& box_state = m_state.get_mutable(box);
box_state.inset_left = to_px(left);
box_state.inset_right = to_px(right);
box_state.margin_left = to_px(margin_left);
box_state.margin_right = to_px(margin_right);
box_state.set_content_width(width);
}
// https://drafts.csswg.org/css-position-3/#abs-non-replaced-height
@ -1093,11 +1165,75 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box, Ava
independent_formatting_context->parent_context_did_dimension_child_root_box();
}
void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(Box const& box, AvailableSpace const& available_space, BeforeOrAfterInsideLayout)
void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(Box const& box, AvailableSpace const& available_space, BeforeOrAfterInsideLayout before_or_after_inside_layout)
{
// 10.6.5 Absolutely positioned, replaced elements
// This situation is similar to 10.6.4, except that the element has an intrinsic height.
// 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(box, available_space));
auto height = compute_height_for_replaced_element(box, available_space);
auto height_of_containing_block = available_space.height.to_px();
auto available = height_of_containing_block - height;
auto const& computed_values = box.computed_values();
auto top = computed_values.inset().top();
auto margin_top = computed_values.margin().top();
auto bottom = computed_values.inset().bottom();
auto margin_bottom = computed_values.margin().bottom();
auto static_position = calculate_static_position(box);
auto to_px = [&](const CSS::LengthPercentage& l) {
return l.to_px(box, height_of_containing_block);
};
// If 'margin-top' or 'margin-bottom' is specified as 'auto' its used value is determined by the rules below.
// 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' with the element's static position.
if (top.is_auto() && bottom.is_auto()) {
top = CSS::Length::make_px(static_position.x());
}
// 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 'margin-bottom' with '0'.
if (bottom.is_auto()) {
if (margin_top.is_auto())
margin_top = CSS::Length::make_px(0);
if (margin_bottom.is_auto())
margin_bottom = CSS::Length::make_px(0);
}
// 4. If at this point both 'margin-top' and 'margin-bottom' are still 'auto',
// solve the equation under the extra constraint that the two margins must get equal values.
if (margin_top.is_auto() && margin_bottom.is_auto()) {
auto remainder = available - to_px(top) - to_px(bottom);
margin_top = CSS::Length::make_px(remainder / 2);
margin_bottom = CSS::Length::make_px(remainder / 2);
}
// 5. If at this point there is an 'auto' left, solve the equation for that value.
if (top.is_auto()) {
top = CSS::Length::make_px(available - to_px(bottom) - to_px(margin_top) - to_px(margin_bottom));
} else if (bottom.is_auto()) {
bottom = CSS::Length::make_px(available - to_px(top) - to_px(margin_top) - to_px(margin_bottom));
} else if (margin_top.is_auto()) {
margin_top = CSS::Length::make_px(available - to_px(top) - to_px(bottom) - to_px(margin_bottom));
} else if (margin_bottom.is_auto()) {
margin_bottom = CSS::Length::make_px(available - to_px(top) - to_px(margin_top) - to_px(bottom));
}
// 6. If at this point the values are over-constrained, ignore the value for 'bottom' and solve for that value.
if (0 != available - to_px(top) - to_px(bottom) - to_px(margin_top) - to_px(margin_bottom)) {
bottom = CSS::Length::make_px(available - to_px(top) - to_px(margin_top) - to_px(margin_bottom));
}
auto& box_state = m_state.get_mutable(box);
box_state.set_content_height(height);
// do not set calculated insets or margins on the first pass, there will be a second pass
if (before_or_after_inside_layout == BeforeOrAfterInsideLayout::Before)
return;
box_state.inset_top = to_px(top);
box_state.inset_bottom = to_px(bottom);
box_state.margin_top = to_px(margin_top);
box_state.margin_bottom = to_px(margin_bottom);
}
// https://www.w3.org/TR/css-position-3/#relpos-insets