From 50239b58aadf7c992c19c78379a17fde9d76cb29 Mon Sep 17 00:00:00 2001 From: Callum Law Date: Mon, 15 Sep 2025 11:40:27 +1200 Subject: [PATCH] LibWeb: Reset `border-image` to initial value when using `border` Also includes associated handling for serialization of the `border` shorthand. --- .../LibWeb/CSS/Parser/PropertyParsing.cpp | 13 +++- Libraries/LibWeb/CSS/Properties.json | 3 +- .../CSS/StyleValues/ShorthandStyleValue.cpp | 32 ++++++++- .../css/set-style-declaration-shorthand.txt | 7 +- .../parsing/border-shorthand.txt | 41 ++++++++++++ .../cssom/border-shorthand-serialization.txt | 7 +- .../wpt-import/css/cssom/shorthand-values.txt | 6 +- .../parsing/border-shorthand.html | 66 +++++++++++++++++++ 8 files changed, 161 insertions(+), 14 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/css/css-backgrounds/parsing/border-shorthand.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/css/css-backgrounds/parsing/border-shorthand.html diff --git a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp index b13ed8d2891..fa0bfbbf32f 100644 --- a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp @@ -1570,6 +1570,7 @@ RefPtr Parser::parse_single_background_size_value(PropertyID p return BackgroundSizeStyleValue::create(x_size.release_value(), y_size.release_value()); } +// https://drafts.csswg.org/css-backgrounds-3/#propdef-border RefPtr Parser::parse_border_value(PropertyID property_id, TokenStream& tokens) { RefPtr border_width; @@ -1639,11 +1640,17 @@ RefPtr Parser::parse_border_value(PropertyID property_id, Toke if (!border_color) border_color = property_initial_value(color_property); - // FIXME: Also reset border-image, in line with the spec: https://www.w3.org/TR/css-backgrounds-3/#border-shorthands transaction.commit(); + + if (first_is_one_of(property_id, PropertyID::BorderBlock, PropertyID::BorderInline)) + return ShorthandStyleValue::create(property_id, + { width_property, style_property, color_property }, + { border_width.release_nonnull(), border_style.release_nonnull(), border_color.release_nonnull() }); + + // The border shorthand also resets border-image to its initial value return ShorthandStyleValue::create(property_id, - { width_property, style_property, color_property }, - { border_width.release_nonnull(), border_style.release_nonnull(), border_color.release_nonnull() }); + { width_property, style_property, color_property, PropertyID::BorderImage }, + { border_width.release_nonnull(), border_style.release_nonnull(), border_color.release_nonnull(), property_initial_value(PropertyID::BorderImage) }); } // https://drafts.csswg.org/css-backgrounds/#border-image diff --git a/Libraries/LibWeb/CSS/Properties.json b/Libraries/LibWeb/CSS/Properties.json index af2d7bb3f6b..85ad76c7b60 100644 --- a/Libraries/LibWeb/CSS/Properties.json +++ b/Libraries/LibWeb/CSS/Properties.json @@ -498,7 +498,8 @@ "longhands": [ "border-width", "border-style", - "border-color" + "border-color", + "border-image" ], "needs-layout-for-getcomputedstyle": true }, diff --git a/Libraries/LibWeb/CSS/StyleValues/ShorthandStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/ShorthandStyleValue.cpp index 2e90b005f80..1f94d065caf 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ShorthandStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/ShorthandStyleValue.cpp @@ -239,17 +239,45 @@ String ShorthandStyleValue::to_string(SerializationMode mode) const return MUST(builder.to_string()); } case PropertyID::Border: { + // `border` only has a reasonable value if border-image is it's initial value (in which case it is omitted) + if (!longhand(PropertyID::BorderImage)->equals(property_initial_value(PropertyID::BorderImage))) + return ""_string; + auto all_longhands_same_value = [](ValueComparingRefPtr const& shorthand) -> bool { auto longhands = shorthand->as_shorthand().values(); return all_of(longhands, [&](auto const& longhand) { return longhand == longhands[0]; }); }; + auto const& border_width = longhand(PropertyID::BorderWidth); + auto const& border_style = longhand(PropertyID::BorderStyle); + auto const& border_color = longhand(PropertyID::BorderColor); + // `border` only has a reasonable value if all four sides are the same. - if (!all_longhands_same_value(longhand(PropertyID::BorderWidth)) || !all_longhands_same_value(longhand(PropertyID::BorderStyle)) || !all_longhands_same_value(longhand(PropertyID::BorderColor))) + if (!all_longhands_same_value(border_width) || !all_longhands_same_value(border_style) || !all_longhands_same_value(border_color)) return ""_string; - return default_to_string(); + StringBuilder builder; + + if (!border_width->equals(property_initial_value(PropertyID::BorderWidth))) + builder.appendff("{}", border_width->to_string(mode)); + + if (!border_style->equals(property_initial_value(PropertyID::BorderStyle))) { + if (!builder.is_empty()) + builder.append(' '); + builder.appendff("{}", border_style->to_string(mode)); + } + + if (!border_color->equals(property_initial_value(PropertyID::BorderColor))) { + if (!builder.is_empty()) + builder.append(' '); + builder.appendff("{}", border_color->to_string(mode)); + } + + if (builder.is_empty()) + return border_width->to_string(mode); + + return builder.to_string_without_validation(); } case PropertyID::BorderImage: { auto source = longhand(PropertyID::BorderImageSource); diff --git a/Tests/LibWeb/Text/expected/css/set-style-declaration-shorthand.txt b/Tests/LibWeb/Text/expected/css/set-style-declaration-shorthand.txt index fdd46993ce3..3c5700113f8 100644 --- a/Tests/LibWeb/Text/expected/css/set-style-declaration-shorthand.txt +++ b/Tests/LibWeb/Text/expected/css/set-style-declaration-shorthand.txt @@ -30,7 +30,7 @@ Setting border: '1px solid red'; becomes... border-style: 'solid' border-color: 'red' border: '1px solid red' - e.style.length: 12 + e.style.length: 17 > [0] border-top-width > [1] border-right-width > [2] border-bottom-width @@ -43,4 +43,9 @@ Setting border: '1px solid red'; becomes... > [9] border-right-color > [10] border-bottom-color > [11] border-left-color + > [12] border-image-source + > [13] border-image-slice + > [14] border-image-width + > [15] border-image-outset + > [16] border-image-repeat diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-backgrounds/parsing/border-shorthand.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-backgrounds/parsing/border-shorthand.txt new file mode 100644 index 00000000000..750bf31704f --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-backgrounds/parsing/border-shorthand.txt @@ -0,0 +1,41 @@ +Harness status: OK + +Found 36 tests + +36 Pass +Pass e.style['border'] = "2px solid color-mix(42deg)" should not set the property value +Pass e.style['border'] = "2px solid color-contrast(42deg)" should not set the property value +Pass e.style['border'] = "5px dotted blue" should set border-bottom-color +Pass e.style['border'] = "5px dotted blue" should set border-bottom-style +Pass e.style['border'] = "5px dotted blue" should set border-bottom-width +Pass e.style['border'] = "5px dotted blue" should set border-image-outset +Pass e.style['border'] = "5px dotted blue" should set border-image-repeat +Pass e.style['border'] = "5px dotted blue" should set border-image-slice +Pass e.style['border'] = "5px dotted blue" should set border-image-source +Pass e.style['border'] = "5px dotted blue" should set border-image-width +Pass e.style['border'] = "5px dotted blue" should set border-left-color +Pass e.style['border'] = "5px dotted blue" should set border-left-style +Pass e.style['border'] = "5px dotted blue" should set border-left-width +Pass e.style['border'] = "5px dotted blue" should set border-right-color +Pass e.style['border'] = "5px dotted blue" should set border-right-style +Pass e.style['border'] = "5px dotted blue" should set border-right-width +Pass e.style['border'] = "5px dotted blue" should set border-top-color +Pass e.style['border'] = "5px dotted blue" should set border-top-style +Pass e.style['border'] = "5px dotted blue" should set border-top-width +Pass e.style['border'] = "5px dotted blue" should not set unrelated longhands +Pass e.style['border-top'] = "thin" should set border-top-color +Pass e.style['border-top'] = "thin" should set border-top-style +Pass e.style['border-top'] = "thin" should set border-top-width +Pass e.style['border-top'] = "thin" should not set unrelated longhands +Pass e.style['border-right'] = "double" should set border-right-color +Pass e.style['border-right'] = "double" should set border-right-style +Pass e.style['border-right'] = "double" should set border-right-width +Pass e.style['border-right'] = "double" should not set unrelated longhands +Pass e.style['border-bottom'] = "green" should set border-bottom-color +Pass e.style['border-bottom'] = "green" should set border-bottom-style +Pass e.style['border-bottom'] = "green" should set border-bottom-width +Pass e.style['border-bottom'] = "green" should not set unrelated longhands +Pass e.style['border-left'] = "1px dotted red" should set border-left-color +Pass e.style['border-left'] = "1px dotted red" should set border-left-style +Pass e.style['border-left'] = "1px dotted red" should set border-left-width +Pass e.style['border-left'] = "1px dotted red" should not set unrelated longhands \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/cssom/border-shorthand-serialization.txt b/Tests/LibWeb/Text/expected/wpt-import/css/cssom/border-shorthand-serialization.txt index 80285c69934..8f07bd8637d 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/cssom/border-shorthand-serialization.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/cssom/border-shorthand-serialization.txt @@ -2,8 +2,7 @@ Harness status: OK Found 3 tests -1 Pass -2 Fail -Fail Declaration with border longhands is not serialized to a border shorthand declaration. -Fail Declaration with border longhands and border-image is not serialized to a border shorthand declaration. +3 Pass +Pass Declaration with border longhands is not serialized to a border shorthand declaration. +Pass Declaration with border longhands and border-image is not serialized to a border shorthand declaration. Pass Border shorthand is serialized correctly if all border-image-* are set to their initial specified values. \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/cssom/shorthand-values.txt b/Tests/LibWeb/Text/expected/wpt-import/css/cssom/shorthand-values.txt index 0ccfa5c34a2..cdccee56f3f 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/cssom/shorthand-values.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/cssom/shorthand-values.txt @@ -2,13 +2,13 @@ Harness status: OK Found 20 tests -14 Pass -6 Fail +15 Pass +5 Fail Pass The serialization of border: 1px; border-top: 1px; should be canonical. Pass The serialization of border: 1px solid red; should be canonical. Pass The serialization of border: 1px red; should be canonical. Pass The serialization of border: red; should be canonical. -Fail The serialization of border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none; should be canonical. +Pass The serialization of border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none; should be canonical. Fail The serialization of border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; should be canonical. Fail The serialization of border-top: 1px; border-right: 2px; border-bottom: 3px; border-left: 4px; should be canonical. Fail The serialization of border: 1px; border-top: 2px; should be canonical. diff --git a/Tests/LibWeb/Text/input/wpt-import/css/css-backgrounds/parsing/border-shorthand.html b/Tests/LibWeb/Text/input/wpt-import/css/css-backgrounds/parsing/border-shorthand.html new file mode 100644 index 00000000000..407a27a9e4f --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/css/css-backgrounds/parsing/border-shorthand.html @@ -0,0 +1,66 @@ + + + + +CSS Backgrounds and Borders Module Level 3: border sets longhands + + + + + + + + + + + +