summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authorMacDue <macdue@dueutil.tech>2022-09-15 08:31:17 +0100
committerSam Atkins <atkinssj@gmail.com>2022-09-16 10:50:48 +0100
commitd1b99282d8851b086b2a5131347a2ec003625c46 (patch)
treebe744fe216450730a06f13ed2c9b67b55db72100 /Userland/Libraries
parent980c92e9b542f3b0044744aabd9a5d94be7c8356 (diff)
downloadserenity-d1b99282d8851b086b2a5131347a2ec003625c46.zip
LibWeb+Meta: Parse the `backdrop-filter` CSS property
Note: The parsing and style value completely ignores the SVG filters part of the spec for now... That's a yak for another day :^)
Diffstat (limited to 'Userland/Libraries')
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp186
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.h1
-rw-r--r--Userland/Libraries/LibWeb/CSS/Properties.json11
3 files changed, 198 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
index 754182d5bb..bb0275054b 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
@@ -4409,6 +4409,188 @@ RefPtr<StyleValue> Parser::parse_content_value(Vector<ComponentValue> const& com
return ContentStyleValue::create(StyleValueList::create(move(content_values), StyleValueList::Separator::Space), move(alt_text));
}
+RefPtr<StyleValue> Parser::parse_filter_value_list_value(Vector<ComponentValue> const& component_values)
+{
+ if (component_values.size() == 1 && component_values.first().is(Token::Type::Ident)) {
+ auto ident = parse_identifier_value(component_values.first());
+ if (ident && ident->to_identifier() == ValueID::None)
+ return ident;
+ }
+
+ TokenStream tokens { component_values };
+
+ // FIXME: <url>s are ignored for now
+ // <filter-value-list> = [ <filter-function> | <url> ]+
+
+ enum class FilterToken {
+ // Color filters:
+ Brightness,
+ Contrast,
+ Grayscale,
+ Invert,
+ Opacity,
+ Saturate,
+ Sepia,
+ // Special filters:
+ Blur,
+ DropShadow,
+ HueRotate
+ };
+
+ auto filter_token_to_operation = [&](auto filter) {
+ VERIFY(to_underlying(filter) < to_underlying(FilterToken::Blur));
+ return static_cast<Filter::Color::Operation>(filter);
+ };
+
+ auto parse_number_percentage = [&](auto& token) -> Optional<NumberPercentage> {
+ if (token.is(Token::Type::Percentage))
+ return NumberPercentage(Percentage(token.token().percentage()));
+ if (token.is(Token::Type::Number))
+ return NumberPercentage(Number(Number::Type::Number, token.token().number_value()));
+ return {};
+ };
+
+ auto parse_filter_function_name = [&](auto name) -> Optional<FilterToken> {
+ if (name.equals_ignoring_case("blur"sv))
+ return FilterToken::Blur;
+ if (name.equals_ignoring_case("brightness"sv))
+ return FilterToken::Brightness;
+ if (name.equals_ignoring_case("contrast"sv))
+ return FilterToken::Contrast;
+ if (name.equals_ignoring_case("drop-shadow"sv))
+ return FilterToken::DropShadow;
+ if (name.equals_ignoring_case("grayscale"sv))
+ return FilterToken::Grayscale;
+ if (name.equals_ignoring_case("hue-rotate"sv))
+ return FilterToken::HueRotate;
+ if (name.equals_ignoring_case("invert"sv))
+ return FilterToken::Invert;
+ if (name.equals_ignoring_case("opacity"sv))
+ return FilterToken::Opacity;
+ if (name.equals_ignoring_case("saturate"sv))
+ return FilterToken::Saturate;
+ if (name.equals_ignoring_case("sepia"sv))
+ return FilterToken::Sepia;
+ return {};
+ };
+
+ auto parse_filter_function = [&](auto filter_token, auto function_values) -> Optional<FilterFunction> {
+ TokenStream tokens { function_values };
+ tokens.skip_whitespace();
+
+ auto if_no_more_tokens_return = [&](auto filter) -> Optional<FilterFunction> {
+ tokens.skip_whitespace();
+ if (tokens.has_next_token())
+ return {};
+ return filter;
+ };
+
+ if (filter_token == FilterToken::Blur) {
+ // blur( <length>? )
+ if (!tokens.has_next_token())
+ return Filter::Blur {};
+ auto blur_radius = parse_length(tokens.next_token());
+ if (!blur_radius.has_value())
+ return {};
+ return if_no_more_tokens_return(Filter::Blur { *blur_radius });
+ } else if (filter_token == FilterToken::DropShadow) {
+ if (!tokens.has_next_token())
+ return {};
+ auto next_token = [&]() -> auto&
+ {
+ auto& token = tokens.next_token();
+ tokens.skip_whitespace();
+ return token;
+ };
+ // drop-shadow( [ <color>? && <length>{2,3} ] )
+ // Note: The following code is a little awkward to allow the color to be before or after the lengths.
+ auto& first_param = next_token();
+ Optional<Length> maybe_radius = {};
+ auto maybe_color = parse_color(first_param);
+ auto x_offset = parse_length(maybe_color.has_value() ? next_token() : first_param);
+ if (!x_offset.has_value() || !tokens.has_next_token()) {
+ return {};
+ }
+ auto y_offset = parse_length(next_token());
+ if (!y_offset.has_value()) {
+ return {};
+ }
+ if (tokens.has_next_token()) {
+ auto& token = next_token();
+ maybe_radius = parse_length(token);
+ if (!maybe_color.has_value() && (!maybe_radius.has_value() || tokens.has_next_token())) {
+ maybe_color = parse_color(!maybe_radius.has_value() ? token : next_token());
+ if (!maybe_color.has_value()) {
+ return {};
+ }
+ } else if (!maybe_radius.has_value()) {
+ return {};
+ }
+ }
+ return if_no_more_tokens_return(Filter::DropShadow { *x_offset, *y_offset, maybe_radius, maybe_color });
+ } else if (filter_token == FilterToken::HueRotate) {
+ // hue-rotate( [ <angle> | <zero> ]? )
+ if (!tokens.has_next_token())
+ return Filter::HueRotate {};
+ auto& token = tokens.next_token();
+ if (token.is(Token::Type::Number)) {
+ // hue-rotate(0)
+ auto number = token.token().number();
+ if (number.is_integer() && number.integer_value() == 0)
+ return if_no_more_tokens_return(Filter::HueRotate { Filter::HueRotate::Zero {} });
+ return {};
+ }
+ if (!token.is(Token::Type::Dimension))
+ return {};
+ float angle_value = token.token().dimension_value();
+ auto angle_unit_name = token.token().dimension_unit();
+ auto angle_unit = Angle::unit_from_name(angle_unit_name);
+ if (!angle_unit.has_value())
+ return {};
+ Angle angle { angle_value, angle_unit.release_value() };
+ return if_no_more_tokens_return(Filter::HueRotate { angle });
+ } else {
+ // Simple filters:
+ // brightness( <number-percentage>? )
+ // contrast( <number-percentage>? )
+ // grayscale( <number-percentage>? )
+ // invert( <number-percentage>? )
+ // opacity( <number-percentage>? )
+ // sepia( <number-percentage>? )
+ // saturate( <number-percentage>? )
+ if (!tokens.has_next_token())
+ return Filter::Color { filter_token_to_operation(filter_token) };
+ auto amount = parse_number_percentage(tokens.next_token());
+ if (!amount.has_value())
+ return {};
+ return if_no_more_tokens_return(Filter::Color { filter_token_to_operation(filter_token), *amount });
+ }
+ };
+
+ Vector<FilterFunction> filter_value_list {};
+
+ while (tokens.has_next_token()) {
+ tokens.skip_whitespace();
+ if (!tokens.has_next_token())
+ break;
+ auto& token = tokens.next_token();
+ if (!token.is_function())
+ return {};
+ auto filter_token = parse_filter_function_name(token.function().name());
+ if (!filter_token.has_value())
+ return {};
+ auto filter_function = parse_filter_function(*filter_token, token.function().values());
+ if (!filter_function.has_value())
+ return {};
+ filter_value_list.append(*filter_function);
+ }
+
+ if (filter_value_list.is_empty())
+ return {};
+
+ return FilterValueListStyleValue::create(move(filter_value_list));
+}
+
RefPtr<StyleValue> Parser::parse_flex_value(Vector<ComponentValue> const& component_values)
{
if (component_values.size() == 1) {
@@ -5492,6 +5674,10 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue>> Parser::parse_css_value(Property
// Special-case property handling
switch (property_id) {
+ case PropertyID::BackdropFilter:
+ if (auto parsed_value = parse_filter_value_list_value(component_values))
+ return parsed_value.release_nonnull();
+ return ParseError::SyntaxError;
case PropertyID::Background:
if (auto parsed_value = parse_background_value(component_values))
return parsed_value.release_nonnull();
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
index 24592381aa..39f9681dd5 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
@@ -340,6 +340,7 @@ private:
RefPtr<StyleValue> parse_comma_separated_value_list(Vector<ComponentValue> const&, ParseFunction);
RefPtr<StyleValue> parse_simple_comma_separated_value_list(Vector<ComponentValue> const&);
+ RefPtr<StyleValue> parse_filter_value_list_value(Vector<ComponentValue> const&);
RefPtr<StyleValue> parse_background_value(Vector<ComponentValue> const&);
RefPtr<StyleValue> parse_single_background_position_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue> parse_single_background_repeat_value(TokenStream<ComponentValue>&);
diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json
index 642916754a..5550db5357 100644
--- a/Userland/Libraries/LibWeb/CSS/Properties.json
+++ b/Userland/Libraries/LibWeb/CSS/Properties.json
@@ -20,6 +20,17 @@
"appearance"
]
},
+ "backdrop-filter": {
+ "affects-layout": false,
+ "inherited": false,
+ "initial": "none",
+ "valid-types": [
+ "filter-value-list"
+ ],
+ "valid-identifiers": [
+ "none"
+ ]
+ },
"background": {
"affects-layout": false,
"inherited": false,