1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 22:07:34 +00:00

LibWeb: Generate PseudoClass metadata

The usual to/from-string functions, and metadata about whether the
pseudo-class is a a function or not, and what type of parameter it
takes.
This commit is contained in:
Sam Atkins 2023-08-12 13:27:24 +01:00 committed by Andreas Kling
parent e25b1f76e1
commit f76c614a84
6 changed files with 350 additions and 0 deletions

View file

@ -45,6 +45,15 @@ function (generate_css_implementation)
arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/Properties.json"
)
invoke_generator(
"PseudoClass.cpp"
Lagom::GenerateCSSPseudoClass
"${LIBWEB_INPUT_FOLDER}/CSS/PseudoClasses.json"
"CSS/PseudoClass.h"
"CSS/PseudoClass.cpp"
arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/PseudoClasses.json"
)
invoke_generator(
"TransformFunctions.cpp"
Lagom::GenerateCSSTransformFunctions

View file

@ -5,6 +5,7 @@ lagom_tool(GenerateCSSEnums SOURCES GenerateCSSEnums.cpp LIBS Li
lagom_tool(GenerateCSSMathFunctions SOURCES GenerateCSSMathFunctions.cpp LIBS LibMain)
lagom_tool(GenerateCSSMediaFeatureID SOURCES GenerateCSSMediaFeatureID.cpp LIBS LibMain)
lagom_tool(GenerateCSSPropertyID SOURCES GenerateCSSPropertyID.cpp LIBS LibMain)
lagom_tool(GenerateCSSPseudoClass SOURCES GenerateCSSPseudoClass.cpp LIBS LibMain)
lagom_tool(GenerateCSSTransformFunctions SOURCES GenerateCSSTransformFunctions.cpp LIBS LibMain)
lagom_tool(GenerateCSSValueID SOURCES GenerateCSSValueID.cpp LIBS LibMain)
lagom_tool(GenerateWindowOrWorkerInterfaces SOURCES GenerateWindowOrWorkerInterfaces.cpp LIBS LibMain LibIDL)

View file

@ -0,0 +1,205 @@
/*
* Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "GeneratorUtil.h"
#include <AK/SourceGenerator.h>
#include <LibCore/ArgsParser.h>
#include <LibMain/Main.h>
ErrorOr<void> generate_header_file(JsonObject& pseudo_classes_data, Core::File& file);
ErrorOr<void> generate_implementation_file(JsonObject& pseudo_classes_data, Core::File& file);
ErrorOr<int> serenity_main(Main::Arguments arguments)
{
StringView generated_header_path;
StringView generated_implementation_path;
StringView identifiers_json_path;
Core::ArgsParser args_parser;
args_parser.add_option(generated_header_path, "Path to the PseudoClasses header file to generate", "generated-header-path", 'h', "generated-header-path");
args_parser.add_option(generated_implementation_path, "Path to the PseudoClasses implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
args_parser.add_option(identifiers_json_path, "Path to the JSON file to read from", "json-path", 'j', "json-path");
args_parser.parse(arguments);
auto json = TRY(read_entire_file_as_json(identifiers_json_path));
VERIFY(json.is_object());
auto data = json.as_object();
auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write));
auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write));
TRY(generate_header_file(data, *generated_header_file));
TRY(generate_implementation_file(data, *generated_implementation_file));
return 0;
}
ErrorOr<void> generate_header_file(JsonObject& pseudo_classes_data, Core::File& file)
{
StringBuilder builder;
SourceGenerator generator { builder };
TRY(generator.try_append(R"~~~(
#pragma once
#include <AK/Optional.h>
#include <AK/StringView.h>
namespace Web::CSS {
enum class PseudoClass {
)~~~"));
TRY(pseudo_classes_data.try_for_each_member([&](auto& name, auto&) -> ErrorOr<void> {
auto member_generator = TRY(generator.fork());
TRY(member_generator.set("name:titlecase", TRY(title_casify(name))));
TRY(member_generator.try_appendln(" @name:titlecase@,"));
return {};
}));
TRY(generator.try_append(R"~~~(
};
Optional<PseudoClass> pseudo_class_from_string(StringView);
StringView pseudo_class_name(PseudoClass);
struct PseudoClassMetadata {
enum class ParameterType {
None,
ANPlusB,
ANPlusBOf,
ForgivingSelectorList,
LanguageRanges,
SelectorList,
} parameter_type;
bool is_valid_as_function;
bool is_valid_as_identifier;
};
PseudoClassMetadata pseudo_class_metadata(PseudoClass);
}
)~~~"));
TRY(file.write_until_depleted(generator.as_string_view().bytes()));
return {};
}
ErrorOr<void> generate_implementation_file(JsonObject& pseudo_classes_data, Core::File& file)
{
StringBuilder builder;
SourceGenerator generator { builder };
TRY(generator.try_append(R"~~~(
#include <LibWeb/CSS/PseudoClass.h>
namespace Web::CSS {
Optional<PseudoClass> pseudo_class_from_string(StringView string)
{
)~~~"));
TRY(pseudo_classes_data.try_for_each_member([&](auto& name, auto&) -> ErrorOr<void> {
auto member_generator = TRY(generator.fork());
TRY(member_generator.set("name", TRY(String::from_deprecated_string(name))));
TRY(member_generator.set("name:titlecase", TRY(title_casify(name))));
TRY(member_generator.try_append(R"~~~(
if (string.equals_ignoring_ascii_case("@name@"sv))
return PseudoClass::@name:titlecase@;
)~~~"));
return {};
}));
TRY(generator.try_append(R"~~~(
return {};
}
StringView pseudo_class_name(PseudoClass pseudo_class)
{
switch (pseudo_class) {
)~~~"));
TRY(pseudo_classes_data.try_for_each_member([&](auto& name, auto&) -> ErrorOr<void> {
auto member_generator = TRY(generator.fork());
TRY(member_generator.set("name", TRY(String::from_deprecated_string(name))));
TRY(member_generator.set("name:titlecase", TRY(title_casify(name))));
TRY(member_generator.try_append(R"~~~(
case PseudoClass::@name:titlecase@:
return "@name@"sv;
)~~~"));
return {};
}));
TRY(generator.try_append(R"~~~(
}
VERIFY_NOT_REACHED();
}
PseudoClassMetadata pseudo_class_metadata(PseudoClass pseudo_class)
{
switch (pseudo_class) {
)~~~"));
TRY(pseudo_classes_data.try_for_each_member([&](auto& name, JsonValue const& value) -> ErrorOr<void> {
auto member_generator = TRY(generator.fork());
auto& pseudo_class = value.as_object();
auto argument_string = pseudo_class.get_deprecated_string("argument"sv).value();
bool is_valid_as_identifier = argument_string.is_empty();
bool is_valid_as_function = !argument_string.is_empty();
if (argument_string.ends_with('?')) {
is_valid_as_identifier = true;
argument_string = argument_string.substring(0, argument_string.length() - 1);
}
String parameter_type = "None"_string;
if (is_valid_as_function) {
if (argument_string == "<an+b>"sv) {
parameter_type = "ANPlusB"_string;
} else if (argument_string == "<an+b-of>"sv) {
parameter_type = "ANPlusBOf"_string;
} else if (argument_string == "<forgiving-selector-list>"sv) {
parameter_type = "ForgivingSelectorList"_string;
} else if (argument_string == "<language-ranges>"sv) {
parameter_type = "LanguageRanges"_string;
} else if (argument_string == "<selector-list>"sv) {
parameter_type = "SelectorList"_string;
} else {
warnln("Unrecognized pseudo-class argument type: `{}`", argument_string);
VERIFY_NOT_REACHED();
}
}
TRY(member_generator.set("name:titlecase", TRY(title_casify(name))));
TRY(member_generator.set("parameter_type", parameter_type));
TRY(member_generator.set("is_valid_as_function", is_valid_as_function ? "true"_string : "false"_string));
TRY(member_generator.set("is_valid_as_identifier", is_valid_as_identifier ? "true"_string : "false"_string));
TRY(member_generator.try_append(R"~~~(
case PseudoClass::@name:titlecase@:
return {
.parameter_type = PseudoClassMetadata::ParameterType::@parameter_type@,
.is_valid_as_function = @is_valid_as_function@,
.is_valid_as_identifier = @is_valid_as_identifier@,
};
)~~~"));
return {};
}));
TRY(generator.try_append(R"~~~(
}
VERIFY_NOT_REACHED();
}
}
)~~~"));
TRY(file.write_until_depleted(generator.as_string_view().bytes()));
return {};
}

View file

@ -164,6 +164,23 @@ compiled_action("generate_css_property_id") {
]
}
compiled_action("generate_css_pseudo_class") {
tool = "//Meta/Lagom/Tools/CodeGenerators/LibWeb:GenerateCSSPseudoClass"
inputs = [ "CSS/PseudoClasses.json" ]
outputs = [
"$target_gen_dir/CSS/PseudoClass.h",
"$target_gen_dir/CSS/PseudoClass.cpp",
]
args = [
"-h",
rebase_path(outputs[0], root_build_dir),
"-c",
rebase_path(outputs[1], root_build_dir),
"-j",
rebase_path(inputs[0], root_build_dir),
]
}
compiled_action("generate_css_transform_functions") {
tool =
"//Meta/Lagom/Tools/CodeGenerators/LibWeb:GenerateCSSTransformFunctions"
@ -228,6 +245,7 @@ source_set("all_generated") {
":generate_css_math_functions",
":generate_css_media_feature_id",
":generate_css_property_id",
":generate_css_pseudo_class",
":generate_css_transform_functions",
":generate_css_value_id",
":generate_default_stylesheet_source",

View file

@ -630,6 +630,7 @@ set(GENERATED_SOURCES
CSS/MathFunctions.cpp
CSS/MediaFeatureID.cpp
CSS/PropertyID.cpp
CSS/PseudoClass.cpp
CSS/QuirksModeStyleSheetSource.cpp
CSS/TransformFunctions.cpp
CSS/ValueID.cpp

View file

@ -0,0 +1,116 @@
{
"active": {
"argument": ""
},
"buffering": {
"argument": ""
},
"checked": {
"argument": ""
},
"defined": {
"argument": ""
},
"disabled": {
"argument": ""
},
"empty": {
"argument": ""
},
"enabled": {
"argument": ""
},
"first-child": {
"argument": ""
},
"first-of-type": {
"argument": ""
},
"focus": {
"argument": ""
},
"focus-visible": {
"argument": ""
},
"focus-within": {
"argument": ""
},
"host": {
"argument": "<selector-list>?"
},
"hover": {
"argument": ""
},
"indeterminate": {
"argument": ""
},
"is": {
"argument": "<forgiving-selector-list>"
},
"lang": {
"argument": "<language-ranges>"
},
"last-child": {
"argument": ""
},
"last-of-type": {
"argument": ""
},
"link": {
"argument": ""
},
"muted": {
"argument": ""
},
"not": {
"argument": "<selector-list>"
},
"nth-child": {
"argument": "<an+b-of>"
},
"nth-last-child": {
"argument": "<an+b-of>"
},
"nth-last-of-type": {
"argument": "<an+b>"
},
"nth-of-type": {
"argument": "<an+b>"
},
"only-child": {
"argument": ""
},
"only-of-type": {
"argument": ""
},
"paused": {
"argument": ""
},
"playing": {
"argument": ""
},
"root": {
"argument": ""
},
"scope": {
"argument": ""
},
"seeking": {
"argument": ""
},
"stalled": {
"argument": ""
},
"target": {
"argument": ""
},
"visited": {
"argument": ""
},
"volume-locked": {
"argument": ""
},
"where": {
"argument": "<forgiving-selector-list>"
}
}