mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-31 21:29:06 +00:00
LibWeb: Parse @property
CSS directives
This is not a complete parse, as it doesn't validate or take into account the parsed syntax. Enough to get us a few more WPT tests though :)
This commit is contained in:
parent
50d64b0fb7
commit
a4c72f50c0
Notes:
github-actions[bot]
2024-10-23 05:56:40 +00:00
Author: https://github.com/a-ungurianu
Commit: a4c72f50c0
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1777
Reviewed-by: https://github.com/ADKaster
Reviewed-by: https://github.com/AtkinsSJ ✅
6 changed files with 178 additions and 0 deletions
|
@ -0,0 +1 @@
|
|||
Number of parsed css rules: 1 (expected: 1)
|
|
@ -0,0 +1,6 @@
|
|||
@property rule syntax value: *
|
||||
@property rule syntax value: *
|
||||
@property rule inherits value: false
|
||||
@property rule inherits value: false
|
||||
@property rule initialValue value: blue
|
||||
@property rule initialValue value: blue
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
@property badname {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: blue;
|
||||
}
|
||||
|
||||
@property --extra-syntax-tokens {
|
||||
syntax: "*" "bad";
|
||||
inherits: false;
|
||||
initial-value: blue;
|
||||
}
|
||||
|
||||
@property --extra-inherits-tokens {
|
||||
syntax: "*";
|
||||
inherits: false "bad";
|
||||
initial-value: blue;
|
||||
}
|
||||
|
||||
@property --missing-syntax {
|
||||
inherits: false;
|
||||
initial-value: blue;
|
||||
}
|
||||
|
||||
@property --missing-inherits {
|
||||
syntax: "*";
|
||||
initial-value: blue;
|
||||
}
|
||||
|
||||
@property --valid {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
</style>
|
||||
<div>This text shouldn't be visible</div>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
const cssRuleCount = document.styleSheets[0].cssRules.length;
|
||||
|
||||
println(`Number of parsed css rules: ${cssRuleCount} (expected: 1)`);
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
@property --tester {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: blue;
|
||||
}
|
||||
</style>
|
||||
<div>This text shouldn't be visible</div>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
const propertyRule = document.styleSheets[0].cssRules[0];
|
||||
println(`@property rule syntax value: ${propertyRule.syntax}`);
|
||||
propertyRule.syntax = "<color> | none";
|
||||
println(`@property rule syntax value: ${propertyRule.syntax}`);
|
||||
|
||||
println(`@property rule inherits value: ${propertyRule.inherits}`);
|
||||
propertyRule.inherits = true;
|
||||
println(`@property rule inherits value: ${propertyRule.inherits}`);
|
||||
|
||||
println(`@property rule initialValue value: ${propertyRule.initialValue}`);
|
||||
propertyRule.initialValue = "red";
|
||||
println(`@property rule initialValue value: ${propertyRule.initialValue}`);
|
||||
});
|
||||
</script>
|
|
@ -26,6 +26,7 @@
|
|||
#include <LibWeb/CSS/CSSMediaRule.h>
|
||||
#include <LibWeb/CSS/CSSNamespaceRule.h>
|
||||
#include <LibWeb/CSS/CSSNestedDeclarations.h>
|
||||
#include <LibWeb/CSS/CSSPropertyRule.h>
|
||||
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
||||
#include <LibWeb/CSS/CSSStyleRule.h>
|
||||
#include <LibWeb/CSS/CSSStyleSheet.h>
|
||||
|
@ -1362,6 +1363,9 @@ JS::GCPtr<CSSRule> Parser::convert_to_rule(Rule const& rule, Nested nested)
|
|||
if (at_rule.name.equals_ignoring_ascii_case("supports"sv))
|
||||
return convert_to_supports_rule(at_rule, nested);
|
||||
|
||||
if (at_rule.name.equals_ignoring_ascii_case("property"sv))
|
||||
return convert_to_property_rule(at_rule);
|
||||
|
||||
// FIXME: More at rules!
|
||||
dbgln_if(CSS_PARSER_DEBUG, "Unrecognized CSS at-rule: @{}", at_rule.name);
|
||||
return {};
|
||||
|
@ -1753,6 +1757,102 @@ JS::GCPtr<CSSSupportsRule> Parser::convert_to_supports_rule(AtRule const& rule,
|
|||
auto rule_list = CSSRuleList::create(m_context.realm(), child_rules);
|
||||
return CSSSupportsRule::create(m_context.realm(), supports.release_nonnull(), rule_list);
|
||||
}
|
||||
JS::GCPtr<CSSPropertyRule> Parser::convert_to_property_rule(AtRule const& rule)
|
||||
{
|
||||
// https://drafts.css-houdini.org/css-properties-values-api-1/#at-ruledef-property
|
||||
// @property <custom-property-name> {
|
||||
// <declaration-list>
|
||||
// }
|
||||
|
||||
if (rule.prelude.is_empty()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse @property rule: Empty prelude.");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto prelude_stream = TokenStream { rule.prelude };
|
||||
prelude_stream.discard_whitespace();
|
||||
auto const& token = prelude_stream.consume_a_token();
|
||||
if (!token.is_token()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @property has invalid prelude, prelude = {}; discarding.", rule.prelude);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto name_token = token.token();
|
||||
prelude_stream.discard_whitespace();
|
||||
|
||||
if (prelude_stream.has_next_token()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @property has invalid prelude, prelude = {}; discarding.", rule.prelude);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!name_token.is(Token::Type::Ident)) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @property name is invalid: {}; discarding.", name_token.to_debug_string());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!is_a_custom_property_name_string(name_token.ident())) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @property name doesn't start with '--': {}; discarding.", name_token.ident());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const& name = name_token.ident();
|
||||
|
||||
Optional<FlyString> syntax_maybe;
|
||||
Optional<bool> inherits_maybe;
|
||||
Optional<String> initial_value_maybe;
|
||||
|
||||
rule.for_each_as_declaration_list([&](auto& declaration) {
|
||||
if (declaration.name.equals_ignoring_ascii_case("syntax"sv)) {
|
||||
TokenStream token_stream { declaration.value };
|
||||
token_stream.discard_whitespace();
|
||||
|
||||
auto const& syntax_token = token_stream.consume_a_token();
|
||||
if (syntax_token.is(Token::Type::String)) {
|
||||
token_stream.discard_whitespace();
|
||||
if (token_stream.has_next_token()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Unexpected trailing tokens in syntax");
|
||||
} else {
|
||||
syntax_maybe = syntax_token.token().string();
|
||||
}
|
||||
} else {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Unexpected value for @property \"syntax\": {}; discarding.", declaration.to_string());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (declaration.name.equals_ignoring_ascii_case("inherits"sv)) {
|
||||
TokenStream token_stream { declaration.value };
|
||||
token_stream.discard_whitespace();
|
||||
|
||||
auto const& inherits_token = token_stream.consume_a_token();
|
||||
if (inherits_token.is_ident("true"sv) || inherits_token.is_ident("false"sv)) {
|
||||
auto const& ident = inherits_token.token().ident();
|
||||
token_stream.discard_whitespace();
|
||||
if (token_stream.has_next_token()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Unexpected trailing tokens in inherits");
|
||||
} else {
|
||||
inherits_maybe = (ident == "true");
|
||||
}
|
||||
} else {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Expected true/false for @property \"inherits\" value, got: {}; discarding.", inherits_token.to_debug_string());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (declaration.name.equals_ignoring_ascii_case("initial-value"sv)) {
|
||||
// FIXME: Ensure that the initial value matches the syntax, and parse the correct CSSValue out
|
||||
StringBuilder initial_value_sb;
|
||||
for (auto const& component : declaration.value) {
|
||||
initial_value_sb.append(component.to_string());
|
||||
}
|
||||
initial_value_maybe = MUST(initial_value_sb.to_string());
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (syntax_maybe.has_value() && inherits_maybe.has_value()) {
|
||||
return CSSPropertyRule::create(m_context.realm(), name, syntax_maybe.value(), inherits_maybe.value(), std::move(initial_value_maybe));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Parser::PropertiesAndCustomProperties Parser::extract_properties(Vector<RuleOrListOfDeclarations> const& rules_and_lists_of_declarations)
|
||||
{
|
||||
|
|
|
@ -187,6 +187,7 @@ private:
|
|||
JS::GCPtr<CSSMediaRule> convert_to_media_rule(AtRule const&, Nested);
|
||||
JS::GCPtr<CSSNamespaceRule> convert_to_namespace_rule(AtRule const&);
|
||||
JS::GCPtr<CSSSupportsRule> convert_to_supports_rule(AtRule const&, Nested);
|
||||
JS::GCPtr<CSSPropertyRule> convert_to_property_rule(AtRule const& rule);
|
||||
|
||||
PropertyOwningCSSStyleDeclaration* convert_to_style_declaration(Vector<Declaration> const&);
|
||||
Optional<StyleProperty> convert_to_style_property(Declaration const&);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue