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

LibWeb: Improve vertical margin collapse between adjacent blocks

Collect all the preceding block-level siblings whose vertical margins
are collapsible. Both margin-top and margin-bottom now (previously,
we only considered the margin-bottom of siblings.)

Use the right margin in part-negative and all-negative situations.
This commit is contained in:
Andreas Kling 2022-03-25 00:05:56 +01:00
parent aac4382cd1
commit c02e6f991a
2 changed files with 41 additions and 25 deletions

View file

@ -329,9 +329,8 @@ void BlockFormattingContext::compute_height(Box const& box, FormattingState& sta
auto& box_state = state.get_mutable(box);
// FIXME: While negative values are generally allowed for margins, for now just ignore those for height calculation
box_state.margin_top = max(computed_values.margin().top.resolved(box, width_of_containing_block_as_length).to_px(box), 0);
box_state.margin_bottom = max(computed_values.margin().bottom.resolved(box, width_of_containing_block_as_length).to_px(box), 0);
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;
@ -439,42 +438,57 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically
compute_vertical_box_model_metrics(child_box, containing_block);
float y = box_state.margin_box_top()
float y = box_state.border_box_top()
+ box_state.offset_top;
// NOTE: Empty (0-height) preceding siblings have their margins collapsed with *their* preceding sibling, etc.
float collapsed_bottom_margin_of_preceding_siblings = 0;
Vector<float> collapsible_margins;
auto* relevant_sibling = child_box.previous_sibling_of_type<Layout::BlockContainer>();
while (relevant_sibling != nullptr) {
if (!relevant_sibling->is_absolutely_positioned() && !relevant_sibling->is_floating()) {
auto const& relevant_sibling_state = m_state.get(*relevant_sibling);
collapsed_bottom_margin_of_preceding_siblings = max(collapsed_bottom_margin_of_preceding_siblings, relevant_sibling_state.margin_bottom);
collapsible_margins.append(relevant_sibling_state.margin_bottom);
// NOTE: Empty (0-height) preceding siblings have their margins collapsed with *their* preceding sibling, etc.
if (relevant_sibling_state.border_box_height() > 0)
break;
collapsible_margins.append(relevant_sibling_state.margin_top);
}
relevant_sibling = relevant_sibling->previous_sibling_of_type<Layout::BlockContainer>();
}
if (relevant_sibling) {
// Collapse top margin with the collapsed margin(s) of preceding siblings.
collapsible_margins.append(box_state.margin_top);
float smallest_margin = 0;
float largest_margin = 0;
size_t negative_margin_count = 0;
for (auto margin : collapsible_margins) {
if (margin < 0)
++negative_margin_count;
largest_margin = max(largest_margin, margin);
smallest_margin = min(smallest_margin, margin);
}
float collapsed_margin = 0;
if (negative_margin_count == collapsible_margins.size()) {
// When all margins are negative, the size of the collapsed margin is the smallest (most negative) margin.
collapsed_margin = smallest_margin;
} else if (negative_margin_count > 0) {
// When negative margins are involved, the size of the collapsed margin is the sum of the largest positive margin and the smallest (most negative) negative margin.
collapsed_margin = largest_margin + smallest_margin;
} else {
// Otherwise, collapse all the adjacent margins by using only the largest one.
collapsed_margin = largest_margin;
}
auto const& relevant_sibling_state = m_state.get(*relevant_sibling);
y += relevant_sibling_state.offset.y()
+ relevant_sibling_state.content_height
+ relevant_sibling_state.border_box_bottom();
// Collapse top margin with bottom margin of preceding siblings if needed
float my_margin_top = box_state.margin_top;
if (my_margin_top < 0 || collapsed_bottom_margin_of_preceding_siblings < 0) {
// Negative margins present.
float largest_negative_margin = -min(my_margin_top, collapsed_bottom_margin_of_preceding_siblings);
float largest_positive_margin = (my_margin_top < 0 && collapsed_bottom_margin_of_preceding_siblings < 0) ? 0 : max(my_margin_top, collapsed_bottom_margin_of_preceding_siblings);
float final_margin = largest_positive_margin - largest_negative_margin;
y += final_margin - my_margin_top;
} else if (collapsed_bottom_margin_of_preceding_siblings > my_margin_top) {
// Sibling's margin is larger than mine, adjust so we use sibling's.
y += collapsed_bottom_margin_of_preceding_siblings - my_margin_top;
}
+ relevant_sibling_state.border_box_bottom()
+ collapsed_margin;
} else {
y += box_state.margin_top;
}
auto clear_floating_boxes = [&](FloatSideData& float_side) {