Quellcode durchsuchen

LibWeb: Parse multiple box-shadows :^)

Again, we don't yet render these (we render nothing) but this gets rid
of a decent amount of CSS spam on Discord.
Sam Atkins vor 3 Jahren
Ursprung
Commit
b51f428165

+ 4 - 0
Base/res/html/misc/box-shadow.html

@@ -37,5 +37,9 @@
   <p>box-shadow: 20px 10px 5px magenta</p>
 </div>
 
+<div class="box" style="box-shadow: 20px 10px 5px magenta, cyan -20px -10px 5px, yellow 10px -5px 5px 20px">
+  <p>box-shadow: 20px 10px 5px magenta, cyan -20px -10px 5px, yellow 10px -5px 5px 20px</p>
+</div>
+
 </body>
 </html>

+ 41 - 21
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -3169,7 +3169,19 @@ RefPtr<StyleValue> Parser::parse_box_shadow_value(Vector<StyleComponentValueRule
             return ident;
     }
 
-    // FIXME: Also support multiple comma-separated box-shadows
+    return parse_comma_separated_value_list(component_values, [this](auto& tokens) {
+        return parse_single_box_shadow_value(tokens);
+    });
+}
+
+RefPtr<StyleValue> Parser::parse_single_box_shadow_value(TokenStream<StyleComponentValueRule>& tokens)
+{
+    auto start_position = tokens.position();
+    auto error = [&]() {
+        tokens.rewind_to_position(start_position);
+        return nullptr;
+    };
+
     Optional<Color> color;
     Optional<Length> offset_x;
     Optional<Length> offset_y;
@@ -3177,58 +3189,66 @@ RefPtr<StyleValue> Parser::parse_box_shadow_value(Vector<StyleComponentValueRule
     Optional<Length> spread_distance;
     Optional<BoxShadowPlacement> placement;
 
-    for (size_t i = 0; i < component_values.size(); ++i) {
-        if (auto maybe_color = parse_color(component_values[i]); maybe_color.has_value()) {
+    while (tokens.has_next_token()) {
+        auto& token = tokens.peek_token();
+
+        if (auto maybe_color = parse_color(token); maybe_color.has_value()) {
             if (color.has_value())
-                return nullptr;
+                return error();
             color = maybe_color.release_value();
+            tokens.next_token();
             continue;
         }
 
-        if (auto maybe_offset_x = parse_length(component_values[i]); maybe_offset_x.has_value()) {
+        if (auto maybe_offset_x = parse_length(token); maybe_offset_x.has_value()) {
             // horizontal offset
             if (offset_x.has_value())
-                return nullptr;
+                return error();
             offset_x = maybe_offset_x.release_value();
+            tokens.next_token();
 
             // vertical offset
-            if (++i >= component_values.size())
-                return nullptr;
-            auto maybe_offset_y = parse_length(component_values[i]);
+            if (!tokens.has_next_token())
+                return error();
+            auto maybe_offset_y = parse_length(tokens.peek_token());
             if (!maybe_offset_y.has_value())
-                return nullptr;
+                return error();
             offset_y = maybe_offset_y.release_value();
+            tokens.next_token();
 
             // blur radius (optional)
-            if (i + 1 >= component_values.size())
+            if (!tokens.has_next_token())
                 break;
-            auto maybe_blur_radius = parse_length(component_values[i + 1]);
+            auto maybe_blur_radius = parse_length(tokens.peek_token());
             if (!maybe_blur_radius.has_value())
                 continue;
-            ++i;
             blur_radius = maybe_blur_radius.release_value();
+            tokens.next_token();
 
             // spread distance (optional)
-            if (i + 1 >= component_values.size())
+            if (!tokens.has_next_token())
                 break;
-            auto maybe_spread_distance = parse_length(component_values[i + 1]);
+            auto maybe_spread_distance = parse_length(tokens.peek_token());
             if (!maybe_spread_distance.has_value())
                 continue;
-            ++i;
             spread_distance = maybe_spread_distance.release_value();
+            tokens.next_token();
 
             continue;
         }
 
-        if (component_values[i].is(Token::Type::Ident) && component_values[i].token().ident().equals_ignoring_case("inset"sv)) {
+        if (token.is(Token::Type::Ident) && token.token().ident().equals_ignoring_case("inset"sv)) {
             if (placement.has_value())
-                return nullptr;
+                return error();
             placement = BoxShadowPlacement::Inner;
+            tokens.next_token();
             continue;
         }
 
-        // Unrecognized value
-        return nullptr;
+        if (token.is(Token::Type::Comma))
+            break;
+
+        return error();
     }
 
     // FIXME: If color is absent, default to `currentColor`
@@ -3237,7 +3257,7 @@ RefPtr<StyleValue> Parser::parse_box_shadow_value(Vector<StyleComponentValueRule
 
     // x/y offsets are required
     if (!offset_x.has_value() || !offset_y.has_value())
-        return nullptr;
+        return error();
 
     // Other lengths default to 0
     if (!blur_radius.has_value())

+ 1 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.h

@@ -232,6 +232,7 @@ private:
     RefPtr<StyleValue> parse_border_radius_value(Vector<StyleComponentValueRule> const&);
     RefPtr<StyleValue> parse_border_radius_shorthand_value(Vector<StyleComponentValueRule> const&);
     RefPtr<StyleValue> parse_box_shadow_value(Vector<StyleComponentValueRule> const&);
+    RefPtr<StyleValue> parse_single_box_shadow_value(TokenStream<StyleComponentValueRule>&);
     RefPtr<StyleValue> parse_flex_value(Vector<StyleComponentValueRule> const&);
     RefPtr<StyleValue> parse_flex_flow_value(Vector<StyleComponentValueRule> const&);
     RefPtr<StyleValue> parse_font_value(Vector<StyleComponentValueRule> const&);