LibWeb: Ensure the shortest serialization is used for border-radius

This implementation also fixes an issue where the individual components
of the `border-radius` shorthand were always assumed to be of type
`BorderRadiusStyleValue`, which could lead to a crash when CSS-wide
keywords were used.
This commit is contained in:
Tim Ledbetter 2025-03-18 12:49:16 +00:00 committed by Jelle Raaijmakers
parent 040dca0223
commit 85728b297f
Notes: github-actions[bot] 2025-03-18 20:56:13 +00:00
4 changed files with 119 additions and 18 deletions

View file

@ -134,20 +134,50 @@ String ShorthandStyleValue::to_string(SerializationMode mode) const
return MUST(builder.to_string());
}
case PropertyID::BorderRadius: {
auto& top_left = longhand(PropertyID::BorderTopLeftRadius)->as_border_radius();
auto& top_right = longhand(PropertyID::BorderTopRightRadius)->as_border_radius();
auto& bottom_right = longhand(PropertyID::BorderBottomRightRadius)->as_border_radius();
auto& bottom_left = longhand(PropertyID::BorderBottomLeftRadius)->as_border_radius();
auto top_left = longhand(PropertyID::BorderTopLeftRadius);
auto top_right = longhand(PropertyID::BorderTopRightRadius);
auto bottom_right = longhand(PropertyID::BorderBottomRightRadius);
auto bottom_left = longhand(PropertyID::BorderBottomLeftRadius);
return MUST(String::formatted("{} {} {} {} / {} {} {} {}",
top_left.horizontal_radius().to_string(),
top_right.horizontal_radius().to_string(),
bottom_right.horizontal_radius().to_string(),
bottom_left.horizontal_radius().to_string(),
top_left.vertical_radius().to_string(),
top_right.vertical_radius().to_string(),
bottom_right.vertical_radius().to_string(),
bottom_left.vertical_radius().to_string()));
auto horizontal_radius = [&](auto& style_value) -> String {
if (style_value->is_border_radius())
return style_value->as_border_radius().horizontal_radius().to_string();
return style_value->to_string(mode);
};
auto top_left_horizontal_string = horizontal_radius(top_left);
auto top_right_horizontal_string = horizontal_radius(top_right);
auto bottom_right_horizontal_string = horizontal_radius(bottom_right);
auto bottom_left_horizontal_string = horizontal_radius(bottom_left);
auto vertical_radius = [&](auto& style_value) -> String {
if (style_value->is_border_radius())
return style_value->as_border_radius().vertical_radius().to_string();
return style_value->to_string(mode);
};
auto top_left_vertical_string = vertical_radius(top_left);
auto top_right_vertical_string = vertical_radius(top_right);
auto bottom_right_vertical_string = vertical_radius(bottom_right);
auto bottom_left_vertical_string = vertical_radius(bottom_left);
auto serialize_radius = [](auto top_left, auto const& top_right, auto const& bottom_right, auto const& bottom_left) -> String {
if (first_is_equal_to_all_of(top_left, top_right, bottom_right, bottom_left))
return top_left;
if (top_left == bottom_right && top_right == bottom_left)
return MUST(String::formatted("{} {}", top_left, top_right));
if (top_right == bottom_left)
return MUST(String::formatted("{} {} {}", top_left, top_right, bottom_right));
return MUST(String::formatted("{} {} {} {}", top_left, top_right, bottom_right, bottom_left));
};
auto first_radius_serialization = serialize_radius(move(top_left_horizontal_string), top_right_horizontal_string, bottom_right_horizontal_string, bottom_left_horizontal_string);
auto second_radius_serialization = serialize_radius(move(top_left_vertical_string), top_right_vertical_string, bottom_right_vertical_string, bottom_left_vertical_string);
if (first_radius_serialization == second_radius_serialization)
return first_radius_serialization;
return MUST(String::formatted("{} / {}", first_radius_serialization, second_radius_serialization));
}
case PropertyID::Columns: {
auto column_width = longhand(PropertyID::ColumnWidth)->to_string(mode);

View file

@ -57,9 +57,9 @@ All supported properties and their default values exposed from CSSStyleDeclarati
'WebkitBorderBottomRightRadius': '0px'
'webkitBorderBottomRightRadius': '0px'
'-webkit-border-bottom-right-radius': '0px'
'WebkitBorderRadius': '0px 0px 0px 0px / 0px 0px 0px 0px'
'webkitBorderRadius': '0px 0px 0px 0px / 0px 0px 0px 0px'
'-webkit-border-radius': '0px 0px 0px 0px / 0px 0px 0px 0px'
'WebkitBorderRadius': '0px'
'webkitBorderRadius': '0px'
'-webkit-border-radius': '0px'
'WebkitBorderTopLeftRadius': '0px'
'webkitBorderTopLeftRadius': '0px'
'-webkit-border-top-left-radius': '0px'
@ -242,8 +242,8 @@ All supported properties and their default values exposed from CSSStyleDeclarati
'border-left-style': 'none'
'borderLeftWidth': 'medium'
'border-left-width': 'medium'
'borderRadius': '0px 0px 0px 0px / 0px 0px 0px 0px'
'border-radius': '0px 0px 0px 0px / 0px 0px 0px 0px'
'borderRadius': '0px'
'border-radius': '0px'
'borderRight': 'medium none rgb(0, 0, 0)'
'border-right': 'medium none rgb(0, 0, 0)'
'borderRightColor': 'rgb(0, 0, 0)'

View file

@ -0,0 +1,29 @@
Harness status: OK
Found 23 tests
21 Pass
2 Fail
Pass e.style['border-radius'] = "initial" should set the property value
Pass e.style['border-radius'] = "inherit" should set the property value
Pass e.style['border-radius'] = "unset" should set the property value
Pass e.style['border-radius'] = "revert" should set the property value
Pass e.style['border-radius'] = "1px" should set the property value
Pass e.style['border-radius'] = "1px 5%" should set the property value
Pass e.style['border-radius'] = "1px 2% 3px" should set the property value
Pass e.style['border-radius'] = "1px 2% 3px 4%" should set the property value
Pass e.style['border-radius'] = "1px / 2px" should set the property value
Pass e.style['border-radius'] = "5em / 1px 2% 3px 4%" should set the property value
Pass e.style['border-radius'] = "1px 2% / 3px 4px" should set the property value
Pass e.style['border-radius'] = "1px 2px 3em / 1px 2px 3%" should set the property value
Pass e.style['border-radius'] = "1px 2% / 2px 3em 4px 5em" should set the property value
Pass e.style['border-radius'] = "1px 2% 3px 4% / 5em" should set the property value
Pass e.style['border-radius'] = "1px 1px 1px 2% / 1px 2% 1px 2%" should set the property value
Pass e.style['border-radius'] = "1px 1px 1px 1px / 1px 1px 2% 1px" should set the property value
Pass e.style['border-radius'] = "1px 1px 2% 2%" should set the property value
Pass e.style['border-radius'] = "1px 2% 1px 1px" should set the property value
Pass e.style['border-radius'] = "1px 2% 2% 2% / 1px 2% 3px 2%" should set the property value
Pass e.style['border-top-left-radius'] = "10px" should set the property value
Pass e.style['border-top-right-radius'] = "20%" should set the property value
Fail e.style['border-bottom-right-radius'] = "30px 40%" should set the property value
Fail e.style['border-bottom-left-radius'] = "50% 60px" should set the property value

View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Backgrounds and Borders Module Level 3: parsing border-radius with valid values</title>
<link rel="help" href="https://drafts.csswg.org/css-backgrounds/#border-radius">
<meta name="assert" content="border-radius supports CSS Wide keywords and the full grammar '<length-percentage>{1,4} [ / <length-percentage>{1,4} ]?'.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_valid_value("border-radius", "initial");
test_valid_value("border-radius", "inherit");
test_valid_value("border-radius", "unset");
test_valid_value("border-radius", "revert");
test_valid_value("border-radius", "1px");
test_valid_value("border-radius", "1px 5%");
test_valid_value("border-radius", "1px 2% 3px");
test_valid_value("border-radius", "1px 2% 3px 4%");
test_valid_value("border-radius", "1px / 2px");
test_valid_value("border-radius", "5em / 1px 2% 3px 4%");
test_valid_value("border-radius", "1px 2% / 3px 4px");
test_valid_value("border-radius", "1px 2px 3em / 1px 2px 3%");
test_valid_value("border-radius", "1px 2% / 2px 3em 4px 5em");
test_valid_value("border-radius", "1px 2% 3px 4% / 5em");
test_valid_value("border-radius", "1px 1px 1px 2% / 1px 2% 1px 2%", "1px 1px 1px 2% / 1px 2%");
test_valid_value("border-radius", "1px 1px 1px 1px / 1px 1px 2% 1px", "1px / 1px 1px 2%");
test_valid_value("border-radius", "1px 1px 2% 2%");
test_valid_value("border-radius", "1px 2% 1px 1px");
test_valid_value("border-radius", "1px 2% 2% 2% / 1px 2% 3px 2%", "1px 2% 2% / 1px 2% 3px");
test_valid_value("border-top-left-radius", "10px");
test_valid_value("border-top-right-radius", "20%");
test_valid_value("border-bottom-right-radius", "30px 40%");
test_valid_value("border-bottom-left-radius", "50% 60px");
</script>
</body>
</html>