mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-16 16:12:53 +00:00
LibWeb: Implement functional pseudo-element parsing
"Functional" as in "it's a function token" and not "it works", because the behaviour for these is unimplemented. :^) This is modeled after the pseudo-class parsing, but with some changes based on things I don't like about that implementation. I've implemented the `<pt-name-selector>` parameter used by view-transitions for now, but nothing else.
This commit is contained in:
parent
5cf04a33ad
commit
88e11eea2d
Notes:
github-actions[bot]
2025-03-25 07:56:12 +00:00
Author: https://github.com/AtkinsSJ
Commit: 88e11eea2d
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4063
8 changed files with 237 additions and 43 deletions
|
@ -127,13 +127,15 @@ This generated `PsuedoElement.h` and `PseudoElement.cpp`.
|
||||||
|
|
||||||
Each entry has the following properties:
|
Each entry has the following properties:
|
||||||
|
|
||||||
| Field | Required | Default | Description |
|
| Field | Required | Default | Description |
|
||||||
|----------------------|----------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|----------------------|----------|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `spec` | No | Nothing | Link to the spec definition, for reference. Not used in generated code. |
|
| `alias-for` | No | Nothing | Use to specify that this should be treated as an alias for the named pseudo-element. |
|
||||||
| `alias-for` | No | Nothing | Use to specify that this should be treated as an alias for the named pseudo-element. |
|
| `function-syntax` | No | Nothing | Syntax for the function arguments if this is a function-type pseudo-element. Copied directly from the spec. |
|
||||||
| `is-generated` | No | `false` | Whether this is a [generated pseudo-element.](https://drafts.csswg.org/css-pseudo-4/#generated-content) |
|
| `is-generated` | No | `false` | Whether this is a [generated pseudo-element.](https://drafts.csswg.org/css-pseudo-4/#generated-content) |
|
||||||
| `is-allowed-in-has` | No | `false` | Whether this is a [`:has`-allowed pseudo-element.](https://drafts.csswg.org/selectors/#has-allowed-pseudo-element) |
|
| `is-allowed-in-has` | No | `false` | Whether this is a [`:has`-allowed pseudo-element.](https://drafts.csswg.org/selectors/#has-allowed-pseudo-element) |
|
||||||
| `property-whitelist` | No | Nothing | Some pseudo-elements only permit certain properties. If so, name them in an array here. Some special values are allowed here for categories of properties - see below. |
|
| `property-whitelist` | No | Nothing | Some pseudo-elements only permit certain properties. If so, name them in an array here. Some special values are allowed here for categories of properties - see below. |
|
||||||
|
| `spec` | No | Nothing | Link to the spec definition, for reference. Not used in generated code. |
|
||||||
|
| `type` | No | `"identifier"` | What type of pseudo-element is this. Either "identifier", "function", or "both". |
|
||||||
|
|
||||||
The generated code provides:
|
The generated code provides:
|
||||||
- A `PseudoElement` enum listing every pseudo-element name
|
- A `PseudoElement` enum listing every pseudo-element name
|
||||||
|
|
|
@ -778,7 +778,7 @@ Optional<String> KeyframeEffect::pseudo_element() const
|
||||||
{
|
{
|
||||||
if (!m_target_pseudo_selector.has_value())
|
if (!m_target_pseudo_selector.has_value())
|
||||||
return {};
|
return {};
|
||||||
return MUST(String::formatted("::{}", m_target_pseudo_selector->name()));
|
return m_target_pseudo_selector->serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/web-animations-1/#dom-keyframeeffect-pseudoelement
|
// https://drafts.csswg.org/web-animations-1/#dom-keyframeeffect-pseudoelement
|
||||||
|
|
|
@ -401,46 +401,104 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
||||||
if (peek_token_ends_selector())
|
if (peek_token_ends_selector())
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
|
|
||||||
bool is_pseudo = false;
|
// Note that we already consumed one colon before we entered this function.
|
||||||
|
// FIXME: Don't do that.
|
||||||
|
bool is_pseudo_element = false;
|
||||||
if (tokens.next_token().is(Token::Type::Colon)) {
|
if (tokens.next_token().is(Token::Type::Colon)) {
|
||||||
is_pseudo = true;
|
is_pseudo_element = true;
|
||||||
tokens.discard_a_token();
|
tokens.discard_a_token();
|
||||||
if (peek_token_ends_selector())
|
if (peek_token_ends_selector())
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_pseudo) {
|
if (is_pseudo_element) {
|
||||||
auto const& name_token = tokens.consume_a_token();
|
auto const& name_token = tokens.consume_a_token();
|
||||||
if (!name_token.is(Token::Type::Ident)) {
|
bool is_function = false;
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "Expected an ident for pseudo-element, got: '{}'", name_token.to_debug_string());
|
FlyString pseudo_name;
|
||||||
|
|
||||||
|
if (name_token.is(Token::Type::Ident)) {
|
||||||
|
pseudo_name = name_token.token().ident();
|
||||||
|
} else if (name_token.is_function()) {
|
||||||
|
pseudo_name = name_token.function().name;
|
||||||
|
is_function = true;
|
||||||
|
} else {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Expected an ident or function token for pseudo-element, got: '{}'", name_token.to_debug_string());
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pseudo_name = name_token.token().ident();
|
bool is_aliased_pseudo = false;
|
||||||
|
auto pseudo_element = pseudo_element_from_string(pseudo_name);
|
||||||
if (auto pseudo_element = pseudo_element_from_string(pseudo_name); pseudo_element.has_value()) {
|
if (!pseudo_element.has_value()) {
|
||||||
// :has() is fussy about pseudo-elements inside it
|
pseudo_element = aliased_pseudo_element_from_string(pseudo_name);
|
||||||
if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(*pseudo_element)) {
|
is_aliased_pseudo = pseudo_element.has_value();
|
||||||
return ParseError::SyntaxError;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Selector::SimpleSelector {
|
|
||||||
.type = Selector::SimpleSelector::Type::PseudoElement,
|
|
||||||
.value = Selector::PseudoElementSelector { pseudo_element.release_value() }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aliased pseudo-elements behave like their target pseudo-element, but serialize as themselves. So store their
|
if (pseudo_element.has_value()) {
|
||||||
// name like we do for unknown -webkit pseudos below.
|
auto metadata = pseudo_element_metadata(*pseudo_element);
|
||||||
if (auto pseudo_element = aliased_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)) {
|
if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(*pseudo_element)) {
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Selector::PseudoElementSelector::Value value = Empty {};
|
||||||
|
if (is_function) {
|
||||||
|
if (!metadata.is_valid_as_function) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Pseudo-element '::{}()' is not valid as a function.", pseudo_name);
|
||||||
|
return ParseError::SyntaxError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse arguments
|
||||||
|
TokenStream function_tokens { name_token.function().value };
|
||||||
|
function_tokens.discard_whitespace();
|
||||||
|
|
||||||
|
switch (metadata.parameter_type) {
|
||||||
|
case PseudoElementMetadata::ParameterType::None:
|
||||||
|
if (function_tokens.has_next_token()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Pseudo-element '::{}()' invalid: Should have no arguments.", pseudo_name);
|
||||||
|
return ParseError::SyntaxError;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PseudoElementMetadata::ParameterType::PTNameSelector: {
|
||||||
|
// <pt-name-selector> = '*' | <custom-ident>
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#typedef-pt-name-selector
|
||||||
|
if (function_tokens.next_token().is_delim('*')) {
|
||||||
|
function_tokens.discard_a_token(); // *
|
||||||
|
value = Selector::PseudoElementSelector::PTNameSelector { .is_universal = true };
|
||||||
|
} else if (auto custom_ident = parse_custom_ident(function_tokens, {}); custom_ident.has_value()) {
|
||||||
|
value = Selector::PseudoElementSelector::PTNameSelector { .value = custom_ident.release_value() };
|
||||||
|
} else {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Invalid <pt-name-selector> in :{}() - expected `*` or `<custom-ident>`, got `{}`", pseudo_name, function_tokens.next_token().to_debug_string());
|
||||||
|
return ParseError::SyntaxError;
|
||||||
|
}
|
||||||
|
function_tokens.discard_whitespace();
|
||||||
|
if (function_tokens.has_next_token()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Invalid <pt-name-selector> in :{}() - trailing tokens", pseudo_name);
|
||||||
|
return ParseError::SyntaxError;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!metadata.is_valid_as_identifier) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Pseudo-element '::{}' is not valid as an identifier.", pseudo_name);
|
||||||
|
return ParseError::SyntaxError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aliased pseudo-elements behave like their target pseudo-element, but serialize as themselves. So store their
|
||||||
|
// name like we do for unknown -webkit pseudos below.
|
||||||
|
if (is_aliased_pseudo) {
|
||||||
|
return Selector::SimpleSelector {
|
||||||
|
.type = Selector::SimpleSelector::Type::PseudoElement,
|
||||||
|
.value = Selector::PseudoElementSelector { pseudo_element.release_value(), pseudo_name.to_string().to_ascii_lowercase(), move(value) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return Selector::SimpleSelector {
|
return Selector::SimpleSelector {
|
||||||
.type = Selector::SimpleSelector::Type::PseudoElement,
|
.type = Selector::SimpleSelector::Type::PseudoElement,
|
||||||
.value = Selector::PseudoElementSelector { pseudo_element.release_value(), pseudo_name.to_string().to_ascii_lowercase() }
|
.value = Selector::PseudoElementSelector { pseudo_element.release_value(), move(value) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,7 +507,7 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
||||||
// and that are not functional notations must be treated as valid at parse time. (That is, ::-webkit-asdf is
|
// and that are not functional notations must be treated as valid at parse time. (That is, ::-webkit-asdf is
|
||||||
// valid at parse time, but ::-webkit-jkl() is not.) If they’re not otherwise recognized and supported, they
|
// valid at parse time, but ::-webkit-jkl() is not.) If they’re not otherwise recognized and supported, they
|
||||||
// must be treated as matching nothing, and are unknown -webkit- pseudo-elements.
|
// must be treated as matching nothing, and are unknown -webkit- pseudo-elements.
|
||||||
if (pseudo_name.starts_with_bytes("-webkit-"sv, CaseSensitivity::CaseInsensitive)) {
|
if (!is_function && pseudo_name.starts_with_bytes("-webkit-"sv, CaseSensitivity::CaseInsensitive)) {
|
||||||
// :has() only allows a limited set of pseudo-elements inside it, which doesn't include unknown ones.
|
// :has() only allows a limited set of pseudo-elements inside it, which doesn't include unknown ones.
|
||||||
if (m_pseudo_class_context.contains_slow(PseudoClass::Has))
|
if (m_pseudo_class_context.contains_slow(PseudoClass::Has))
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
|
|
|
@ -111,5 +111,28 @@
|
||||||
},
|
},
|
||||||
"track": {
|
"track": {
|
||||||
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-track"
|
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-track"
|
||||||
|
},
|
||||||
|
"view-transition": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-view-transitions-1/#selectordef-view-transition"
|
||||||
|
},
|
||||||
|
"view-transition-group": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-view-transitions-1/#selectordef-view-transition-group",
|
||||||
|
"type": "function",
|
||||||
|
"function-syntax": "<pt-name-selector>"
|
||||||
|
},
|
||||||
|
"view-transition-image-pair": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-view-transitions-1/#selectordef-view-transition-image-pair",
|
||||||
|
"type": "function",
|
||||||
|
"function-syntax": "<pt-name-selector>"
|
||||||
|
},
|
||||||
|
"view-transition-new": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-view-transitions-1/#selectordef-view-transition-new",
|
||||||
|
"type": "function",
|
||||||
|
"function-syntax": "<pt-name-selector>"
|
||||||
|
},
|
||||||
|
"view-transition-old": {
|
||||||
|
"spec": "https://drafts.csswg.org/css-view-transitions-1/#selectordef-view-transition-old",
|
||||||
|
"type": "function",
|
||||||
|
"function-syntax": "<pt-name-selector>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
|
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -285,6 +285,31 @@ u32 Selector::specificity() const
|
||||||
return *m_specificity;
|
return *m_specificity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String Selector::PseudoElementSelector::serialize() const
|
||||||
|
{
|
||||||
|
StringBuilder builder;
|
||||||
|
builder.append("::"sv);
|
||||||
|
|
||||||
|
if (!m_name.is_empty()) {
|
||||||
|
builder.append(m_name);
|
||||||
|
} else {
|
||||||
|
builder.append(pseudo_element_name(m_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_value.visit(
|
||||||
|
[&builder](PTNameSelector const& pt_name_selector) {
|
||||||
|
builder.append('(');
|
||||||
|
if (pt_name_selector.is_universal)
|
||||||
|
builder.append('*');
|
||||||
|
else
|
||||||
|
builder.append(pt_name_selector.value);
|
||||||
|
builder.append(')');
|
||||||
|
},
|
||||||
|
[](Empty const&) {});
|
||||||
|
|
||||||
|
return builder.to_string_without_validation();
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/cssom/#serialize-a-simple-selector
|
// https://www.w3.org/TR/cssom/#serialize-a-simple-selector
|
||||||
String Selector::SimpleSelector::serialize() const
|
String Selector::SimpleSelector::serialize() const
|
||||||
{
|
{
|
||||||
|
@ -519,8 +544,7 @@ String Selector::serialize() const
|
||||||
// 4. If this is the last part of the chain of the selector and there is a pseudo-element,
|
// 4. If this is the last part of the chain of the selector and there is a pseudo-element,
|
||||||
// append "::" followed by the name of the pseudo-element, to s.
|
// append "::" followed by the name of the pseudo-element, to s.
|
||||||
if (compound_selector.simple_selectors.last().type == Selector::SimpleSelector::Type::PseudoElement) {
|
if (compound_selector.simple_selectors.last().type == Selector::SimpleSelector::Type::PseudoElement) {
|
||||||
s.append("::"sv);
|
s.append(compound_selector.simple_selectors.last().pseudo_element().serialize());
|
||||||
s.append(compound_selector.simple_selectors.last().pseudo_element().name());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,15 +25,24 @@ class Selector : public RefCounted<Selector> {
|
||||||
public:
|
public:
|
||||||
class PseudoElementSelector {
|
class PseudoElementSelector {
|
||||||
public:
|
public:
|
||||||
explicit PseudoElementSelector(PseudoElement type)
|
struct PTNameSelector {
|
||||||
|
bool is_universal { false };
|
||||||
|
FlyString value {};
|
||||||
|
};
|
||||||
|
|
||||||
|
using Value = Variant<Empty, PTNameSelector>;
|
||||||
|
|
||||||
|
explicit PseudoElementSelector(PseudoElement type, Value value = {})
|
||||||
: m_type(type)
|
: m_type(type)
|
||||||
|
, m_value(move(value))
|
||||||
{
|
{
|
||||||
VERIFY(is_known_pseudo_element_type(type));
|
VERIFY(is_known_pseudo_element_type(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
PseudoElementSelector(PseudoElement type, String name)
|
PseudoElementSelector(PseudoElement type, String name, Value value = {})
|
||||||
: m_type(type)
|
: m_type(type)
|
||||||
, m_name(move(name))
|
, m_name(move(name))
|
||||||
|
, m_value(move(value))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,19 +53,16 @@ public:
|
||||||
return to_underlying(type) < to_underlying(PseudoElement::KnownPseudoElementCount);
|
return to_underlying(type) < to_underlying(PseudoElement::KnownPseudoElementCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView name() const
|
String serialize() const;
|
||||||
{
|
|
||||||
if (!m_name.is_empty())
|
|
||||||
return m_name;
|
|
||||||
|
|
||||||
return pseudo_element_name(m_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
PseudoElement type() const { return m_type; }
|
PseudoElement type() const { return m_type; }
|
||||||
|
|
||||||
|
PTNameSelector const& pt_name_selector() const { return m_value.get<PTNameSelector>(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PseudoElement m_type;
|
PseudoElement m_type;
|
||||||
String m_name;
|
String m_name;
|
||||||
|
Variant<Empty, PTNameSelector> m_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SimpleSelector {
|
struct SimpleSelector {
|
||||||
|
|
|
@ -586,7 +586,19 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector, int in
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoElement) {
|
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoElement) {
|
||||||
builder.appendff(" pseudo_element={}", simple_selector.pseudo_element().name());
|
auto const& pseudo_element = simple_selector.pseudo_element();
|
||||||
|
builder.appendff(" pseudo_element={}", CSS::pseudo_element_name(pseudo_element.type()));
|
||||||
|
auto pseudo_element_metadata = CSS::pseudo_element_metadata(pseudo_element.type());
|
||||||
|
|
||||||
|
switch (pseudo_element_metadata.parameter_type) {
|
||||||
|
case CSS::PseudoElementMetadata::ParameterType::None:
|
||||||
|
break;
|
||||||
|
case CSS::PseudoElementMetadata::ParameterType::PTNameSelector: {
|
||||||
|
auto const& [is_universal, value] = pseudo_element.pt_name_selector();
|
||||||
|
builder.appendff("(is_universal={}, value='{}')", is_universal, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Attribute) {
|
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Attribute) {
|
||||||
|
|
|
@ -88,6 +88,16 @@ StringView pseudo_element_name(PseudoElement);
|
||||||
bool is_has_allowed_pseudo_element(PseudoElement);
|
bool is_has_allowed_pseudo_element(PseudoElement);
|
||||||
bool pseudo_element_supports_property(PseudoElement, PropertyID);
|
bool pseudo_element_supports_property(PseudoElement, PropertyID);
|
||||||
|
|
||||||
|
struct PseudoElementMetadata {
|
||||||
|
enum class ParameterType {
|
||||||
|
None,
|
||||||
|
PTNameSelector,
|
||||||
|
} parameter_type;
|
||||||
|
bool is_valid_as_function;
|
||||||
|
bool is_valid_as_identifier;
|
||||||
|
};
|
||||||
|
PseudoElementMetadata pseudo_element_metadata(PseudoElement);
|
||||||
|
|
||||||
enum class GeneratedPseudoElement : @generated_pseudo_element_underlying_type@ {
|
enum class GeneratedPseudoElement : @generated_pseudo_element_underlying_type@ {
|
||||||
)~~~");
|
)~~~");
|
||||||
pseudo_elements_data.for_each_member([&](auto& name, JsonValue const& value) {
|
pseudo_elements_data.for_each_member([&](auto& name, JsonValue const& value) {
|
||||||
|
@ -463,6 +473,65 @@ bool pseudo_element_supports_property(PseudoElement pseudo_element, PropertyID p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PseudoElementMetadata pseudo_element_metadata(PseudoElement pseudo_element)
|
||||||
|
{
|
||||||
|
switch (pseudo_element) {
|
||||||
|
)~~~");
|
||||||
|
pseudo_elements_data.for_each_member([&](auto& name, JsonValue const& value) {
|
||||||
|
auto& pseudo_element = value.as_object();
|
||||||
|
if (pseudo_element.has("alias-for"sv))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool is_valid_as_function = false;
|
||||||
|
bool is_valid_as_identifier = false;
|
||||||
|
auto const& type = pseudo_element.get_string("type"sv);
|
||||||
|
if (type == "function"sv) {
|
||||||
|
is_valid_as_function = true;
|
||||||
|
} else if (type == "both"sv) {
|
||||||
|
is_valid_as_function = true;
|
||||||
|
is_valid_as_identifier = true;
|
||||||
|
} else {
|
||||||
|
is_valid_as_identifier = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String parameter_type = "None"_string;
|
||||||
|
if (is_valid_as_function) {
|
||||||
|
auto const& function_syntax = pseudo_element.get_string("function-syntax"sv).value();
|
||||||
|
if (function_syntax == "<pt-name-selector>"sv) {
|
||||||
|
parameter_type = "PTNameSelector"_string;
|
||||||
|
} else {
|
||||||
|
warnln("Unrecognized pseudo-element parameter type: `{}`", function_syntax);
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
} else if (pseudo_element.has("function-syntax"sv)) {
|
||||||
|
warnln("Pseudo-element `::{}` has `function-syntax` but is not a function type.", name);
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto member_generator = generator.fork();
|
||||||
|
member_generator.set("name:titlecase", title_casify(name));
|
||||||
|
member_generator.set("parameter_type", parameter_type);
|
||||||
|
member_generator.set("is_valid_as_function", is_valid_as_function ? "true"_string : "false"_string);
|
||||||
|
member_generator.set("is_valid_as_identifier", is_valid_as_identifier ? "true"_string : "false"_string);
|
||||||
|
|
||||||
|
member_generator.append(R"~~~(
|
||||||
|
case PseudoElement::@name:titlecase@:
|
||||||
|
return {
|
||||||
|
.parameter_type = PseudoElementMetadata::ParameterType::@parameter_type@,
|
||||||
|
.is_valid_as_function = @is_valid_as_function@,
|
||||||
|
.is_valid_as_identifier = @is_valid_as_identifier@,
|
||||||
|
};
|
||||||
|
)~~~");
|
||||||
|
});
|
||||||
|
|
||||||
|
generator.append(R"~~~(
|
||||||
|
case PseudoElement::KnownPseudoElementCount:
|
||||||
|
case PseudoElement::UnknownWebKit:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
Optional<GeneratedPseudoElement> to_generated_pseudo_element(PseudoElement pseudo_element)
|
Optional<GeneratedPseudoElement> to_generated_pseudo_element(PseudoElement pseudo_element)
|
||||||
{
|
{
|
||||||
switch (pseudo_element) {
|
switch (pseudo_element) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue