Ver código fonte

LibWeb/CSS: Preserve whitespace and comments in custom properties

A couple of parts of this:
- Store the source text for Declarations of custom properties.
- Then save that in the UnresolvedStyleValue.
- Serialize UnresolvedStyleValue using the saved source when available -
  that is, for custom properties but not for regular properties that
  include var() or attr().
Sam Atkins 9 meses atrás
pai
commit
bf3e6daedb

+ 10 - 4
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -953,7 +953,13 @@ Optional<Declaration> Parser::consume_a_declaration(TokenStream<T>& input, Neste
     // 8. If decl’s name is a custom property name string, then set decl’s original text to the segment
     //    of the original source text string corresponding to the tokens of decl’s value.
     if (is_a_custom_property_name_string(declaration.name)) {
-        // FIXME: Set the original source text
+        // TODO: If we could reach inside the source string that the TokenStream uses, we could grab this as
+        //       a single substring instead of having to reconstruct it.
+        StringBuilder original_text;
+        for (auto const& value : declaration.value) {
+            original_text.append(value.original_source_text());
+        }
+        declaration.original_text = original_text.to_string_without_validation();
     }
     //    Otherwise, if decl’s value contains a top-level simple block with an associated token of <{-token>,
     //    and also contains any other non-<whitespace-token> value, return nothing.
@@ -1738,7 +1744,7 @@ Optional<StyleProperty> Parser::convert_to_style_property(Declaration const& dec
     }
 
     auto value_token_stream = TokenStream(declaration.value);
-    auto value = parse_css_value(property_id.value(), value_token_stream);
+    auto value = parse_css_value(property_id.value(), value_token_stream, declaration.original_text);
     if (value.is_error()) {
         if (value.error() == ParseError::SyntaxError) {
             dbgln_if(CSS_PARSER_DEBUG, "Unable to parse value for CSS property '{}'.", property_name);
@@ -7636,7 +7642,7 @@ bool block_contains_var_or_attr(SimpleBlock const& block)
     return false;
 }
 
-Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue>> Parser::parse_css_value(PropertyID property_id, TokenStream<ComponentValue>& unprocessed_tokens)
+Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue>> Parser::parse_css_value(PropertyID property_id, TokenStream<ComponentValue>& unprocessed_tokens, Optional<String> original_source_text)
 {
     m_context.set_current_property_id(property_id);
     Vector<ComponentValue> component_values;
@@ -7670,7 +7676,7 @@ Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue>> Parser::parse_css_value(Prope
     }
 
     if (property_id == PropertyID::Custom || contains_var_or_attr)
-        return UnresolvedStyleValue::create(move(component_values), contains_var_or_attr);
+        return UnresolvedStyleValue::create(move(component_values), contains_var_or_attr, original_source_text);
 
     if (component_values.is_empty())
         return ParseError::SyntaxError;

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

@@ -229,7 +229,7 @@ private:
     RefPtr<CSSStyleValue> parse_conic_gradient_function(TokenStream<ComponentValue>&);
     RefPtr<CSSStyleValue> parse_radial_gradient_function(TokenStream<ComponentValue>&);
 
-    ParseErrorOr<NonnullRefPtr<CSSStyleValue>> parse_css_value(PropertyID, TokenStream<ComponentValue>&);
+    ParseErrorOr<NonnullRefPtr<CSSStyleValue>> parse_css_value(PropertyID, TokenStream<ComponentValue>&, Optional<String> original_source_text = {});
     RefPtr<CSSStyleValue> parse_css_value_for_property(PropertyID, TokenStream<ComponentValue>&);
     struct PropertyAndValue {
         PropertyID property;

+ 4 - 1
Userland/Libraries/LibWeb/CSS/StyleValues/UnresolvedStyleValue.cpp

@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
  * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
- * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
+ * Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
  * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
  *
  * SPDX-License-Identifier: BSD-2-Clause
@@ -14,6 +14,9 @@ namespace Web::CSS {
 
 String UnresolvedStyleValue::to_string() const
 {
+    if (m_original_source_text.has_value())
+        return *m_original_source_text;
+
     StringBuilder builder;
     for (auto& value : m_values)
         builder.append(value.to_string());

+ 5 - 3
Userland/Libraries/LibWeb/CSS/StyleValues/UnresolvedStyleValue.h

@@ -17,9 +17,9 @@ namespace Web::CSS {
 
 class UnresolvedStyleValue final : public CSSStyleValue {
 public:
-    static ValueComparingNonnullRefPtr<UnresolvedStyleValue> create(Vector<Parser::ComponentValue>&& values, bool contains_var_or_attr)
+    static ValueComparingNonnullRefPtr<UnresolvedStyleValue> create(Vector<Parser::ComponentValue>&& values, bool contains_var_or_attr, Optional<String> original_source_text)
     {
-        return adopt_ref(*new (nothrow) UnresolvedStyleValue(move(values), contains_var_or_attr));
+        return adopt_ref(*new (nothrow) UnresolvedStyleValue(move(values), contains_var_or_attr, move(original_source_text)));
     }
     virtual ~UnresolvedStyleValue() override = default;
 
@@ -31,15 +31,17 @@ public:
     virtual bool equals(CSSStyleValue const& other) const override;
 
 private:
-    UnresolvedStyleValue(Vector<Parser::ComponentValue>&& values, bool contains_var_or_attr)
+    UnresolvedStyleValue(Vector<Parser::ComponentValue>&& values, bool contains_var_or_attr, Optional<String> original_source_text)
         : CSSStyleValue(Type::Unresolved)
         , m_values(move(values))
         , m_contains_var_or_attr(contains_var_or_attr)
+        , m_original_source_text(move(original_source_text))
     {
     }
 
     Vector<Parser::ComponentValue> m_values;
     bool m_contains_var_or_attr { false };
+    Optional<String> m_original_source_text;
 };
 
 }