LibWeb: Disallow spread distance value when parsing text-shadow

`text-shadow` does not support setting a value for spread distance
unlike `box-shadow`.
This commit is contained in:
Callum Law 2025-09-15 16:15:59 +12:00 committed by Sam Atkins
parent 5d8a859457
commit 1ac7b47764
7 changed files with 23 additions and 27 deletions

View File

@ -32,6 +32,7 @@
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
#include <LibWeb/CSS/StyleValues/BasicShapeStyleValue.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
#include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValue.h>
#include <LibWeb/CSS/Supports.h>
#include <LibWeb/CSS/URL.h>
@ -464,12 +465,8 @@ private:
RefPtr<StyleValue const> parse_single_repeat_style_value(PropertyID, TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_scrollbar_color_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_scrollbar_gutter_value(TokenStream<ComponentValue>&);
enum class AllowInsetKeyword {
No,
Yes,
};
RefPtr<StyleValue const> parse_shadow_value(TokenStream<ComponentValue>&, AllowInsetKeyword);
RefPtr<StyleValue const> parse_single_shadow_value(TokenStream<ComponentValue>&, AllowInsetKeyword);
RefPtr<StyleValue const> parse_shadow_value(TokenStream<ComponentValue>&, ShadowStyleValue::ShadowType);
RefPtr<StyleValue const> parse_single_shadow_value(TokenStream<ComponentValue>&, ShadowStyleValue::ShadowType);
RefPtr<StyleValue const> parse_text_decoration_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_text_decoration_line_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_text_underline_position_value(TokenStream<ComponentValue>&);

View File

@ -510,7 +510,7 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_css_value(Pr
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::BoxShadow:
if (auto parsed_value = parse_shadow_value(tokens, AllowInsetKeyword::Yes); parsed_value && !tokens.has_next_token())
if (auto parsed_value = parse_shadow_value(tokens, ShadowStyleValue::ShadowType::Normal); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::ColorScheme:
@ -747,7 +747,7 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_css_value(Pr
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::TextShadow:
if (auto parsed_value = parse_shadow_value(tokens, AllowInsetKeyword::No); parsed_value && !tokens.has_next_token())
if (auto parsed_value = parse_shadow_value(tokens, ShadowStyleValue::ShadowType::Text); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::TextUnderlinePosition:
@ -2062,18 +2062,18 @@ RefPtr<StyleValue const> Parser::parse_columns_value(TokenStream<ComponentValue>
{ column_count.release_nonnull(), column_width.release_nonnull(), column_height.release_nonnull() });
}
RefPtr<StyleValue const> Parser::parse_shadow_value(TokenStream<ComponentValue>& tokens, AllowInsetKeyword allow_inset_keyword)
RefPtr<StyleValue const> Parser::parse_shadow_value(TokenStream<ComponentValue>& tokens, ShadowStyleValue::ShadowType shadow_type)
{
// "none"
if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
return none;
return parse_comma_separated_value_list(tokens, [this, allow_inset_keyword](auto& tokens) {
return parse_single_shadow_value(tokens, allow_inset_keyword);
return parse_comma_separated_value_list(tokens, [this, shadow_type](auto& tokens) {
return parse_single_shadow_value(tokens, shadow_type);
});
}
RefPtr<StyleValue const> Parser::parse_single_shadow_value(TokenStream<ComponentValue>& tokens, AllowInsetKeyword allow_inset_keyword)
RefPtr<StyleValue const> Parser::parse_single_shadow_value(TokenStream<ComponentValue>& tokens, ShadowStyleValue::ShadowType shadow_type)
{
auto transaction = tokens.begin_transaction();
@ -2136,13 +2136,17 @@ RefPtr<StyleValue const> Parser::parse_single_shadow_value(TokenStream<Component
auto maybe_spread_distance = possibly_dynamic_length(tokens.next_token());
if (!maybe_spread_distance)
continue;
if (shadow_type == ShadowStyleValue::ShadowType::Text)
return nullptr;
spread_distance = maybe_spread_distance;
tokens.discard_a_token();
continue;
}
if (allow_inset_keyword == AllowInsetKeyword::Yes && token.is_ident("inset"sv)) {
if (shadow_type == ShadowStyleValue::ShadowType::Normal && token.is_ident("inset"sv)) {
if (placement.has_value())
return nullptr;
placement = ShadowPlacement::Inner;

View File

@ -22,6 +22,13 @@ enum class ShadowPlacement {
class ShadowStyleValue final : public StyleValueWithDefaultOperators<ShadowStyleValue> {
public:
enum class ShadowType : u8 {
// none | <shadow>#
Normal,
// none | [ <color>? && <length>{2,3} ]#
Text
};
static ValueComparingNonnullRefPtr<ShadowStyleValue const> create(
ValueComparingRefPtr<StyleValue const> color,
ValueComparingNonnullRefPtr<StyleValue const> offset_x,

View File

@ -83,7 +83,6 @@ void paint_text_shadow(DisplayListRecordingContext& context, PaintableFragment c
int blur_radius = context.rounded_device_pixels(layer.blur_radius).value();
// Space around the painted text to allow it to blur.
// FIXME: Include spread in this once we use that.
int margin = blur_radius * 2;
Gfx::IntRect text_rect {
margin, margin,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -35,15 +35,5 @@
<p>text-shadow: 4px 4px blue, 8px 8px green, 12px 12px yellow</p>
</div>
<h2>Spread</h2>
<div class="box" style="text-shadow: 0 0 0 5px magenta">
<p>text-shadow: 0 0 0 5px magenta</p>
</div>
<h2>Spread and blur</h2>
<div class="box" style="text-shadow: 0 0 10px 5px magenta">
<p>text-shadow: 0 0 10px 5px magenta</p>
</div>
</body>
</html>

View File

@ -2,12 +2,11 @@ Harness status: OK
Found 8 tests
7 Pass
1 Fail
8 Pass
Pass e.style['text-shadow'] = "auto" should not set the property value
Pass e.style['text-shadow'] = "10px 20px -30px" should not set the property value
Pass e.style['text-shadow'] = "10px" should not set the property value
Fail e.style['text-shadow'] = "10px 20px 30px 40px" should not set the property value
Pass e.style['text-shadow'] = "10px 20px 30px 40px" should not set the property value
Pass e.style['text-shadow'] = "red 10px 20px blue" should not set the property value
Pass e.style['text-shadow'] = "10% 20px" should not set the property value
Pass e.style['text-shadow'] = "10px 20% 30px" should not set the property value