Browse Source

LibWeb: Generate property_accepts_value() function :^)

Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:

```css
.foo {
  width: 2em;
  width: orange;
}
```

... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.

Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
Sam Atkins 3 years ago
parent
commit
11d3098f40

+ 103 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/Generate_CSS_PropertyID_cpp.cpp

@@ -252,6 +252,109 @@ bool property_has_quirk(PropertyID property_id, Quirk quirk)
     }
     }
 }
 }
 
 
+bool property_accepts_value(PropertyID property_id, StyleValue& style_value)
+{
+    if (style_value.is_builtin() || style_value.is_custom_property())
+        return true;
+
+    switch (property_id) {
+)~~~");
+
+    properties.for_each_member([&](auto& name, auto& value) {
+        VERIFY(value.is_object());
+        auto& object = value.as_object();
+        bool has_valid_types = object.has("valid-types");
+        auto has_valid_identifiers = object.has("valid-identifiers");
+        if (has_valid_types || has_valid_identifiers) {
+            auto property_generator = generator.fork();
+            property_generator.set("name:titlecase", title_casify(name));
+            property_generator.append(R"~~~(
+    case PropertyID::@name:titlecase@: {
+)~~~");
+
+            if (has_valid_types) {
+                auto valid_types_value = object.get("valid-types");
+                VERIFY(valid_types_value.is_array());
+                auto valid_types = valid_types_value.as_array();
+                if (!valid_types.is_empty()) {
+                    for (auto& type : valid_types.values()) {
+                        VERIFY(type.is_string());
+                        auto type_name = type.as_string();
+                        if (type_name == "color") {
+                            property_generator.append(R"~~~(
+        if (style_value.is_color())
+            return true;
+)~~~");
+                        } else if (type_name == "image") {
+                            property_generator.append(R"~~~(
+        if (style_value.is_image())
+            return true;
+)~~~");
+                        } else if (type_name == "length" || type_name == "percentage") {
+                            // FIXME: Handle lengths and percentages separately
+                            property_generator.append(R"~~~(
+        if (style_value.is_length() || style_value.is_calculated())
+            return true;
+)~~~");
+                        } else if (type_name == "number" || type_name == "integer") {
+                            // FIXME: Handle integers separately
+                            property_generator.append(R"~~~(
+        if (style_value.is_numeric())
+            return true;
+)~~~");
+                        } else if (type_name == "string") {
+                            property_generator.append(R"~~~(
+        if (style_value.is_string())
+            return true;
+)~~~");
+                        } else if (type_name == "url") {
+                            // FIXME: Handle urls!
+                        } else {
+                            warnln("Unrecognized valid-type name: '{}'", type_name);
+                            VERIFY_NOT_REACHED();
+                        }
+                    }
+                }
+            }
+
+            if (has_valid_identifiers) {
+                auto valid_identifiers_value = object.get("valid-identifiers");
+                VERIFY(valid_identifiers_value.is_array());
+                auto valid_identifiers = valid_identifiers_value.as_array();
+                if (!valid_identifiers.is_empty()) {
+                    property_generator.append(R"~~~(
+        switch (style_value.to_identifier()) {
+)~~~");
+                    for (auto& identifier : valid_identifiers.values()) {
+                        VERIFY(identifier.is_string());
+                        auto identifier_generator = generator.fork();
+                        identifier_generator.set("identifier:titlecase", title_casify(identifier.as_string()));
+                        identifier_generator.append(R"~~~(
+        case ValueID::@identifier:titlecase@:
+)~~~");
+                    }
+                    property_generator.append(R"~~~(
+            return true;
+        default:
+            break;
+        }
+)~~~");
+                }
+            }
+
+            generator.append(R"~~~(
+        return false;
+    }
+)~~~");
+        }
+    });
+
+    generator.append(R"~~~(
+    default:
+        return true;
+    }
+}
+
 size_t property_maximum_value_count(PropertyID property_id)
 size_t property_maximum_value_count(PropertyID property_id)
 {
 {
     switch (property_id) {
     switch (property_id) {

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/Generate_CSS_PropertyID_h.cpp

@@ -106,6 +106,7 @@ bool is_inherited_property(PropertyID);
 bool is_pseudo_property(PropertyID);
 bool is_pseudo_property(PropertyID);
 RefPtr<StyleValue> property_initial_value(PropertyID);
 RefPtr<StyleValue> property_initial_value(PropertyID);
 
 
+bool property_accepts_value(PropertyID, StyleValue&);
 size_t property_maximum_value_count(PropertyID);
 size_t property_maximum_value_count(PropertyID);
 
 
 constexpr PropertyID first_property_id = PropertyID::@first_property_id@;
 constexpr PropertyID first_property_id = PropertyID::@first_property_id@;