Przeglądaj źródła

LibWeb: Parse and compute CSS `quotes` property

Sam Atkins 1 rok temu
rodzic
commit
dc7a52957e

+ 14 - 0
Userland/Libraries/LibWeb/CSS/ComputedValues.h

@@ -38,6 +38,15 @@ struct GridAutoFlow {
     bool dense { false };
 };
 
+struct QuotesData {
+    enum class Type {
+        None,
+        Auto,
+        Specified,
+    } type;
+    Vector<Array<String, 2>> strings {};
+};
+
 class InitialValues {
 public:
     static AspectRatio aspect_ratio() { return AspectRatio { true, {} }; }
@@ -120,6 +129,7 @@ public:
     static CSS::OutlineStyle outline_style() { return CSS::OutlineStyle::None; }
     static CSS::Length outline_width() { return CSS::Length::make_px(3); }
     static CSS::TableLayout table_layout() { return CSS::TableLayout::Auto; }
+    static QuotesData quotes() { return QuotesData { .type = QuotesData::Type::Auto }; }
 
     static CSS::MathShift math_shift() { return CSS::MathShift::Normal; }
     static CSS::MathStyle math_style() { return CSS::MathStyle::Normal; }
@@ -350,6 +360,8 @@ public:
 
     CSS::TableLayout table_layout() const { return m_noninherited.table_layout; }
 
+    CSS::QuotesData quotes() const { return m_inherited.quotes; }
+
     CSS::MathShift math_shift() const { return m_inherited.math_shift; }
     CSS::MathStyle math_style() const { return m_inherited.math_style; }
     int math_depth() const { return m_inherited.math_depth; }
@@ -383,6 +395,7 @@ protected:
         CSS::ListStyleType list_style_type { InitialValues::list_style_type() };
         CSS::ListStylePosition list_style_position { InitialValues::list_style_position() };
         CSS::Visibility visibility { InitialValues::visibility() };
+        CSS::QuotesData quotes { InitialValues::quotes() };
 
         Optional<SVGPaint> fill;
         CSS::FillRule fill_rule { InitialValues::fill_rule() };
@@ -577,6 +590,7 @@ public:
     void set_grid_auto_flow(CSS::GridAutoFlow grid_auto_flow) { m_noninherited.grid_auto_flow = grid_auto_flow; }
     void set_transition_delay(CSS::Time const& transition_delay) { m_noninherited.transition_delay = transition_delay; }
     void set_table_layout(CSS::TableLayout value) { m_noninherited.table_layout = value; }
+    void set_quotes(CSS::QuotesData value) { m_inherited.quotes = value; }
 
     void set_fill(SVGPaint value) { m_inherited.fill = value; }
     void set_stroke(SVGPaint value) { m_inherited.stroke = value; }

+ 33 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -4586,6 +4586,35 @@ RefPtr<StyleValue> Parser::parse_place_self_value(Vector<ComponentValue> const&
     return PlaceItemsStyleValue::create(*maybe_align_self_value, *maybe_justify_self_value);
 }
 
+RefPtr<StyleValue> Parser::parse_quotes_value(Vector<ComponentValue> const& component_values)
+{
+    // https://www.w3.org/TR/css-content-3/#quotes-property
+    // auto | none | [ <string> <string> ]+
+
+    if (component_values.size() == 1) {
+        auto identifier = parse_identifier_value(component_values.first());
+        if (identifier && property_accepts_identifier(PropertyID::Quotes, identifier->to_identifier()))
+            return identifier;
+        return nullptr;
+    }
+
+    // Parse an even number of <string> values.
+    if (component_values.size() % 2 != 0)
+        return nullptr;
+
+    auto tokens = TokenStream { component_values };
+    StyleValueVector string_values;
+    while (tokens.has_next_token()) {
+        auto maybe_string = parse_string_value(tokens.next_token());
+        if (!maybe_string)
+            return nullptr;
+
+        string_values.append(maybe_string.release_nonnull());
+    }
+
+    return StyleValueList::create(move(string_values), StyleValueList::Separator::Space);
+}
+
 RefPtr<StyleValue> Parser::parse_text_decoration_value(Vector<ComponentValue> const& component_values)
 {
     RefPtr<StyleValue> decoration_line;
@@ -5869,6 +5898,10 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue>> Parser::parse_css_value(Property
         if (auto parsed_value = parse_place_self_value(component_values))
             return parsed_value.release_nonnull();
         return ParseError::SyntaxError;
+    case PropertyID::Quotes:
+        if (auto parsed_value = parse_quotes_value(component_values))
+            return parsed_value.release_nonnull();
+        return ParseError::SyntaxError;
     case PropertyID::TextDecoration:
         if (auto parsed_value = parse_text_decoration_value(component_values))
             return parsed_value.release_nonnull();

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

@@ -243,6 +243,7 @@ private:
     RefPtr<StyleValue> parse_place_content_value(Vector<ComponentValue> const&);
     RefPtr<StyleValue> parse_place_items_value(Vector<ComponentValue> const&);
     RefPtr<StyleValue> parse_place_self_value(Vector<ComponentValue> const&);
+    RefPtr<StyleValue> parse_quotes_value(Vector<ComponentValue> const&);
     enum class AllowInsetKeyword {
         No,
         Yes,

+ 11 - 0
Userland/Libraries/LibWeb/CSS/Properties.json

@@ -1836,6 +1836,17 @@
       "position"
     ]
   },
+  "quotes": {
+    "inherited": true,
+    "initial": "auto",
+    "valid-types": [
+      "string"
+    ],
+    "valid-identifiers": [
+      "auto",
+      "none"
+    ]
+  },
   "right": {
     "inherited": false,
     "initial": "auto",

+ 28 - 0
Userland/Libraries/LibWeb/CSS/StyleProperties.cpp

@@ -990,4 +990,32 @@ void StyleProperties::set_math_depth(int math_depth)
     set_property(PropertyID::MathDepth, MathDepthStyleValue::create_integer(IntegerStyleValue::create(math_depth)));
 }
 
+QuotesData StyleProperties::quotes() const
+{
+    auto value = property(CSS::PropertyID::Quotes);
+    if (value->is_identifier()) {
+        switch (value->to_identifier()) {
+        case ValueID::Auto:
+            return QuotesData { .type = QuotesData::Type::Auto };
+        case ValueID::None:
+            return QuotesData { .type = QuotesData::Type::None };
+        default:
+            break;
+        }
+    }
+    if (value->is_value_list()) {
+        auto& value_list = value->as_value_list();
+        QuotesData quotes_data { .type = QuotesData::Type::Specified };
+        VERIFY(value_list.size() % 2 == 0);
+        for (auto i = 0u; i < value_list.size(); i += 2) {
+            quotes_data.strings.empend(
+                value_list.value_at(i, false)->as_string().string_value(),
+                value_list.value_at(i + 1, false)->as_string().string_value());
+        }
+        return quotes_data;
+    }
+
+    return InitialValues::quotes();
+}
+
 }

+ 2 - 0
Userland/Libraries/LibWeb/CSS/StyleProperties.h

@@ -146,6 +146,8 @@ public:
     void set_math_depth(int math_depth);
     int math_depth() const { return m_math_depth; }
 
+    QuotesData quotes() const;
+
     static NonnullRefPtr<Gfx::Font const> font_fallback(bool monospace, bool bold);
 
 private:

+ 1 - 0
Userland/Libraries/LibWeb/Layout/Node.cpp

@@ -806,6 +806,7 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
         computed_values.set_math_style(math_style.value());
 
     computed_values.set_math_depth(computed_style.math_depth());
+    computed_values.set_quotes(computed_style.quotes());
 
     // Update any anonymous children that inherit from this node.
     // FIXME: This is pretty hackish. It would be nicer if they shared the inherited style