mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-03 08:08:43 +00:00
LibWeb/CSS: Implement "parse with a <syntax>
"
Uses the SyntaxNode tree to parse a list of ComponentValues into some kind of StyleValue.
This commit is contained in:
parent
ded2207762
commit
0a5e8c2865
Notes:
github-actions[bot]
2025-07-16 13:49:04 +00:00
Author: https://github.com/AtkinsSJ
Commit: 0a5e8c2865
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5400
Reviewed-by: https://github.com/tcl3 ✅
6 changed files with 140 additions and 7 deletions
|
@ -144,6 +144,8 @@ public:
|
||||||
};
|
};
|
||||||
static Optional<Vector<ComponentValue>> parse_declaration_value(TokenStream<ComponentValue>&, StopAtComma = StopAtComma::No);
|
static Optional<Vector<ComponentValue>> parse_declaration_value(TokenStream<ComponentValue>&, StopAtComma = StopAtComma::No);
|
||||||
|
|
||||||
|
NonnullRefPtr<CSSStyleValue const> parse_with_a_syntax(Vector<ComponentValue> const& input, SyntaxNode const& syntax, Optional<DOM::AbstractElement> const& element = {});
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parser(ParsingParams const&, Vector<Token>);
|
Parser(ParsingParams const&, Vector<Token>);
|
||||||
|
|
||||||
|
@ -517,6 +519,8 @@ private:
|
||||||
|
|
||||||
NonnullRefPtr<CSSStyleValue const> resolve_unresolved_style_value(DOM::AbstractElement&, GuardedSubstitutionContexts&, PropertyIDOrCustomPropertyName, UnresolvedStyleValue const&);
|
NonnullRefPtr<CSSStyleValue const> resolve_unresolved_style_value(DOM::AbstractElement&, GuardedSubstitutionContexts&, PropertyIDOrCustomPropertyName, UnresolvedStyleValue const&);
|
||||||
|
|
||||||
|
RefPtr<CSSStyleValue const> parse_according_to_syntax_node(TokenStream<ComponentValue>& tokens, SyntaxNode const& syntax_node, Optional<DOM::AbstractElement> const& element);
|
||||||
|
|
||||||
static bool has_ignored_vendor_prefix(StringView);
|
static bool has_ignored_vendor_prefix(StringView);
|
||||||
|
|
||||||
void extract_property(Declaration const&, Parser::PropertiesAndCustomProperties&);
|
void extract_property(Declaration const&, Parser::PropertiesAndCustomProperties&);
|
||||||
|
|
|
@ -33,9 +33,16 @@ void UniversalSyntaxNode::dump(StringBuilder& builder, int indent) const
|
||||||
builder.appendff("{: >{}}Universal\n", "", indent);
|
builder.appendff("{: >{}}Universal\n", "", indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeSyntaxNode::TypeSyntaxNode(FlyString type_name)
|
NonnullOwnPtr<TypeSyntaxNode> TypeSyntaxNode::create(FlyString type_name)
|
||||||
|
{
|
||||||
|
auto value_type = value_type_from_string(type_name);
|
||||||
|
return adopt_own(*new TypeSyntaxNode(move(type_name), move(value_type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSyntaxNode::TypeSyntaxNode(FlyString type_name, Optional<ValueType> value_type)
|
||||||
: SyntaxNode(NodeType::Type)
|
: SyntaxNode(NodeType::Type)
|
||||||
, m_type_name(move(type_name))
|
, m_type_name(move(type_name))
|
||||||
|
, m_value_type(move(value_type))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <AK/NonnullOwnPtr.h>
|
#include <AK/NonnullOwnPtr.h>
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
#include <LibWeb/CSS/PropertyID.h>
|
||||||
|
|
||||||
namespace Web::CSS::Parser {
|
namespace Web::CSS::Parser {
|
||||||
|
|
||||||
|
@ -79,20 +80,19 @@ private:
|
||||||
// '<foo>'
|
// '<foo>'
|
||||||
class TypeSyntaxNode final : public SyntaxNode {
|
class TypeSyntaxNode final : public SyntaxNode {
|
||||||
public:
|
public:
|
||||||
static NonnullOwnPtr<TypeSyntaxNode> create(FlyString type_name)
|
static NonnullOwnPtr<TypeSyntaxNode> create(FlyString type_name);
|
||||||
{
|
|
||||||
return adopt_own(*new TypeSyntaxNode(move(type_name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~TypeSyntaxNode() override;
|
virtual ~TypeSyntaxNode() override;
|
||||||
|
|
||||||
FlyString const& type_name() const { return m_type_name; }
|
FlyString const& type_name() const { return m_type_name; }
|
||||||
|
Optional<ValueType> const& value_type() const { return m_value_type; }
|
||||||
|
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent) const override;
|
virtual void dump(StringBuilder&, int indent) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TypeSyntaxNode(FlyString type_name);
|
TypeSyntaxNode(FlyString type_name, Optional<ValueType> value_type);
|
||||||
FlyString m_type_name;
|
FlyString m_type_name;
|
||||||
|
Optional<ValueType> m_value_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
// '+'
|
// '+'
|
||||||
|
|
|
@ -5,10 +5,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/GenericShorthands.h>
|
#include <AK/GenericShorthands.h>
|
||||||
|
#include <LibWeb/CSS/Parser/ArbitrarySubstitutionFunctions.h>
|
||||||
#include <LibWeb/CSS/Parser/Parser.h>
|
#include <LibWeb/CSS/Parser/Parser.h>
|
||||||
#include <LibWeb/CSS/Parser/Syntax.h>
|
#include <LibWeb/CSS/Parser/Syntax.h>
|
||||||
#include <LibWeb/CSS/Parser/SyntaxParsing.h>
|
#include <LibWeb/CSS/Parser/SyntaxParsing.h>
|
||||||
#include <LibWeb/CSS/Parser/TokenStream.h>
|
#include <LibWeb/CSS/Parser/TokenStream.h>
|
||||||
|
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
|
||||||
|
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
|
||||||
|
#include <LibWeb/CSS/StyleValues/GuaranteedInvalidStyleValue.h>
|
||||||
|
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
||||||
|
#include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h>
|
||||||
|
|
||||||
namespace Web::CSS::Parser {
|
namespace Web::CSS::Parser {
|
||||||
|
|
||||||
|
@ -207,4 +213,116 @@ OwnPtr<SyntaxNode> parse_as_syntax(Vector<ComponentValue> const& component_value
|
||||||
return AlternativesSyntaxNode::create(move(syntax_components));
|
return AlternativesSyntaxNode::create(move(syntax_components));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<CSSStyleValue const> parse_with_a_syntax(ParsingParams const& parsing_params, Vector<ComponentValue> const& input, SyntaxNode const& syntax, Optional<DOM::AbstractElement> const& element)
|
||||||
|
{
|
||||||
|
return Parser::create(parsing_params, ""sv).parse_with_a_syntax(input, syntax, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<CSSStyleValue const> Parser::parse_according_to_syntax_node(TokenStream<ComponentValue>& tokens, SyntaxNode const& syntax_node, Optional<DOM::AbstractElement> const& element)
|
||||||
|
{
|
||||||
|
auto transaction = tokens.begin_transaction();
|
||||||
|
|
||||||
|
switch (syntax_node.type()) {
|
||||||
|
case SyntaxNode::NodeType::Universal:
|
||||||
|
if (auto declaration_value = parse_declaration_value(tokens); declaration_value.has_value()) {
|
||||||
|
transaction.commit();
|
||||||
|
return UnresolvedStyleValue::create(declaration_value.release_value());
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
case SyntaxNode::NodeType::Ident: {
|
||||||
|
auto const& ident_node = as<IdentSyntaxNode>(syntax_node);
|
||||||
|
tokens.discard_whitespace();
|
||||||
|
if (tokens.consume_a_token().is_ident(ident_node.ident())) {
|
||||||
|
transaction.commit();
|
||||||
|
if (auto keyword = keyword_from_string(ident_node.ident()); keyword.has_value())
|
||||||
|
return CSSKeywordValue::create(keyword.release_value());
|
||||||
|
return CustomIdentStyleValue::create(ident_node.ident());
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
case SyntaxNode::NodeType::Type: {
|
||||||
|
auto const& type_node = as<TypeSyntaxNode>(syntax_node);
|
||||||
|
auto const& type_name = type_node.type_name();
|
||||||
|
if (auto value_type = value_type_from_string(type_name); value_type.has_value()) {
|
||||||
|
if (auto result = parse_value(*value_type, tokens)) {
|
||||||
|
transaction.commit();
|
||||||
|
return result.release_nonnull();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Couldn't parse `<{}>` because we don't know what it is.", type_name);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
case SyntaxNode::NodeType::Multiplier: {
|
||||||
|
auto const& multiplier_node = as<MultiplierSyntaxNode>(syntax_node);
|
||||||
|
StyleValueVector values;
|
||||||
|
tokens.discard_whitespace();
|
||||||
|
while (tokens.has_next_token()) {
|
||||||
|
auto parsed_child = parse_according_to_syntax_node(tokens, multiplier_node.child(), element);
|
||||||
|
if (!parsed_child)
|
||||||
|
break;
|
||||||
|
values.append(parsed_child.release_nonnull());
|
||||||
|
tokens.discard_whitespace();
|
||||||
|
}
|
||||||
|
if (values.is_empty())
|
||||||
|
return nullptr;
|
||||||
|
transaction.commit();
|
||||||
|
return StyleValueList::create(move(values), StyleValueList::Separator::Space);
|
||||||
|
}
|
||||||
|
case SyntaxNode::NodeType::CommaSeparatedMultiplier: {
|
||||||
|
auto const& multiplier_node = as<CommaSeparatedMultiplierSyntaxNode>(syntax_node);
|
||||||
|
auto result = parse_comma_separated_value_list(tokens, [&](auto& tokens) {
|
||||||
|
return parse_according_to_syntax_node(tokens, multiplier_node.child(), element);
|
||||||
|
});
|
||||||
|
if (!result)
|
||||||
|
return nullptr;
|
||||||
|
transaction.commit();
|
||||||
|
return result.release_nonnull();
|
||||||
|
}
|
||||||
|
case SyntaxNode::NodeType::Alternatives: {
|
||||||
|
auto const& alternatives_node = as<AlternativesSyntaxNode>(syntax_node);
|
||||||
|
for (auto const& child : alternatives_node.children()) {
|
||||||
|
if (auto result = parse_according_to_syntax_node(tokens, *child, element)) {
|
||||||
|
transaction.commit();
|
||||||
|
return result.release_nonnull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-values-5/#parse-with-a-syntax
|
||||||
|
NonnullRefPtr<CSSStyleValue const> Parser::parse_with_a_syntax(Vector<ComponentValue> const& input, SyntaxNode const& syntax, Optional<DOM::AbstractElement> const& element)
|
||||||
|
{
|
||||||
|
// 1. Parse a list of component values from values, and let raw parse be the result.
|
||||||
|
// NB: Already done before this point.
|
||||||
|
|
||||||
|
// FIXME: 2. If el was given, substitute arbitrary substitution functions in raw parse, and set raw parse to that result.
|
||||||
|
// NB: This is currently a no-op because our only caller already substitutes ASFs in the input before calling us.
|
||||||
|
// FIXME: Move substitute_arbitrary_substitution_functions() into the Parser, and keep the guarded contexts there,
|
||||||
|
// so we don't have this awkward situation of needing to pass that to random other functions.
|
||||||
|
|
||||||
|
// 3. parse values according to syntax, with a * value treated as <declaration-value>?, and let parsed result be
|
||||||
|
// the result.
|
||||||
|
// If syntax used a | combinator, let parsed result be the parse result from the first matching clause.
|
||||||
|
TokenStream tokens { input };
|
||||||
|
auto parsed_result = parse_according_to_syntax_node(tokens, syntax, element);
|
||||||
|
tokens.discard_whitespace();
|
||||||
|
|
||||||
|
// 4. If parsed result is failure, return the guaranteed-invalid value.
|
||||||
|
if (!parsed_result || tokens.has_next_token())
|
||||||
|
return GuaranteedInvalidStyleValue::create();
|
||||||
|
|
||||||
|
// 5. Assert: parsed result is now a well-defined list of one or more CSS values, since each branch of a <syntax>
|
||||||
|
// defines an unambiguous parse result (or the * syntax is unambiguous on its own).
|
||||||
|
// NB: Nothing to do.
|
||||||
|
|
||||||
|
// 6. Return parsed result.
|
||||||
|
return parsed_result.release_nonnull();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,13 @@
|
||||||
|
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
#include <LibWeb/DOM/AbstractElement.h>
|
||||||
#include <LibWeb/Forward.h>
|
#include <LibWeb/Forward.h>
|
||||||
|
|
||||||
namespace Web::CSS::Parser {
|
namespace Web::CSS::Parser {
|
||||||
|
|
||||||
OwnPtr<SyntaxNode> parse_as_syntax(Vector<ComponentValue> const&);
|
OwnPtr<SyntaxNode> parse_as_syntax(Vector<ComponentValue> const&);
|
||||||
|
|
||||||
|
NonnullRefPtr<CSSStyleValue const> parse_with_a_syntax(ParsingParams const&, Vector<ComponentValue> const& input, SyntaxNode const& syntax, Optional<DOM::AbstractElement> const& element = {});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -351,6 +351,7 @@ struct AtRule;
|
||||||
struct Declaration;
|
struct Declaration;
|
||||||
struct Function;
|
struct Function;
|
||||||
struct GuaranteedInvalidValue;
|
struct GuaranteedInvalidValue;
|
||||||
|
struct ParsingParams;
|
||||||
struct QualifiedRule;
|
struct QualifiedRule;
|
||||||
struct SimpleBlock;
|
struct SimpleBlock;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue