浏览代码

Meta: Generate functions for validating media-query values

These work differently from how we validate StyleValues. There, we parse
a StyleValue from the CSS, and then see if it is allowed in the
property. That causes problems when the syntax is ambiguous - for
example, `0` can be a number or a Length.

Here instead, we ask what kinds of value are allowed for a
media-feature, and then only attempt to parse those kinds of value.
This makes the ambiguity problem go away. :^)

Each media-feature in the spec only accepts one type of value, and/or
some identifiers. This makes the switch statements for the type a bit
excessive, but the spec does not *require* that only one type is
allowed, so this is more future-proof.
Sam Atkins 3 年之前
父节点
当前提交
7ce8a91341

+ 134 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/Generate_CSS_MediaFeatureID_cpp.cpp

@@ -84,6 +84,140 @@ bool media_feature_type_is_range(MediaFeatureID media_feature_id)
     VERIFY_NOT_REACHED();
 }
 
+bool media_feature_accepts_type(MediaFeatureID media_feature_id, MediaFeatureValueType value_type)
+{
+    switch (media_feature_id) {)~~~");
+
+    json.as_object().for_each_member([&](auto& name, auto& member) {
+        VERIFY(member.is_object());
+        auto& feature = member.as_object();
+
+        auto member_generator = generator.fork();
+        member_generator.set("name:titlecase", title_casify(name));
+        member_generator.append(R"~~~(
+    case MediaFeatureID::@name:titlecase@:)~~~");
+
+        bool have_output_value_type_switch = false;
+        if (feature.has("values")) {
+            auto append_value_type_switch_if_needed = [&]() {
+                if (!have_output_value_type_switch) {
+                    member_generator.append(R"~~~(
+        switch (value_type) {)~~~");
+                }
+                have_output_value_type_switch = true;
+            };
+            auto& values = feature.get("values");
+            VERIFY(values.is_array());
+            auto& values_array = values.as_array();
+            for (auto& type : values_array.values()) {
+                VERIFY(type.is_string());
+                auto type_name = type.as_string();
+                // Skip identifiers.
+                if (type_name[0] != '<')
+                    continue;
+                if (type_name == "<mq-boolean>") {
+                    append_value_type_switch_if_needed();
+                    member_generator.append(R"~~~(
+        case MediaFeatureValueType::Boolean:
+            return true;)~~~");
+                } else if (type_name == "<integer>") {
+                    append_value_type_switch_if_needed();
+                    member_generator.append(R"~~~(
+        case MediaFeatureValueType::Integer:
+            return true;)~~~");
+                } else if (type_name == "<length>") {
+                    append_value_type_switch_if_needed();
+                    member_generator.append(R"~~~(
+        case MediaFeatureValueType::Length:
+            return true;)~~~");
+                } else if (type_name == "<ratio>") {
+                    append_value_type_switch_if_needed();
+                    member_generator.append(R"~~~(
+        case MediaFeatureValueType::Ratio:
+            return true;)~~~");
+                } else if (type_name == "<resolution>") {
+                    append_value_type_switch_if_needed();
+                    member_generator.append(R"~~~(
+        case MediaFeatureValueType::Resolution:
+            return true;)~~~");
+                } else {
+                    warnln("Unrecognized media-feature value type: `{}`", type_name);
+                    VERIFY_NOT_REACHED();
+                }
+            }
+        }
+        if (have_output_value_type_switch) {
+            member_generator.append(R"~~~(
+        default:
+            return false;
+        })~~~");
+        } else {
+            member_generator.append(R"~~~(
+        return false;)~~~");
+        }
+    });
+
+    generator.append(R"~~~(
+    }
+    VERIFY_NOT_REACHED();
+}
+
+bool media_feature_accepts_identifier(MediaFeatureID media_feature_id, ValueID identifier)
+{
+    switch (media_feature_id) {)~~~");
+
+    json.as_object().for_each_member([&](auto& name, auto& member) {
+        VERIFY(member.is_object());
+        auto& feature = member.as_object();
+
+        auto member_generator = generator.fork();
+        member_generator.set("name:titlecase", title_casify(name));
+        member_generator.append(R"~~~(
+    case MediaFeatureID::@name:titlecase@:)~~~");
+
+        bool have_output_identifier_switch = false;
+        if (feature.has("values")) {
+            auto append_identifier_switch_if_needed = [&]() {
+                if (!have_output_identifier_switch) {
+                    member_generator.append(R"~~~(
+        switch (identifier) {)~~~");
+                }
+                have_output_identifier_switch = true;
+            };
+            auto& values = feature.get("values");
+            VERIFY(values.is_array());
+            auto& values_array = values.as_array();
+            for (auto& identifier : values_array.values()) {
+                VERIFY(identifier.is_string());
+                auto identifier_name = identifier.as_string();
+                // Skip types.
+                if (identifier_name[0] == '<')
+                    continue;
+                append_identifier_switch_if_needed();
+
+                auto ident_generator = member_generator.fork();
+                ident_generator.set("identifier:titlecase", title_casify(identifier_name));
+                ident_generator.append(R"~~~(
+        case ValueID::@identifier:titlecase@:
+            return true;)~~~");
+            }
+        }
+        if (have_output_identifier_switch) {
+            member_generator.append(R"~~~(
+        default:
+            return false;
+        })~~~");
+        } else {
+            member_generator.append(R"~~~(
+        return false;)~~~");
+        }
+    });
+
+    generator.append(R"~~~(
+    }
+    VERIFY_NOT_REACHED();
+}
+
 }
 )~~~");
 

+ 11 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/Generate_CSS_MediaFeatureID_h.cpp

@@ -25,9 +25,18 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
 
 #include <AK/StringView.h>
 #include <AK/Traits.h>
+#include <LibWeb/CSS/ValueID.h>
 
 namespace Web::CSS {
 
+enum class MediaFeatureValueType {
+    Boolean,
+    Integer,
+    Length,
+    Ratio,
+    Resolution,
+};
+
 enum class MediaFeatureID {)~~~");
 
     json.as_object().for_each_member([&](auto& name, auto&) {
@@ -44,6 +53,8 @@ Optional<MediaFeatureID> media_feature_id_from_string(StringView);
 char const* string_from_media_feature_id(MediaFeatureID);
 
 bool media_feature_type_is_range(MediaFeatureID);
+bool media_feature_accepts_type(MediaFeatureID, MediaFeatureValueType);
+bool media_feature_accepts_identifier(MediaFeatureID, ValueID);
 
 }
 )~~~");