mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-05 15:49:11 +00:00
LibWeb: Generate pseudo-element code from JSON
Initially, this generates the enum and to/from-string functions. The JSON itself contains more data than that, but it's unused for now.
This commit is contained in:
parent
0ed2e71801
commit
ffa1dba96a
Notes:
github-actions[bot]
2025-03-24 09:51:30 +00:00
Author: https://github.com/AtkinsSJ
Commit: ffa1dba96a
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4021
14 changed files with 244 additions and 106 deletions
|
@ -28,7 +28,7 @@ WebIDL::ExceptionOr<Optional<CSS::Selector::PseudoElementSelector>> pseudo_eleme
|
||||||
// 3. If value is one of the legacy Selectors Level 2 single-colon selectors (':before', ':after', ':first-letter', or ':first-line'),
|
// 3. If value is one of the legacy Selectors Level 2 single-colon selectors (':before', ':after', ':first-letter', or ':first-line'),
|
||||||
// then return the equivalent two-colon selector (e.g. '::before').
|
// then return the equivalent two-colon selector (e.g. '::before').
|
||||||
if (value.has_value() && value->is_one_of(":before", ":after", ":first-letter", ":first-line")) {
|
if (value.has_value() && value->is_one_of(":before", ":after", ":first-letter", ":first-line")) {
|
||||||
return CSS::Selector::PseudoElementSelector::from_string(MUST(value->substring_from_byte_offset(1)));
|
return CSS::pseudo_element_from_string(MUST(value->substring_from_byte_offset(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Otherwise, return value.
|
// 4. Otherwise, return value.
|
||||||
|
|
|
@ -956,6 +956,7 @@ set(GENERATED_SOURCES
|
||||||
CSS/MediaFeatureID.cpp
|
CSS/MediaFeatureID.cpp
|
||||||
CSS/PropertyID.cpp
|
CSS/PropertyID.cpp
|
||||||
CSS/PseudoClass.cpp
|
CSS/PseudoClass.cpp
|
||||||
|
CSS/PseudoElement.cpp
|
||||||
CSS/QuirksModeStyleSheetSource.cpp
|
CSS/QuirksModeStyleSheetSource.cpp
|
||||||
CSS/TransformFunctions.cpp
|
CSS/TransformFunctions.cpp
|
||||||
MathML/MathMLStyleSheetSource.cpp
|
MathML/MathMLStyleSheetSource.cpp
|
||||||
|
|
|
@ -418,15 +418,15 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
||||||
|
|
||||||
auto pseudo_name = name_token.token().ident();
|
auto pseudo_name = name_token.token().ident();
|
||||||
|
|
||||||
if (auto pseudo_element = Selector::PseudoElementSelector::from_string(pseudo_name); pseudo_element.has_value()) {
|
if (auto pseudo_element = pseudo_element_from_string(pseudo_name); pseudo_element.has_value()) {
|
||||||
// :has() is fussy about pseudo-elements inside it
|
// :has() is fussy about pseudo-elements inside it
|
||||||
if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(pseudo_element->type())) {
|
if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(*pseudo_element)) {
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Selector::SimpleSelector {
|
return Selector::SimpleSelector {
|
||||||
.type = Selector::SimpleSelector::Type::PseudoElement,
|
.type = Selector::SimpleSelector::Type::PseudoElement,
|
||||||
.value = pseudo_element.release_value()
|
.value = Selector::PseudoElementSelector { pseudo_element.release_value() }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,20 +481,20 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
||||||
|
|
||||||
// Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
|
// Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
|
||||||
// https://www.w3.org/TR/selectors/#pseudo-element-syntax
|
// https://www.w3.org/TR/selectors/#pseudo-element-syntax
|
||||||
if (auto pseudo_element = Selector::PseudoElementSelector::from_string(pseudo_name); pseudo_element.has_value()) {
|
if (auto pseudo_element = pseudo_element_from_string(pseudo_name); pseudo_element.has_value()) {
|
||||||
switch (pseudo_element.value().type()) {
|
switch (pseudo_element.value()) {
|
||||||
case PseudoElement::After:
|
case PseudoElement::After:
|
||||||
case PseudoElement::Before:
|
case PseudoElement::Before:
|
||||||
case PseudoElement::FirstLetter:
|
case PseudoElement::FirstLetter:
|
||||||
case PseudoElement::FirstLine:
|
case PseudoElement::FirstLine:
|
||||||
// :has() is fussy about pseudo-elements inside it
|
// :has() is fussy about pseudo-elements inside it
|
||||||
if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(pseudo_element->type())) {
|
if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(pseudo_element.value())) {
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Selector::SimpleSelector {
|
return Selector::SimpleSelector {
|
||||||
.type = Selector::SimpleSelector::Type::PseudoElement,
|
.type = Selector::SimpleSelector::Type::PseudoElement,
|
||||||
.value = pseudo_element.value()
|
.value = Selector::PseudoElementSelector { pseudo_element.value() }
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
43
Libraries/LibWeb/CSS/PseudoElements.json
Normal file
43
Libraries/LibWeb/CSS/PseudoElements.json
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"after": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-after",
|
||||||
|
"is-generated": true
|
||||||
|
},
|
||||||
|
"backdrop": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-position-4/#selectordef-backdrop"
|
||||||
|
},
|
||||||
|
"before": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-before",
|
||||||
|
"is-generated": true
|
||||||
|
},
|
||||||
|
"details-content": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-details-content"
|
||||||
|
},
|
||||||
|
"file-selector-button": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-file-selector-button"
|
||||||
|
},
|
||||||
|
"fill": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-fill"
|
||||||
|
},
|
||||||
|
"first-letter": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-first-letter"
|
||||||
|
},
|
||||||
|
"first-line": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-first-line"
|
||||||
|
},
|
||||||
|
"marker": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-marker"
|
||||||
|
},
|
||||||
|
"placeholder": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-placeholder"
|
||||||
|
},
|
||||||
|
"selection": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-selection"
|
||||||
|
},
|
||||||
|
"thumb": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-thumb"
|
||||||
|
},
|
||||||
|
"track": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-track"
|
||||||
|
}
|
||||||
|
}
|
|
@ -535,74 +535,6 @@ String serialize_a_group_of_selectors(SelectorList const& selectors)
|
||||||
return MUST(String::join(", "sv, selectors));
|
return MUST(String::join(", "sv, selectors));
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView Selector::PseudoElementSelector::name(PseudoElement pseudo_element)
|
|
||||||
{
|
|
||||||
switch (pseudo_element) {
|
|
||||||
case PseudoElement::Before:
|
|
||||||
return "before"sv;
|
|
||||||
case PseudoElement::After:
|
|
||||||
return "after"sv;
|
|
||||||
case PseudoElement::FirstLine:
|
|
||||||
return "first-line"sv;
|
|
||||||
case PseudoElement::FirstLetter:
|
|
||||||
return "first-letter"sv;
|
|
||||||
case PseudoElement::Marker:
|
|
||||||
return "marker"sv;
|
|
||||||
case PseudoElement::Track:
|
|
||||||
return "track"sv;
|
|
||||||
case PseudoElement::Fill:
|
|
||||||
return "fill"sv;
|
|
||||||
case PseudoElement::Thumb:
|
|
||||||
return "thumb"sv;
|
|
||||||
case PseudoElement::Placeholder:
|
|
||||||
return "placeholder"sv;
|
|
||||||
case PseudoElement::Selection:
|
|
||||||
return "selection"sv;
|
|
||||||
case PseudoElement::Backdrop:
|
|
||||||
return "backdrop"sv;
|
|
||||||
case PseudoElement::FileSelectorButton:
|
|
||||||
return "file-selector-button"sv;
|
|
||||||
case PseudoElement::DetailsContent:
|
|
||||||
return "details-content"sv;
|
|
||||||
case PseudoElement::KnownPseudoElementCount:
|
|
||||||
case PseudoElement::UnknownWebKit:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<Selector::PseudoElementSelector> Selector::PseudoElementSelector::from_string(FlyString const& name)
|
|
||||||
{
|
|
||||||
if (name.equals_ignoring_ascii_case("after"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::After };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("before"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::Before };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("first-letter"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::FirstLetter };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("first-line"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::FirstLine };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("marker"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::Marker };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("track"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::Track };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("fill"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::Fill };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("thumb"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::Thumb };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("placeholder"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::Placeholder };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("selection"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::Selection };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("backdrop"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::Backdrop };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("file-selector-button"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::FileSelectorButton };
|
|
||||||
} else if (name.equals_ignoring_ascii_case("details-content"sv)) {
|
|
||||||
return Selector::PseudoElementSelector { PseudoElement::DetailsContent };
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
NonnullRefPtr<Selector> Selector::relative_to(SimpleSelector const& parent) const
|
NonnullRefPtr<Selector> Selector::relative_to(SimpleSelector const& parent) const
|
||||||
{
|
{
|
||||||
// To make us relative to the parent, prepend it to the list of compound selectors,
|
// To make us relative to the parent, prepend it to the list of compound selectors,
|
||||||
|
|
|
@ -14,34 +14,12 @@
|
||||||
#include <LibWeb/CSS/Keyword.h>
|
#include <LibWeb/CSS/Keyword.h>
|
||||||
#include <LibWeb/CSS/Parser/ComponentValue.h>
|
#include <LibWeb/CSS/Parser/ComponentValue.h>
|
||||||
#include <LibWeb/CSS/PseudoClass.h>
|
#include <LibWeb/CSS/PseudoClass.h>
|
||||||
|
#include <LibWeb/CSS/PseudoElement.h>
|
||||||
|
|
||||||
namespace Web::CSS {
|
namespace Web::CSS {
|
||||||
|
|
||||||
using SelectorList = Vector<NonnullRefPtr<class Selector>>;
|
using SelectorList = Vector<NonnullRefPtr<class Selector>>;
|
||||||
|
|
||||||
enum class PseudoElement : u8 {
|
|
||||||
Before,
|
|
||||||
After,
|
|
||||||
FirstLine,
|
|
||||||
FirstLetter,
|
|
||||||
Marker,
|
|
||||||
Track,
|
|
||||||
Fill,
|
|
||||||
Thumb,
|
|
||||||
Placeholder,
|
|
||||||
Selection,
|
|
||||||
Backdrop,
|
|
||||||
FileSelectorButton,
|
|
||||||
DetailsContent,
|
|
||||||
|
|
||||||
// Keep this last.
|
|
||||||
KnownPseudoElementCount,
|
|
||||||
|
|
||||||
// https://www.w3.org/TR/selectors-4/#compat
|
|
||||||
// NOTE: This is not last as the 'unknown -webkit- pseudo-elements' are not stored as part of any Element.
|
|
||||||
UnknownWebKit,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is a <complex-selector> in the spec. https://www.w3.org/TR/selectors-4/#complex
|
// This is a <complex-selector> in the spec. https://www.w3.org/TR/selectors-4/#complex
|
||||||
class Selector : public RefCounted<Selector> {
|
class Selector : public RefCounted<Selector> {
|
||||||
public:
|
public:
|
||||||
|
@ -61,21 +39,17 @@ public:
|
||||||
|
|
||||||
bool operator==(PseudoElementSelector const&) const = default;
|
bool operator==(PseudoElementSelector const&) const = default;
|
||||||
|
|
||||||
static Optional<PseudoElementSelector> from_string(FlyString const&);
|
|
||||||
|
|
||||||
[[nodiscard]] static bool is_known_pseudo_element_type(PseudoElement type)
|
[[nodiscard]] static bool is_known_pseudo_element_type(PseudoElement type)
|
||||||
{
|
{
|
||||||
return to_underlying(type) < to_underlying(PseudoElement::KnownPseudoElementCount);
|
return to_underlying(type) < to_underlying(PseudoElement::KnownPseudoElementCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
static StringView name(PseudoElement pseudo_element);
|
|
||||||
|
|
||||||
StringView name() const
|
StringView name() const
|
||||||
{
|
{
|
||||||
if (!m_name.is_empty())
|
if (!m_name.is_empty())
|
||||||
return m_name;
|
return m_name;
|
||||||
|
|
||||||
return name(m_type);
|
return pseudo_element_name(m_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
PseudoElement type() const { return m_type; }
|
PseudoElement type() const { return m_type; }
|
||||||
|
|
|
@ -1401,7 +1401,7 @@ void Element::serialize_pseudo_elements_as_json(JsonArraySerializer<StringBuilde
|
||||||
if (!pseudo_element)
|
if (!pseudo_element)
|
||||||
continue;
|
continue;
|
||||||
auto object = MUST(children_array.add_object());
|
auto object = MUST(children_array.add_object());
|
||||||
MUST(object.add("name"sv, MUST(String::formatted("::{}", CSS::Selector::PseudoElementSelector::name(static_cast<CSS::PseudoElement>(i))))));
|
MUST(object.add("name"sv, MUST(String::formatted("::{}", CSS::pseudo_element_name(static_cast<CSS::PseudoElement>(i))))));
|
||||||
MUST(object.add("type"sv, "pseudo-element"));
|
MUST(object.add("type"sv, "pseudo-element"));
|
||||||
MUST(object.add("parent-id"sv, unique_id().value()));
|
MUST(object.add("parent-id"sv, unique_id().value()));
|
||||||
MUST(object.add("pseudo-element"sv, i));
|
MUST(object.add("pseudo-element"sv, i));
|
||||||
|
|
|
@ -99,7 +99,7 @@ void dump_tree(StringBuilder& builder, DOM::Node const& node)
|
||||||
if (element.use_pseudo_element().has_value()) {
|
if (element.use_pseudo_element().has_value()) {
|
||||||
for (int i = 0; i < indent; ++i)
|
for (int i = 0; i < indent; ++i)
|
||||||
builder.append(" "sv);
|
builder.append(" "sv);
|
||||||
builder.appendff(" (pseudo-element: {})\n", CSS::Selector::PseudoElementSelector::name(element.use_pseudo_element().value()));
|
builder.appendff(" (pseudo-element: {})\n", CSS::pseudo_element_name(element.use_pseudo_element().value()));
|
||||||
}
|
}
|
||||||
} else if (is<DOM::Text>(node)) {
|
} else if (is<DOM::Text>(node)) {
|
||||||
builder.appendff("\"{}\"\n", as<DOM::Text>(node).data());
|
builder.appendff("\"{}\"\n", as<DOM::Text>(node).data());
|
||||||
|
|
|
@ -46,6 +46,15 @@ function (generate_css_implementation)
|
||||||
arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/PseudoClasses.json"
|
arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/PseudoClasses.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
invoke_generator(
|
||||||
|
"PseudoElement.cpp"
|
||||||
|
Lagom::GenerateCSSPseudoElement
|
||||||
|
"${LIBWEB_INPUT_FOLDER}/CSS/PseudoElements.json"
|
||||||
|
"CSS/PseudoElement.h"
|
||||||
|
"CSS/PseudoElement.cpp"
|
||||||
|
arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/PseudoElements.json"
|
||||||
|
)
|
||||||
|
|
||||||
invoke_generator(
|
invoke_generator(
|
||||||
"TransformFunctions.cpp"
|
"TransformFunctions.cpp"
|
||||||
Lagom::GenerateCSSTransformFunctions
|
Lagom::GenerateCSSTransformFunctions
|
||||||
|
@ -115,6 +124,7 @@ function (generate_css_implementation)
|
||||||
"CSS/MediaFeatureID.h"
|
"CSS/MediaFeatureID.h"
|
||||||
"CSS/PropertyID.h"
|
"CSS/PropertyID.h"
|
||||||
"CSS/PseudoClass.h"
|
"CSS/PseudoClass.h"
|
||||||
|
"CSS/PseudoElement.h"
|
||||||
"CSS/TransformFunctions.h"
|
"CSS/TransformFunctions.h"
|
||||||
)
|
)
|
||||||
list(TRANSFORM CSS_GENERATED_HEADERS PREPEND "${CMAKE_CURRENT_BINARY_DIR}/")
|
list(TRANSFORM CSS_GENERATED_HEADERS PREPEND "${CMAKE_CURRENT_BINARY_DIR}/")
|
||||||
|
|
|
@ -6,6 +6,7 @@ lagom_tool(GenerateCSSMathFunctions SOURCES GenerateCSSMathFunctions.cpp
|
||||||
lagom_tool(GenerateCSSMediaFeatureID SOURCES GenerateCSSMediaFeatureID.cpp LIBS LibMain)
|
lagom_tool(GenerateCSSMediaFeatureID SOURCES GenerateCSSMediaFeatureID.cpp LIBS LibMain)
|
||||||
lagom_tool(GenerateCSSPropertyID SOURCES GenerateCSSPropertyID.cpp LIBS LibMain)
|
lagom_tool(GenerateCSSPropertyID SOURCES GenerateCSSPropertyID.cpp LIBS LibMain)
|
||||||
lagom_tool(GenerateCSSPseudoClass SOURCES GenerateCSSPseudoClass.cpp LIBS LibMain)
|
lagom_tool(GenerateCSSPseudoClass SOURCES GenerateCSSPseudoClass.cpp LIBS LibMain)
|
||||||
|
lagom_tool(GenerateCSSPseudoElement SOURCES GenerateCSSPseudoElement.cpp LIBS LibMain)
|
||||||
lagom_tool(GenerateCSSStyleProperties SOURCES GenerateCSSStyleProperties.cpp LIBS LibMain)
|
lagom_tool(GenerateCSSStyleProperties SOURCES GenerateCSSStyleProperties.cpp LIBS LibMain)
|
||||||
lagom_tool(GenerateCSSTransformFunctions SOURCES GenerateCSSTransformFunctions.cpp LIBS LibMain)
|
lagom_tool(GenerateCSSTransformFunctions SOURCES GenerateCSSTransformFunctions.cpp LIBS LibMain)
|
||||||
lagom_tool(GenerateWindowOrWorkerInterfaces SOURCES GenerateWindowOrWorkerInterfaces.cpp LIBS LibMain LibIDL)
|
lagom_tool(GenerateWindowOrWorkerInterfaces SOURCES GenerateWindowOrWorkerInterfaces.cpp LIBS LibMain LibIDL)
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022-2025, Sam Atkins <sam@ladybird.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_elements_data, Core::File& file);
|
||||||
|
ErrorOr<void> generate_implementation_file(JsonObject& pseudo_elements_data, Core::File& file);
|
||||||
|
|
||||||
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
{
|
||||||
|
StringView generated_header_path;
|
||||||
|
StringView generated_implementation_path;
|
||||||
|
StringView json_path;
|
||||||
|
|
||||||
|
Core::ArgsParser args_parser;
|
||||||
|
args_parser.add_option(generated_header_path, "Path to the PseudoElements header file to generate", "generated-header-path", 'h', "generated-header-path");
|
||||||
|
args_parser.add_option(generated_implementation_path, "Path to the PseudoElements implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
|
||||||
|
args_parser.add_option(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(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_elements_data, Core::File& file)
|
||||||
|
{
|
||||||
|
StringBuilder builder;
|
||||||
|
SourceGenerator generator { builder };
|
||||||
|
|
||||||
|
auto pseudo_element_count = 0u;
|
||||||
|
pseudo_elements_data.for_each_member([&pseudo_element_count](auto const&, auto const&) { ++pseudo_element_count; });
|
||||||
|
generator.set("pseudo_element_underlying_type", underlying_type_for_enum(pseudo_element_count));
|
||||||
|
|
||||||
|
generator.append(R"~~~(
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Optional.h>
|
||||||
|
#include <AK/StringView.h>
|
||||||
|
|
||||||
|
namespace Web::CSS {
|
||||||
|
|
||||||
|
enum class PseudoElement : @pseudo_element_underlying_type@ {
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
pseudo_elements_data.for_each_member([&](auto& name, auto&) {
|
||||||
|
auto member_generator = generator.fork();
|
||||||
|
member_generator.set("name:titlecase", title_casify(name));
|
||||||
|
|
||||||
|
member_generator.appendln(" @name:titlecase@,");
|
||||||
|
});
|
||||||
|
generator.append(R"~~~(
|
||||||
|
KnownPseudoElementCount,
|
||||||
|
|
||||||
|
UnknownWebKit,
|
||||||
|
};
|
||||||
|
|
||||||
|
Optional<PseudoElement> pseudo_element_from_string(StringView);
|
||||||
|
StringView pseudo_element_name(PseudoElement);
|
||||||
|
|
||||||
|
}
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
TRY(file.write_until_depleted(generator.as_string_view().bytes()));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> generate_implementation_file(JsonObject& pseudo_elements_data, Core::File& file)
|
||||||
|
{
|
||||||
|
StringBuilder builder;
|
||||||
|
SourceGenerator generator { builder };
|
||||||
|
|
||||||
|
generator.append(R"~~~(
|
||||||
|
#include <LibWeb/CSS/PseudoElement.h>
|
||||||
|
|
||||||
|
namespace Web::CSS {
|
||||||
|
|
||||||
|
Optional<PseudoElement> pseudo_element_from_string(StringView string)
|
||||||
|
{
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
pseudo_elements_data.for_each_member([&](auto& name, auto&) {
|
||||||
|
auto member_generator = generator.fork();
|
||||||
|
member_generator.set("name", name);
|
||||||
|
member_generator.set("name:titlecase", title_casify(name));
|
||||||
|
|
||||||
|
member_generator.append(R"~~~(
|
||||||
|
if (string.equals_ignoring_ascii_case("@name@"sv))
|
||||||
|
return PseudoElement::@name:titlecase@;
|
||||||
|
)~~~");
|
||||||
|
});
|
||||||
|
|
||||||
|
generator.append(R"~~~(
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
StringView pseudo_element_name(PseudoElement pseudo_element)
|
||||||
|
{
|
||||||
|
switch (pseudo_element) {
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
pseudo_elements_data.for_each_member([&](auto& name, auto&) {
|
||||||
|
auto member_generator = generator.fork();
|
||||||
|
member_generator.set("name", name);
|
||||||
|
member_generator.set("name:titlecase", title_casify(name));
|
||||||
|
|
||||||
|
member_generator.append(R"~~~(
|
||||||
|
case PseudoElement::@name:titlecase@:
|
||||||
|
return "@name@"sv;
|
||||||
|
)~~~");
|
||||||
|
});
|
||||||
|
|
||||||
|
generator.append(R"~~~(
|
||||||
|
case PseudoElement::KnownPseudoElementCount:
|
||||||
|
case PseudoElement::UnknownWebKit:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
TRY(file.write_until_depleted(generator.as_string_view().bytes()));
|
||||||
|
return {};
|
||||||
|
}
|
|
@ -103,3 +103,14 @@ inline String css_property_to_idl_attribute(StringView property_name, bool lower
|
||||||
// 5. Return output.
|
// 5. Return output.
|
||||||
return MUST(output.to_string());
|
return MUST(output.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline StringView underlying_type_for_enum(size_t member_count)
|
||||||
|
{
|
||||||
|
if (member_count <= NumericLimits<u8>::max())
|
||||||
|
return "u8"sv;
|
||||||
|
if (member_count <= NumericLimits<u16>::max())
|
||||||
|
return "u16"sv;
|
||||||
|
if (member_count <= NumericLimits<u32>::max())
|
||||||
|
return "u32"sv;
|
||||||
|
return "u64"sv;
|
||||||
|
}
|
||||||
|
|
|
@ -52,6 +52,14 @@ lagom_tool("GenerateCSSPseudoClass") {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lagom_tool("GenerateCSSPseudoElement") {
|
||||||
|
sources = [ "GenerateCSSPseudoElement.cpp" ]
|
||||||
|
deps = [
|
||||||
|
":headers",
|
||||||
|
"//Userland/Libraries/LibMain",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
lagom_tool("GenerateCSSTransformFunctions") {
|
lagom_tool("GenerateCSSTransformFunctions") {
|
||||||
sources = [ "GenerateCSSTransformFunctions.cpp" ]
|
sources = [ "GenerateCSSTransformFunctions.cpp" ]
|
||||||
deps = [
|
deps = [
|
||||||
|
|
|
@ -185,6 +185,23 @@ compiled_action("generate_css_pseudo_class") {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compiled_action("generate_css_pseudo_element") {
|
||||||
|
tool = "//Meta/Lagom/Tools/CodeGenerators/LibWeb:GenerateCSSPseudoElement"
|
||||||
|
inputs = [ "CSS/PseudoElement.json" ]
|
||||||
|
outputs = [
|
||||||
|
"$target_gen_dir/CSS/PseudoElement.h",
|
||||||
|
"$target_gen_dir/CSS/PseudoElement.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") {
|
compiled_action("generate_css_transform_functions") {
|
||||||
tool =
|
tool =
|
||||||
"//Meta/Lagom/Tools/CodeGenerators/LibWeb:GenerateCSSTransformFunctions"
|
"//Meta/Lagom/Tools/CodeGenerators/LibWeb:GenerateCSSTransformFunctions"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue