mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
LibWeb: Only allow ASFs in descriptor values if explicitly supported
`@function` descriptors are the only ones that support ASFs, while most descriptors enforce this through their syntaxes implicitly disallowing ASFs, this wasn't the case for `@property/initial-value`. We now explictly disallow ASFs unless they are marked as allowed within `Descriptors.json`.
This commit is contained in:
Notes:
github-actions[bot]
2026-03-30 18:58:58 +00:00
Author: https://github.com/Calme1709 Commit: https://github.com/LadybirdBrowser/ladybird/commit/071b000d9f4 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8653 Reviewed-by: https://github.com/AtkinsSJ ✅
@@ -183,21 +183,23 @@ Each at-rule object has the following fields. Both are required.
|
||||
|
||||
Each descriptor object can have the following fields:
|
||||
|
||||
| Field | Required | Description |
|
||||
|--------------------|----------|-----------------------------------------------------------------------|
|
||||
| `initial` | No | String. The descriptor's initial value if none is provided. |
|
||||
| `legacy-alias-for` | No | String. The name of a different descriptor that this is an alias for. |
|
||||
| `syntax` | Yes | Array of strings. Each string is one option, taken from the spec. |
|
||||
| `FIXME` or `NOTE` | No | Strings, for when you want to leave a note. |
|
||||
| Field | Default | Required | Description |
|
||||
|------------------------------------------|---------|----------|----------------------------------------------------------------------------|
|
||||
| `allow-arbitrary-substitution-functions` | `false` | No | Boolean. Whether this descriptor supports arbitrary substitution functions |
|
||||
| `initial` | Nothing | No | String. The descriptor's initial value if none is provided. |
|
||||
| `legacy-alias-for` | Nothing | No | String. The name of a different descriptor that this is an alias for. |
|
||||
| `syntax` | N/A | Yes | Array of strings. Each string is one option, taken from the spec. |
|
||||
| `FIXME` or `NOTE` | Nothing | No | Strings, for when you want to leave a note. |
|
||||
|
||||
### Custom descriptor fields
|
||||
|
||||
Each custom descriptor object has the following fields
|
||||
|
||||
| Field | Required | Description |
|
||||
|--------------------|----------|----------------------------------------------|
|
||||
| `syntax` | Yes | Array of strings. Each string is one option. |
|
||||
| `FIXME` or `NOTE` | No | Strings, for when you want to leave a note. |
|
||||
| Field | Default | Required | Description |
|
||||
|------------------------------------------|---------|----------|----------------------------------------------------------------------------|
|
||||
| `allow-arbitrary-substitution-functions` | `false` | No | Boolean. Whether this descriptor supports arbitrary substitution functions |
|
||||
| `syntax` | N/A | Yes | Array of strings. Each string is one option. |
|
||||
| `FIXME` or `NOTE` | Nothing | No | Strings, for when you want to leave a note. |
|
||||
|
||||
## Keywords.json
|
||||
|
||||
|
||||
@@ -261,12 +261,14 @@
|
||||
"spec": "https://drafts.csswg.org/css-mixins-1/#function-rule",
|
||||
"descriptors": {
|
||||
"result": {
|
||||
"allow-arbitrary-substitution-functions": true,
|
||||
"syntax": [
|
||||
"<declaration-value>?"
|
||||
]
|
||||
}
|
||||
},
|
||||
"custom-descriptors": {
|
||||
"allow-arbitrary-substitution-functions": true,
|
||||
"syntax": [
|
||||
"<declaration-value>?"
|
||||
]
|
||||
|
||||
@@ -36,20 +36,46 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_descriptor_v
|
||||
|
||||
auto transaction = tokens.begin_transaction();
|
||||
|
||||
auto descriptor_value_start_index = tokens.current_index();
|
||||
SubstitutionFunctionsPresence substitution_functions_presence {};
|
||||
|
||||
tokens.mark();
|
||||
while (tokens.has_next_token()) {
|
||||
auto const& token = tokens.consume_a_token();
|
||||
|
||||
if (token.is(Token::Type::Semicolon))
|
||||
return ParseError::SyntaxError;
|
||||
|
||||
collect_arbitrary_substitution_function_presence(token, substitution_functions_presence);
|
||||
}
|
||||
|
||||
auto metadata = get_descriptor_metadata(at_rule_id, descriptor_name_and_id.id());
|
||||
|
||||
if (substitution_functions_presence.has_any()) {
|
||||
// https://drafts.csswg.org/css-values-5/#resolve-property
|
||||
// Unless otherwise specified, arbitrary substitution functions can be used in place of any part of any
|
||||
// property’s value (including within other functional notations); and are not valid in any other context.
|
||||
|
||||
// NB: Since we are not in a property value context we only allow ASFs if they are explicitly allowed in
|
||||
// Descriptors.json
|
||||
if (!metadata.allow_arbitrary_substitution_functions) {
|
||||
ErrorReporter::the().report(InvalidValueError {
|
||||
.value_type = MUST(String::formatted("{}/{}", to_string(at_rule_id), descriptor_name_and_id.name())),
|
||||
.value_string = tokens.dump_string(),
|
||||
.description = "ASFs are not supported in this descriptor"_string,
|
||||
});
|
||||
return ParseError::SyntaxError;
|
||||
}
|
||||
|
||||
return UnresolvedStyleValue::create(Vector<ComponentValue> { tokens.tokens_since(descriptor_value_start_index) }, substitution_functions_presence);
|
||||
}
|
||||
|
||||
tokens.restore_a_mark();
|
||||
|
||||
Optional<ComputationContext> computation_context = m_document
|
||||
? ComputationContext { .length_resolution_context = Length::ResolutionContext::for_document(*m_document) }
|
||||
: Optional<ComputationContext> {};
|
||||
|
||||
auto metadata = get_descriptor_metadata(at_rule_id, descriptor_name_and_id.id());
|
||||
for (auto const& option : metadata.syntax) {
|
||||
auto syntax_transaction = transaction.create_child();
|
||||
auto parsed_style_value = option.visit(
|
||||
@@ -61,10 +87,8 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_descriptor_v
|
||||
if (value_or_error.is_error())
|
||||
return nullptr;
|
||||
auto value_for_property = value_or_error.release_value();
|
||||
// Descriptors don't accept the following, which properties do:
|
||||
// - CSS-wide keywords
|
||||
// - Arbitrary substitution functions (so, UnresolvedStyleValue)
|
||||
if (value_for_property->is_css_wide_keyword() || value_for_property->is_unresolved())
|
||||
// Descriptors don't accept the CSS-wide keywords
|
||||
if (value_for_property->is_css_wide_keyword())
|
||||
return nullptr;
|
||||
return value_for_property;
|
||||
},
|
||||
|
||||
@@ -138,6 +138,7 @@ struct DescriptorMetadata {
|
||||
UnicodeRangeTokens,
|
||||
};
|
||||
Vector<Variant<Keyword, PropertyID, ValueType>> syntax;
|
||||
bool allow_arbitrary_substitution_functions { false };
|
||||
};
|
||||
|
||||
DescriptorMetadata get_descriptor_metadata(AtRuleID, DescriptorID);
|
||||
@@ -468,7 +469,11 @@ DescriptorMetadata get_descriptor_metadata(AtRuleID at_rule_id, DescriptorID des
|
||||
|
||||
generate_syntax_list(descriptor_generator, syntax);
|
||||
|
||||
descriptor_generator.set("allow-arbitrary-substitution-functions"sv, descriptor.get_bool("allow-arbitrary-substitution-functions"sv).value_or(false) ? "true" : "false");
|
||||
|
||||
descriptor_generator.append(R"~~~(
|
||||
metadata.allow_arbitrary_substitution_functions = @allow-arbitrary-substitution-functions@;
|
||||
|
||||
return metadata;
|
||||
}
|
||||
)~~~");
|
||||
@@ -484,7 +489,12 @@ DescriptorMetadata get_descriptor_metadata(AtRuleID at_rule_id, DescriptorID des
|
||||
)~~~");
|
||||
auto const& syntax = custom_descriptors.get_array("syntax"sv).value();
|
||||
generate_syntax_list(custom_descriptor_generator, syntax);
|
||||
|
||||
custom_descriptor_generator.set("allow-arbitrary-substitution-functions"sv, custom_descriptors.get_bool("allow-arbitrary-substitution-functions"sv).value_or(false) ? "true" : "false");
|
||||
|
||||
custom_descriptor_generator.append(R"~~~(
|
||||
metadata.allow_arbitrary_substitution_functions = @allow-arbitrary-substitution-functions@;
|
||||
|
||||
return metadata;
|
||||
}
|
||||
)~~~");
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
@function --foo(--bar) { --baz: var(--bar); result: var(--baz); }
|
||||
@@ -0,0 +1 @@
|
||||
@property --foo { syntax: "*"; inherits: true; }
|
||||
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
@function --foo(--bar) {
|
||||
--baz: var(--bar);
|
||||
result: var(--baz);
|
||||
}
|
||||
</style>
|
||||
<div id="target"></div>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
println(document.styleSheets[0].cssRules[0].cssText);
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
@property --foo {
|
||||
syntax: "*";
|
||||
inherits: true;
|
||||
initial-value: var(foo);
|
||||
}
|
||||
</style>
|
||||
<div id="target"></div>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
println(document.styleSheets[0].cssRules[0].cssText);
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user