mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 15:57:45 +00:00
LibWeb: Calculate height of absolute-position elements according to spec
This patch implements the full "old model" height algorithm from the CSS Positioned Layout spec. I went with the old model since we don't yet have the machinery required to implement the new model. Also, the width calculations already follow the old model, so this is symmetric with that. Eventually we should of course implement the new positioned layout model.
This commit is contained in:
parent
e77798f5fd
commit
84953c5020
1 changed files with 197 additions and 42 deletions
|
@ -683,60 +683,215 @@ void FormattingContext::compute_width_for_absolutely_positioned_replaced_element
|
|||
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
|
||||
// https://drafts.csswg.org/css-position-3/#abs-non-replaced-height
|
||||
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
|
||||
// 5.3. The Height Of Absolutely Positioned, Non-Replaced Elements
|
||||
|
||||
// FIXME: The section below is partly on-spec, partly ad-hoc.
|
||||
auto& computed_values = box.computed_values();
|
||||
// For absolutely positioned elements, the used values of the vertical dimensions must satisfy this constraint:
|
||||
// top + margin-top + border-top-width + padding-top + height + padding-bottom + border-bottom-width + margin-bottom + bottom = height of containing block
|
||||
|
||||
auto margin_top = box.computed_values().margin().top();
|
||||
auto margin_bottom = box.computed_values().margin().bottom();
|
||||
auto top = box.computed_values().inset().top();
|
||||
auto bottom = box.computed_values().inset().bottom();
|
||||
auto height = box.computed_values().height();
|
||||
|
||||
auto width_of_containing_block = containing_block_width_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);
|
||||
|
||||
auto const& computed_top = computed_values.inset().top();
|
||||
auto const& computed_bottom = computed_values.inset().bottom();
|
||||
auto const& computed_height = computed_values.height();
|
||||
auto const& computed_min_height = computed_values.min_height();
|
||||
auto const& computed_max_height = computed_values.max_height();
|
||||
auto solve_for_top = [&] {
|
||||
top = CSS::Length::make_px(
|
||||
height_of_containing_block
|
||||
- margin_top.length().to_px(box)
|
||||
- box.computed_values().border_top().width
|
||||
- box.computed_values().padding().top().length().to_px(box)
|
||||
- height.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- box.computed_values().padding().bottom().length().to_px(box)
|
||||
- box.computed_values().border_bottom().width
|
||||
- margin_bottom.length().to_px(box)
|
||||
- bottom.resolved(box, height_of_containing_block_as_length).to_px(box));
|
||||
};
|
||||
|
||||
auto used_top = computed_top.resolved(box, height_of_containing_block_as_length).resolved(box).to_px(box);
|
||||
auto used_bottom = computed_bottom.resolved(box, height_of_containing_block_as_length).resolved(box).to_px(box);
|
||||
auto tentative_height = CSS::Length::make_auto();
|
||||
auto solve_for_bottom = [&] {
|
||||
bottom = CSS::Length::make_px(
|
||||
height_of_containing_block
|
||||
- top.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- margin_top.length().to_px(box)
|
||||
- box.computed_values().border_top().width
|
||||
- box.computed_values().padding().top().length().to_px(box)
|
||||
- height.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- box.computed_values().padding().bottom().length().to_px(box)
|
||||
- box.computed_values().border_bottom().width
|
||||
- margin_bottom.length().to_px(box));
|
||||
};
|
||||
|
||||
if (!computed_height.is_auto())
|
||||
tentative_height = computed_values.height().resolved(box, height_of_containing_block_as_length).resolved(box);
|
||||
auto solve_for_height = [&] {
|
||||
height = CSS::Size::make_px(
|
||||
height_of_containing_block
|
||||
- top.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- margin_top.length().to_px(box)
|
||||
- box.computed_values().border_top().width
|
||||
- box.computed_values().padding().top().length().to_px(box)
|
||||
- box.computed_values().padding().bottom().length().to_px(box)
|
||||
- box.computed_values().border_bottom().width
|
||||
- margin_bottom.length().to_px(box)
|
||||
- bottom.resolved(box, height_of_containing_block_as_length).to_px(box));
|
||||
};
|
||||
|
||||
auto solve_for_margin_top = [&] {
|
||||
height = CSS::Size::make_px(
|
||||
height_of_containing_block
|
||||
- top.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- box.computed_values().border_top().width
|
||||
- box.computed_values().padding().top().length().to_px(box)
|
||||
- height.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- box.computed_values().padding().bottom().length().to_px(box)
|
||||
- box.computed_values().border_bottom().width
|
||||
- margin_bottom.length().to_px(box)
|
||||
- bottom.resolved(box, height_of_containing_block_as_length).to_px(box));
|
||||
};
|
||||
|
||||
auto solve_for_margin_bottom = [&] {
|
||||
height = CSS::Size::make_px(
|
||||
height_of_containing_block
|
||||
- top.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- margin_top.length().to_px(box)
|
||||
- box.computed_values().border_top().width
|
||||
- box.computed_values().padding().top().length().to_px(box)
|
||||
- height.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- box.computed_values().padding().bottom().length().to_px(box)
|
||||
- box.computed_values().border_bottom().width
|
||||
- bottom.resolved(box, height_of_containing_block_as_length).to_px(box));
|
||||
};
|
||||
|
||||
auto solve_for_margin_top_and_margin_bottom = [&] {
|
||||
auto remainder = height_of_containing_block
|
||||
- top.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- box.computed_values().border_top().width
|
||||
- box.computed_values().padding().top().length().to_px(box)
|
||||
- height.resolved(box, height_of_containing_block_as_length).to_px(box)
|
||||
- box.computed_values().padding().bottom().length().to_px(box)
|
||||
- box.computed_values().border_bottom().width
|
||||
- bottom.resolved(box, height_of_containing_block_as_length).to_px(box);
|
||||
|
||||
margin_top = CSS::Length::make_px(remainder / 2);
|
||||
margin_bottom = CSS::Length::make_px(remainder / 2);
|
||||
};
|
||||
|
||||
// If all three of top, height, and bottom are auto:
|
||||
if (top.is_auto() && height.is_auto() && bottom.is_auto()) {
|
||||
// First set any auto values for margin-top and margin-bottom to 0,
|
||||
if (margin_top.is_auto())
|
||||
margin_top = CSS::Length::make_px(0);
|
||||
if (margin_bottom.is_auto())
|
||||
margin_bottom = CSS::Length::make_px(0);
|
||||
|
||||
// then set top to the static position,
|
||||
auto static_position = calculate_static_position(box);
|
||||
top = CSS::Length::make_px(static_position.y());
|
||||
|
||||
// and finally apply rule number three below.
|
||||
height = CSS::Size::make_px(compute_auto_height_for_block_formatting_context_root(verify_cast<BlockContainer>(box)));
|
||||
solve_for_bottom();
|
||||
}
|
||||
|
||||
// If none of the three are auto:
|
||||
else if (!top.is_auto() && !height.is_auto() && !bottom.is_auto()) {
|
||||
// If both margin-top and margin-bottom are auto,
|
||||
if (margin_top.is_auto() && margin_bottom.is_auto()) {
|
||||
// solve the equation under the extra constraint that the two margins get equal values.
|
||||
solve_for_margin_top_and_margin_bottom();
|
||||
}
|
||||
|
||||
// If one of margin-top or margin-bottom is auto,
|
||||
else if (margin_top.is_auto() || margin_bottom.is_auto()) {
|
||||
// solve the equation for that value.
|
||||
if (margin_top.is_auto())
|
||||
solve_for_margin_top();
|
||||
else
|
||||
solve_for_margin_bottom();
|
||||
}
|
||||
|
||||
// If the values are over-constrained,
|
||||
else {
|
||||
// ignore the value for bottom and solve for that value.
|
||||
solve_for_bottom();
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise,
|
||||
else {
|
||||
// set auto values for margin-top and margin-bottom to 0,
|
||||
if (margin_top.is_auto())
|
||||
margin_top = CSS::Length::make_px(0);
|
||||
if (margin_bottom.is_auto())
|
||||
margin_bottom = CSS::Length::make_px(0);
|
||||
|
||||
// and pick one of the following six rules that apply.
|
||||
|
||||
// 1. If top and height are auto and bottom is not auto,
|
||||
if (top.is_auto() && height.is_auto() && !bottom.is_auto()) {
|
||||
// then the height is based on the Auto heights for block formatting context roots,
|
||||
height = CSS::Size::make_px(compute_auto_height_for_block_formatting_context_root(verify_cast<BlockContainer>(box)));
|
||||
|
||||
// and solve for top.
|
||||
solve_for_top();
|
||||
}
|
||||
|
||||
// 2. If top and bottom are auto and height is not auto,
|
||||
else if (top.is_auto() && bottom.is_auto() && !height.is_auto()) {
|
||||
// then set top to the static position,
|
||||
top = CSS::Length::make_px(calculate_static_position(box).y());
|
||||
|
||||
// then solve for bottom.
|
||||
solve_for_bottom();
|
||||
}
|
||||
|
||||
// 3. If height and bottom are auto and top is not auto,
|
||||
else if (height.is_auto() && bottom.is_auto() && !top.is_auto()) {
|
||||
// then the height is based on the Auto heights for block formatting context roots,
|
||||
height = CSS::Size::make_px(compute_auto_height_for_block_formatting_context_root(verify_cast<BlockContainer>(box)));
|
||||
|
||||
// and solve for bottom.
|
||||
solve_for_bottom();
|
||||
}
|
||||
|
||||
// 4. If top is auto, height and bottom are not auto,
|
||||
if (top.is_auto() && !height.is_auto() && !bottom.is_auto()) {
|
||||
// then solve for top.
|
||||
solve_for_top();
|
||||
}
|
||||
|
||||
// 5. If height is auto, top and bottom are not auto,
|
||||
if (height.is_auto() && !top.is_auto() && !bottom.is_auto()) {
|
||||
// then solve for height.
|
||||
solve_for_height();
|
||||
}
|
||||
|
||||
// 6. If bottom is auto, top and height are not auto,
|
||||
if (bottom.is_auto() && !top.is_auto() && !height.is_auto()) {
|
||||
// then solve for bottom.
|
||||
solve_for_bottom();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The following is not directly part of any spec, but this is where we resolve
|
||||
// the final used values for vertical margin/border/padding.
|
||||
|
||||
auto 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& box_state = m_state.get_mutable(box);
|
||||
box_state.margin_top = computed_values.margin().top().resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
box_state.margin_bottom = computed_values.margin().bottom().resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
box_state.border_top = computed_values.border_top().width;
|
||||
box_state.border_bottom = computed_values.border_bottom().width;
|
||||
box_state.padding_top = computed_values.padding().top().resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
box_state.padding_bottom = computed_values.padding().bottom().resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
box_state.margin_top = margin_top.resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
box_state.margin_bottom = margin_bottom.resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
box_state.border_top = box.computed_values().border_top().width;
|
||||
box_state.border_bottom = box.computed_values().border_bottom().width;
|
||||
box_state.padding_top = box.computed_values().padding().top().resolved(box, width_of_containing_block_as_length).to_px(box);
|
||||
box_state.padding_bottom = box.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(box, available_space), 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(box, available_space), 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;
|
||||
}
|
||||
|
||||
else if (computed_height.is_auto() && !computed_top.is_auto() && !computed_bottom.is_auto()) {
|
||||
tentative_height = CSS::Length(height_of_containing_block - used_top - box_state.margin_top - box_state.padding_top - box_state.border_top - used_bottom - box_state.margin_bottom - box_state.padding_bottom - box_state.border_bottom, CSS::Length::Type::Px);
|
||||
}
|
||||
|
||||
float used_height = tentative_height.to_px(box);
|
||||
if (!computed_max_height.is_none())
|
||||
used_height = min(used_height, computed_max_height.resolved(box, height_of_containing_block_as_length).resolved(box).to_px(box));
|
||||
if (!computed_min_height.is_auto())
|
||||
used_height = max(used_height, computed_min_height.resolved(box, height_of_containing_block_as_length).resolved(box).to_px(box));
|
||||
|
||||
box_state.set_content_height(used_height);
|
||||
// And here is where we assign the box's content height.
|
||||
box_state.set_content_height(height.resolved(box, height_of_containing_block_as_length).to_px(box));
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue