LibWeb: Make align-content on flex container behave more correctly

In particular, this property now interacts correctly when the flex
container has flex-wrap: wrap-reverse.

This caused some "regressions" in WPT tests for negative overflow in
flex containers, but the previous behavior wasn't correct either,
it just happened to give false positives on tests.
This commit is contained in:
Andreas Kling 2024-10-27 22:30:45 +01:00 committed by Andreas Kling
commit 64f18a93c2
Notes: github-actions[bot] 2024-10-30 09:18:14 +00:00
4 changed files with 159 additions and 107 deletions

View file

@ -6,8 +6,8 @@ Rerun
Found 72 tests Found 72 tests
54 Pass 71 Pass
18 Fail 1 Fail
Details Details
Result Test Name MessagePass .flexbox div 1 Result Test Name MessagePass .flexbox div 1
Pass .flexbox div 2 Pass .flexbox div 2
@ -17,22 +17,22 @@ Pass .flexbox div 5
Pass .flexbox div 6 Pass .flexbox div 6
Pass .flexbox div 7 Pass .flexbox div 7
Pass .flexbox div 8 Pass .flexbox div 8
Fail .flexbox div 9 Pass .flexbox div 9
Fail .flexbox div 10 Pass .flexbox div 10
Fail .flexbox div 11 Pass .flexbox div 11
Fail .flexbox div 12 Pass .flexbox div 12
Fail .flexbox div 13 Pass .flexbox div 13
Fail .flexbox div 14 Pass .flexbox div 14
Fail .flexbox div 15 Pass .flexbox div 15
Fail .flexbox div 16 Pass .flexbox div 16
Fail .flexbox div 17 Pass .flexbox div 17
Fail .flexbox div 18 Pass .flexbox div 18
Fail .flexbox div 19 Pass .flexbox div 19
Fail .flexbox div 20 Pass .flexbox div 20
Fail .flexbox div 21 Pass .flexbox div 21
Fail .flexbox div 22 Pass .flexbox div 22
Fail .flexbox div 23 Pass .flexbox div 23
Fail .flexbox div 24 Pass .flexbox div 24
Pass .flexbox div 25 Pass .flexbox div 25
Pass .flexbox div 26 Pass .flexbox div 26
Pass .flexbox div 27 Pass .flexbox div 27
@ -41,7 +41,7 @@ Pass .flexbox div 29
Pass .flexbox div 30 Pass .flexbox div 30
Pass .flexbox div 31 Pass .flexbox div 31
Pass .flexbox div 32 Pass .flexbox div 32
Fail .flexbox div 33 Pass .flexbox div 33
Pass .flexbox div 34 Pass .flexbox div 34
Pass .flexbox div 35 Pass .flexbox div 35
Pass .flexbox div 36 Pass .flexbox div 36

View file

@ -6,8 +6,8 @@ Rerun
Found 72 tests Found 72 tests
54 Pass 71 Pass
18 Fail 1 Fail
Details Details
Result Test Name MessagePass .flexbox div 1 Result Test Name MessagePass .flexbox div 1
Pass .flexbox div 2 Pass .flexbox div 2
@ -17,22 +17,22 @@ Pass .flexbox div 5
Pass .flexbox div 6 Pass .flexbox div 6
Pass .flexbox div 7 Pass .flexbox div 7
Pass .flexbox div 8 Pass .flexbox div 8
Fail .flexbox div 9 Pass .flexbox div 9
Fail .flexbox div 10 Pass .flexbox div 10
Fail .flexbox div 11 Pass .flexbox div 11
Fail .flexbox div 12 Pass .flexbox div 12
Fail .flexbox div 13 Pass .flexbox div 13
Fail .flexbox div 14 Pass .flexbox div 14
Fail .flexbox div 15 Pass .flexbox div 15
Fail .flexbox div 16 Pass .flexbox div 16
Fail .flexbox div 17 Pass .flexbox div 17
Fail .flexbox div 18 Pass .flexbox div 18
Fail .flexbox div 19 Pass .flexbox div 19
Fail .flexbox div 20 Pass .flexbox div 20
Fail .flexbox div 21 Pass .flexbox div 21
Fail .flexbox div 22 Pass .flexbox div 22
Fail .flexbox div 23 Pass .flexbox div 23
Fail .flexbox div 24 Pass .flexbox div 24
Pass .flexbox div 25 Pass .flexbox div 25
Pass .flexbox div 26 Pass .flexbox div 26
Pass .flexbox div 27 Pass .flexbox div 27
@ -41,7 +41,7 @@ Pass .flexbox div 29
Pass .flexbox div 30 Pass .flexbox div 30
Pass .flexbox div 31 Pass .flexbox div 31
Pass .flexbox div 32 Pass .flexbox div 32
Fail .flexbox div 33 Pass .flexbox div 33
Pass .flexbox div 34 Pass .flexbox div 34
Pass .flexbox div 35 Pass .flexbox div 35
Pass .flexbox div 36 Pass .flexbox div 36

View file

@ -6,8 +6,8 @@ Rerun
Found 11 tests Found 11 tests
5 Pass 4 Pass
6 Fail 7 Fail
Details Details
Result Test Name MessageFail .flexbox 1 Result Test Name MessageFail .flexbox 1
Fail .flexbox 2 Fail .flexbox 2
@ -19,4 +19,4 @@ Fail .flexbox 7
Fail .flexbox 8 Fail .flexbox 8
Pass .flexbox 9 Pass .flexbox 9
Pass .flexbox 10 Pass .flexbox 10
Pass .flexbox 11 Fail .flexbox 11

View file

@ -828,9 +828,6 @@ void FlexFormattingContext::collect_flex_items_into_flex_lines()
line_main_size += main_gap(); line_main_size += main_gap();
} }
m_flex_lines.append(move(line)); m_flex_lines.append(move(line));
if (flex_container().computed_values().flex_wrap() == CSS::FlexWrap::WrapReverse)
m_flex_lines.reverse();
} }
// https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths // https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
@ -1517,13 +1514,17 @@ void FlexFormattingContext::align_all_flex_items_along_the_cross_axis()
} }
} }
// https://www.w3.org/TR/css-flexbox-1/#algo-line-align // https://drafts.csswg.org/css-flexbox-1/#algo-line-align
void FlexFormattingContext::align_all_flex_lines() void FlexFormattingContext::align_all_flex_lines()
{ {
// Align all flex lines per align-content.
if (m_flex_lines.is_empty()) if (m_flex_lines.is_empty())
return; return;
// FIXME: Support reverse bool wrap_reverse = flex_container().computed_values().flex_wrap() == CSS::FlexWrap::WrapReverse;
bool place_items_backwards = false;
bool iterate_lines_backwards = false;
CSSPixels cross_size_of_flex_container = inner_cross_size(m_flex_container_state); CSSPixels cross_size_of_flex_container = inner_cross_size(m_flex_container_state);
@ -1534,76 +1535,117 @@ void FlexFormattingContext::align_all_flex_lines()
for (auto& item : flex_line.items) { for (auto& item : flex_line.items) {
item.cross_offset += center_of_line; item.cross_offset += center_of_line;
} }
} else { return;
}
CSSPixels sum_of_flex_line_cross_sizes = 0; CSSPixels sum_of_flex_line_cross_sizes = 0;
for (auto& line : m_flex_lines) for (auto& line : m_flex_lines)
sum_of_flex_line_cross_sizes += line.cross_size; sum_of_flex_line_cross_sizes += line.cross_size;
// CSS-FLEXBOX-2: Account for gap between flex lines. // CSS-FLEXBOX-2: Account for gap between flex lines.
sum_of_flex_line_cross_sizes += cross_gap() * (m_flex_lines.size() - 1); sum_of_flex_line_cross_sizes += cross_gap() * (m_flex_lines.size() - 1);
CSSPixels start_of_current_line = 0; CSSPixels start_of_current_line = 0;
CSSPixels gap_size = 0; CSSPixels gap_size = 0;
switch (flex_container().computed_values().align_content()) { switch (flex_container().computed_values().align_content()) {
case CSS::AlignContent::FlexStart: case CSS::AlignContent::Start:
case CSS::AlignContent::Start: start_of_current_line = 0;
iterate_lines_backwards = wrap_reverse;
break;
case CSS::AlignContent::End:
start_of_current_line = cross_size_of_flex_container;
place_items_backwards = true;
iterate_lines_backwards = !wrap_reverse;
break;
case CSS::AlignContent::FlexStart:
if (wrap_reverse) {
start_of_current_line = cross_size_of_flex_container;
place_items_backwards = true;
} else {
start_of_current_line = 0; start_of_current_line = 0;
break; }
case CSS::AlignContent::FlexEnd: break;
case CSS::AlignContent::End: case CSS::AlignContent::FlexEnd:
start_of_current_line = cross_size_of_flex_container - sum_of_flex_line_cross_sizes; iterate_lines_backwards = true;
break; if (wrap_reverse) {
case CSS::AlignContent::Center: start_of_current_line = 0;
} else {
start_of_current_line = cross_size_of_flex_container;
place_items_backwards = true;
}
break;
case CSS::AlignContent::Center:
iterate_lines_backwards = wrap_reverse;
start_of_current_line = (cross_size_of_flex_container / 2) - (sum_of_flex_line_cross_sizes / 2);
break;
case CSS::AlignContent::SpaceBetween: {
if (wrap_reverse) {
start_of_current_line = cross_size_of_flex_container;
place_items_backwards = true;
} else {
start_of_current_line = 0;
}
auto leftover_free_space = cross_size_of_flex_container - sum_of_flex_line_cross_sizes;
auto leftover_flex_lines_size = m_flex_lines.size();
if (leftover_free_space >= 0 && leftover_flex_lines_size > 1) {
int gap_count = leftover_flex_lines_size - 1;
gap_size = leftover_free_space / gap_count;
}
break;
}
case CSS::AlignContent::SpaceAround: {
iterate_lines_backwards = wrap_reverse;
auto leftover_free_space = cross_size_of_flex_container - sum_of_flex_line_cross_sizes;
if (leftover_free_space < 0) {
// If the leftover free-space is negative this value is identical to center.
start_of_current_line = (cross_size_of_flex_container / 2) - (sum_of_flex_line_cross_sizes / 2); start_of_current_line = (cross_size_of_flex_container / 2) - (sum_of_flex_line_cross_sizes / 2);
break; break;
case CSS::AlignContent::SpaceBetween: { }
gap_size = leftover_free_space / m_flex_lines.size();
// The spacing between the first/last lines and the flex container edges is half the size of the spacing between flex lines.
start_of_current_line = gap_size / 2;
break;
}
case CSS::AlignContent::SpaceEvenly: {
iterate_lines_backwards = wrap_reverse;
auto leftover_free_space = cross_size_of_flex_container - sum_of_flex_line_cross_sizes;
if (leftover_free_space < 0) {
// If the leftover free-space is negative this value is identical to center.
start_of_current_line = (cross_size_of_flex_container / 2) - (sum_of_flex_line_cross_sizes / 2);
break;
}
gap_size = leftover_free_space / (m_flex_lines.size() + 1);
// The spacing between the first/last lines and the flex container edges is the size of the spacing between flex lines.
start_of_current_line = gap_size;
break;
}
case CSS::AlignContent::Normal:
case CSS::AlignContent::Stretch:
if (wrap_reverse) {
start_of_current_line = cross_size_of_flex_container;
place_items_backwards = true;
} else {
start_of_current_line = 0; start_of_current_line = 0;
}
break;
}
auto leftover_free_space = cross_size_of_flex_container - sum_of_flex_line_cross_sizes; auto place_items = [&](FlexLine& flex_line) {
auto leftover_flex_lines_size = m_flex_lines.size(); if (place_items_backwards) {
if (leftover_free_space >= 0 && leftover_flex_lines_size > 1) { CSSPixels center_of_current_line = start_of_current_line - (flex_line.cross_size / 2);
int gap_count = leftover_flex_lines_size - 1; for (auto& item : flex_line.items) {
gap_size = leftover_free_space / gap_count; item.cross_offset += center_of_current_line;
} }
break; start_of_current_line -= flex_line.cross_size + gap_size;
} // CSS-FLEXBOX-2: Account for gap between flex lines.
case CSS::AlignContent::SpaceAround: { start_of_current_line -= cross_gap();
auto leftover_free_space = cross_size_of_flex_container - sum_of_flex_line_cross_sizes; } else {
if (leftover_free_space < 0) {
// If the leftover free-space is negative this value is identical to center.
start_of_current_line = (cross_size_of_flex_container / 2) - (sum_of_flex_line_cross_sizes / 2);
break;
}
gap_size = leftover_free_space / m_flex_lines.size();
// The spacing between the first/last lines and the flex container edges is half the size of the spacing between flex lines.
start_of_current_line = gap_size / 2;
break;
}
case CSS::AlignContent::SpaceEvenly: {
auto leftover_free_space = cross_size_of_flex_container - sum_of_flex_line_cross_sizes;
if (leftover_free_space < 0) {
// If the leftover free-space is negative this value is identical to center.
start_of_current_line = (cross_size_of_flex_container / 2) - (sum_of_flex_line_cross_sizes / 2);
break;
}
gap_size = leftover_free_space / (m_flex_lines.size() + 1);
// The spacing between the first/last lines and the flex container edges is the size of the spacing between flex lines.
start_of_current_line = gap_size;
break;
}
case CSS::AlignContent::Normal:
case CSS::AlignContent::Stretch:
start_of_current_line = 0;
break;
}
for (auto& flex_line : m_flex_lines) {
CSSPixels center_of_current_line = start_of_current_line + (flex_line.cross_size / 2); CSSPixels center_of_current_line = start_of_current_line + (flex_line.cross_size / 2);
for (auto& item : flex_line.items) { for (auto& item : flex_line.items) {
item.cross_offset += center_of_current_line; item.cross_offset += center_of_current_line;
@ -1612,6 +1654,16 @@ void FlexFormattingContext::align_all_flex_lines()
// CSS-FLEXBOX-2: Account for gap between flex lines. // CSS-FLEXBOX-2: Account for gap between flex lines.
start_of_current_line += cross_gap(); start_of_current_line += cross_gap();
} }
};
if (iterate_lines_backwards) {
for (auto& flex_line : m_flex_lines.in_reverse()) {
place_items(flex_line);
}
} else {
for (auto& flex_line : m_flex_lines) {
place_items(flex_line);
}
} }
} }