LibWeb: Limit <ident> to <custom-ident> in @property/syntax
The definition of syntax in the "css-properties-values-api" spec (which is used for the `@property/syntax` descriptor) is slightly different from the definition of `<syntax>` in the "css-values" spec (which we implement) in that it limits literal idents to exclusively `<custom-ident>`s (i.e. not CSS-wide keywords or "default"). `<custom-ident>`s are also case-sensitive so that behavior is implemented for syntax matching here as well
This commit is contained in:
parent
cfc2e64b4b
commit
6afe2ff27b
8 changed files with 51 additions and 31 deletions
|
|
@ -83,7 +83,7 @@ WebIDL::ExceptionOr<void> register_property(JS::VM& vm, PropertyDefinition defin
|
|||
// 3. Attempt to consume a syntax definition from syntax. If it returns failure, throw a SyntaxError.
|
||||
// Otherwise, let syntax definition be the returned syntax definition.
|
||||
auto syntax_component_values = parse_component_values_list(parsing_params, definition.syntax);
|
||||
auto maybe_syntax = parse_as_syntax(syntax_component_values);
|
||||
auto maybe_syntax = parse_as_syntax(syntax_component_values, Parser::LimitSingleComponentIdentToCustomIdent::Yes);
|
||||
if (!maybe_syntax) {
|
||||
return WebIDL::SyntaxError::create(realm, "Invalid syntax definition"_utf16);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -793,7 +793,7 @@ GC::Ptr<CSSPropertyRule> Parser::convert_to_property_rule(AtRule const& rule)
|
|||
parsing_params = CSS::Parser::ParsingParams { realm() };
|
||||
|
||||
auto syntax_component_values = parse_component_values_list(parsing_params, syntax_maybe.value());
|
||||
auto maybe_syntax = parse_as_syntax(syntax_component_values);
|
||||
auto maybe_syntax = parse_as_syntax(syntax_component_values, LimitSingleComponentIdentToCustomIdent::Yes);
|
||||
|
||||
// If the provided string is not a valid syntax string (if it returns failure when consume
|
||||
// a syntax definition is called on it), the descriptor is invalid and must be ignored.
|
||||
|
|
|
|||
|
|
@ -59,9 +59,10 @@ void TypeSyntaxNode::dump(StringBuilder& builder, int indent) const
|
|||
builder.appendff("{: >{}}Type: {}\n", "", indent, m_type_name);
|
||||
}
|
||||
|
||||
IdentSyntaxNode::IdentSyntaxNode(FlyString ident)
|
||||
IdentSyntaxNode::IdentSyntaxNode(FlyString ident, CaseSensitivity case_sensitivity)
|
||||
: SyntaxNode(NodeType::Ident)
|
||||
, m_ident(move(ident))
|
||||
, m_case_sensitivity(case_sensitivity)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,20 +62,22 @@ private:
|
|||
// 'foo'
|
||||
class IdentSyntaxNode final : public SyntaxNode {
|
||||
public:
|
||||
static NonnullOwnPtr<IdentSyntaxNode> create(FlyString ident)
|
||||
static NonnullOwnPtr<IdentSyntaxNode> create(FlyString ident, CaseSensitivity case_sensitivity)
|
||||
{
|
||||
return adopt_own(*new IdentSyntaxNode(move(ident)));
|
||||
return adopt_own(*new IdentSyntaxNode(move(ident), case_sensitivity));
|
||||
}
|
||||
|
||||
virtual ~IdentSyntaxNode() override;
|
||||
FlyString const& ident() const { return m_ident; }
|
||||
CaseSensitivity case_sensitivity() const { return m_case_sensitivity; }
|
||||
|
||||
virtual String to_string() const override;
|
||||
virtual void dump(StringBuilder&, int indent) const override;
|
||||
|
||||
private:
|
||||
IdentSyntaxNode(FlyString);
|
||||
IdentSyntaxNode(FlyString, CaseSensitivity);
|
||||
FlyString m_ident;
|
||||
CaseSensitivity m_case_sensitivity;
|
||||
};
|
||||
|
||||
// '<foo>'
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
namespace Web::CSS::Parser {
|
||||
|
||||
static OwnPtr<SyntaxNode> parse_syntax_single_component(TokenStream<ComponentValue>& tokens)
|
||||
static OwnPtr<SyntaxNode> parse_syntax_single_component(TokenStream<ComponentValue>& tokens, LimitSingleComponentIdentToCustomIdent limit_single_component_ident_to_custom_ident)
|
||||
{
|
||||
// <syntax-single-component> = '<' <syntax-type-name> '>' | <ident>
|
||||
// <syntax-type-name> = angle | color | custom-ident | image | integer
|
||||
|
|
@ -34,8 +34,14 @@ static OwnPtr<SyntaxNode> parse_syntax_single_component(TokenStream<ComponentVal
|
|||
// <ident>
|
||||
if (tokens.next_token().is(Token::Type::Ident)) {
|
||||
auto ident = tokens.consume_a_token().token().ident();
|
||||
|
||||
// AD-HOC: Some users (i.e. the @property syntax descriptor) only allow custom idents here,
|
||||
// https://github.com/w3c/csswg-drafts/issues/13614
|
||||
if (limit_single_component_ident_to_custom_ident == LimitSingleComponentIdentToCustomIdent::Yes && !is_valid_custom_ident(ident, {}))
|
||||
return {};
|
||||
|
||||
transaction.commit();
|
||||
return IdentSyntaxNode::create(move(ident));
|
||||
return IdentSyntaxNode::create(ident, limit_single_component_ident_to_custom_ident == LimitSingleComponentIdentToCustomIdent::Yes ? CaseSensitivity::CaseSensitive : CaseSensitivity::CaseInsensitive);
|
||||
}
|
||||
|
||||
// '<' <syntax-type-name> '>'
|
||||
|
|
@ -82,7 +88,7 @@ static Optional<char> parse_syntax_multiplier(TokenStream<ComponentValue>& token
|
|||
return {};
|
||||
}
|
||||
|
||||
static OwnPtr<SyntaxNode> parse_syntax_component(TokenStream<ComponentValue>& tokens)
|
||||
static OwnPtr<SyntaxNode> parse_syntax_component(TokenStream<ComponentValue>& tokens, LimitSingleComponentIdentToCustomIdent limit_single_component_ident_to_custom_ident)
|
||||
{
|
||||
// <syntax-component> = <syntax-single-component> <syntax-multiplier>?
|
||||
// | '<' transform-list '>'
|
||||
|
|
@ -105,7 +111,7 @@ static OwnPtr<SyntaxNode> parse_syntax_component(TokenStream<ComponentValue>& to
|
|||
}
|
||||
|
||||
// <syntax-single-component> <syntax-multiplier>?
|
||||
auto syntax_single_component = parse_syntax_single_component(tokens);
|
||||
auto syntax_single_component = parse_syntax_single_component(tokens, limit_single_component_ident_to_custom_ident);
|
||||
if (!syntax_single_component)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -143,7 +149,7 @@ static Optional<char> parse_syntax_combinator(TokenStream<ComponentValue>& token
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/css-values-5/#typedef-syntax
|
||||
OwnPtr<SyntaxNode> parse_as_syntax(Vector<ComponentValue> const& component_values)
|
||||
OwnPtr<SyntaxNode> parse_as_syntax(Vector<ComponentValue> const& component_values, LimitSingleComponentIdentToCustomIdent limit_single_component_ident_to_custom_ident)
|
||||
{
|
||||
// <syntax> = '*' | <syntax-component> [ <syntax-combinator> <syntax-component> ]* | <syntax-string>
|
||||
// <syntax-component> = <syntax-single-component> <syntax-multiplier>?
|
||||
|
|
@ -182,11 +188,11 @@ OwnPtr<SyntaxNode> parse_as_syntax(Vector<ComponentValue> const& component_value
|
|||
return nullptr;
|
||||
|
||||
auto child_component_values = Parser::create(ParsingParams {}, string).parse_as_list_of_component_values();
|
||||
return parse_as_syntax(child_component_values);
|
||||
return parse_as_syntax(child_component_values, limit_single_component_ident_to_custom_ident);
|
||||
}
|
||||
|
||||
// <syntax-component> [ <syntax-combinator> <syntax-component> ]*
|
||||
auto first = parse_syntax_component(tokens);
|
||||
auto first = parse_syntax_component(tokens, limit_single_component_ident_to_custom_ident);
|
||||
if (!first)
|
||||
return nullptr;
|
||||
Vector<NonnullOwnPtr<SyntaxNode>> syntax_components;
|
||||
|
|
@ -196,7 +202,7 @@ OwnPtr<SyntaxNode> parse_as_syntax(Vector<ComponentValue> const& component_value
|
|||
while (tokens.has_next_token()) {
|
||||
auto combinator = parse_syntax_combinator(tokens);
|
||||
tokens.discard_whitespace();
|
||||
auto component = parse_syntax_component(tokens);
|
||||
auto component = parse_syntax_component(tokens, limit_single_component_ident_to_custom_ident);
|
||||
tokens.discard_whitespace();
|
||||
if (!combinator.has_value() || !component) {
|
||||
dbgln("Failed parsing syntax portion, combinator = `{}`, component = `{}`", combinator, component);
|
||||
|
|
@ -234,7 +240,14 @@ RefPtr<StyleValue const> Parser::parse_according_to_syntax_node(TokenStream<Comp
|
|||
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())) {
|
||||
auto token = tokens.consume_a_token();
|
||||
|
||||
if (!token.is(Token::Type::Ident))
|
||||
return nullptr;
|
||||
|
||||
auto ident = token.token().ident();
|
||||
|
||||
if (ident_node.case_sensitivity() == CaseSensitivity::CaseSensitive ? ident == ident_node.ident() : ident.equals_ignoring_ascii_case(ident_node.ident())) {
|
||||
transaction.commit();
|
||||
if (auto keyword = keyword_from_string(ident_node.ident()); keyword.has_value())
|
||||
return KeywordStyleValue::create(keyword.release_value());
|
||||
|
|
|
|||
|
|
@ -14,7 +14,11 @@
|
|||
|
||||
namespace Web::CSS::Parser {
|
||||
|
||||
WEB_API OwnPtr<SyntaxNode> parse_as_syntax(Vector<ComponentValue> const&);
|
||||
enum class LimitSingleComponentIdentToCustomIdent : u8 {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
WEB_API OwnPtr<SyntaxNode> parse_as_syntax(Vector<ComponentValue> const&, LimitSingleComponentIdentToCustomIdent = LimitSingleComponentIdentToCustomIdent::No);
|
||||
|
||||
NonnullRefPtr<StyleValue const> parse_with_a_syntax(ParsingParams const&, Vector<ComponentValue> const& input, SyntaxNode const& syntax);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
|||
|
||||
Found 106 tests
|
||||
|
||||
100 Pass
|
||||
6 Fail
|
||||
102 Pass
|
||||
4 Fail
|
||||
Pass Attribute 'syntax' returns expected value for ["<color>"]
|
||||
Pass Attribute 'syntax' returns expected value for ["<color> | none"]
|
||||
Pass Attribute 'syntax' returns expected value for ["<color># | <image> | none"]
|
||||
|
|
@ -18,8 +18,8 @@ Pass Attribute 'syntax' returns expected value for ["red"]
|
|||
Pass Attribute 'syntax' makes the @property rule invalid for ["rgb(255, 0, 0)"]
|
||||
Pass Attribute 'syntax' makes the @property rule invalid for [<color>]
|
||||
Pass Attribute 'syntax' makes the @property rule invalid for [foo | bar]
|
||||
Fail Attribute 'syntax' makes the @property rule invalid for ["default"]
|
||||
Fail Attribute 'syntax' makes the @property rule invalid for ["Default"]
|
||||
Pass Attribute 'syntax' makes the @property rule invalid for ["default"]
|
||||
Pass Attribute 'syntax' makes the @property rule invalid for ["Default"]
|
||||
Pass Attribute 'syntax' makes the @property rule invalid for ["initial"]
|
||||
Pass Attribute 'syntax' makes the @property rule invalid for ["Initial"]
|
||||
Pass Attribute 'syntax' makes the @property rule invalid for ["inherit"]
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
|||
|
||||
Found 239 tests
|
||||
|
||||
217 Pass
|
||||
22 Fail
|
||||
226 Pass
|
||||
13 Fail
|
||||
Pass syntax:'*', initialValue:'a' is valid
|
||||
Pass syntax:' * ', initialValue:'b' is valid
|
||||
Pass syntax:'<length>', initialValue:'2px' is valid
|
||||
|
|
@ -173,12 +173,12 @@ Pass syntax:'inherit', initialValue:'inherit' is invalid
|
|||
Pass syntax:'unset', initialValue:'unset' is invalid
|
||||
Pass syntax:'revert', initialValue:'revert' is invalid
|
||||
Pass syntax:'revert-layer', initialValue:'revert-layer' is invalid
|
||||
Fail syntax:'default', initialValue:'default' is invalid
|
||||
Fail syntax:'<length>|initial', initialValue:'10px' is invalid
|
||||
Fail syntax:'<length>|INHERIT', initialValue:'10px' is invalid
|
||||
Fail syntax:'<percentage>|unsEt', initialValue:'2%' is invalid
|
||||
Fail syntax:'<color>|REVert', initialValue:'red' is invalid
|
||||
Fail syntax:'<integer>|deFAUlt', initialValue:'1' is invalid
|
||||
Pass syntax:'default', initialValue:'default' is invalid
|
||||
Pass syntax:'<length>|initial', initialValue:'10px' is invalid
|
||||
Pass syntax:'<length>|INHERIT', initialValue:'10px' is invalid
|
||||
Pass syntax:'<percentage>|unsEt', initialValue:'2%' is invalid
|
||||
Pass syntax:'<color>|REVert', initialValue:'red' is invalid
|
||||
Pass syntax:'<integer>|deFAUlt', initialValue:'1' is invalid
|
||||
Fail syntax:'*', initialValue:'initial' is invalid
|
||||
Fail syntax:'*', initialValue:'inherit' is invalid
|
||||
Fail syntax:'*', initialValue:'unset' is invalid
|
||||
|
|
@ -205,7 +205,7 @@ Fail syntax:'*', initialValue:'url(moo '')' is invalid
|
|||
Pass syntax:'*', initialValue:'semi;colon' is invalid
|
||||
Fail syntax:'*', initialValue:'var(invalid var ref)' is invalid
|
||||
Fail syntax:'*', initialValue:'var(--foo)' is invalid
|
||||
Fail syntax:'banana', initialValue:'bAnAnA' is invalid
|
||||
Pass syntax:'banana', initialValue:'bAnAnA' is invalid
|
||||
Pass syntax:'<length>', initialValue:'var(--moo)' is invalid
|
||||
Pass syntax:'<length>', initialValue:'10' is invalid
|
||||
Pass syntax:'<length>', initialValue:'10%' is invalid
|
||||
|
|
@ -223,8 +223,8 @@ Pass syntax:'<percentage>', initialValue:'0' is invalid
|
|||
Pass syntax:'<integer>', initialValue:'1.0' is invalid
|
||||
Pass syntax:'<integer>', initialValue:'1e0' is invalid
|
||||
Pass syntax:'<number>|foo', initialValue:'foo var(--foo, bla)' is invalid
|
||||
Fail syntax:'Foo | bar', initialValue:'foo' is invalid
|
||||
Fail syntax:'Foo | bar', initialValue:'Bar' is invalid
|
||||
Pass syntax:'Foo | bar', initialValue:'foo' is invalid
|
||||
Pass syntax:'Foo | bar', initialValue:'Bar' is invalid
|
||||
Pass syntax:'<angle>', initialValue:'0' is invalid
|
||||
Pass syntax:'<angle>', initialValue:'10%' is invalid
|
||||
Pass syntax:'<time>', initialValue:'2px' is invalid
|
||||
|
|
|
|||
Loading…
Reference in a new issue