mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
LibWeb/CSS: Implement type(<syntax>)
in attr()
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Build Dev Container Image / build (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Build Dev Container Image / build (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
This lets the `attr()` interpret the attribute's contents as an arbitrary type instead of just as a string or number.
This commit is contained in:
parent
0a5e8c2865
commit
27a666f3b2
Notes:
github-actions[bot]
2025-07-16 13:48:58 +00:00
Author: https://github.com/AtkinsSJ
Commit: 27a666f3b2
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5400
Reviewed-by: https://github.com/tcl3 ✅
8 changed files with 204 additions and 105 deletions
|
@ -6,8 +6,11 @@
|
|||
|
||||
#include <LibWeb/CSS/Parser/ArbitrarySubstitutionFunctions.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/CSS/Parser/Syntax.h>
|
||||
#include <LibWeb/CSS/Parser/SyntaxParsing.h>
|
||||
#include <LibWeb/CSS/PropertyName.h>
|
||||
#include <LibWeb/CSS/StyleComputer.h>
|
||||
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
|
||||
|
@ -85,12 +88,16 @@ static Vector<ComponentValue> replace_an_attr_function(DOM::AbstractElement& ele
|
|||
auto const second_argument = arguments.get(1);
|
||||
|
||||
FlyString attribute_name;
|
||||
Optional<FlyString> maybe_syntax = {};
|
||||
|
||||
struct RawString { };
|
||||
Variant<Empty, NonnullOwnPtr<SyntaxNode>, RawString> syntax;
|
||||
Optional<FlyString> unit_name;
|
||||
|
||||
auto failure = [&] -> Vector<ComponentValue> {
|
||||
// This is step 6, but defined here for convenience.
|
||||
|
||||
// 1. If second arg is null, and syntax was omitted, return an empty CSS <string>.
|
||||
if (!second_argument.has_value() && !maybe_syntax.has_value())
|
||||
if (!second_argument.has_value() && syntax.has<Empty>())
|
||||
return { Token::create_string({}) };
|
||||
|
||||
// 2. If second arg is null, return the guaranteed-invalid value.
|
||||
|
@ -115,22 +122,29 @@ static Vector<ComponentValue> replace_an_attr_function(DOM::AbstractElement& ele
|
|||
first_argument_tokens.discard_whitespace();
|
||||
|
||||
// <attr-type> = type( <syntax> ) | raw-string | <attr-unit>
|
||||
// FIXME: Support type(<syntax>)
|
||||
bool is_dimension_unit = false;
|
||||
if (first_argument_tokens.next_token().is(Token::Type::Ident)) {
|
||||
auto const& syntax_ident = first_argument_tokens.next_token().token().ident();
|
||||
if (syntax_ident.equals_ignoring_ascii_case("raw-string"sv)) {
|
||||
maybe_syntax = first_argument_tokens.consume_a_token().token().ident();
|
||||
first_argument_tokens.discard_a_token(); // raw-string
|
||||
syntax = RawString {};
|
||||
} else if (syntax_ident == "%"sv
|
||||
|| Angle::unit_from_name(syntax_ident).has_value()
|
||||
|| Flex::unit_from_name(syntax_ident).has_value()
|
||||
|| Frequency::unit_from_name(syntax_ident).has_value()
|
||||
|| Length::unit_from_name(syntax_ident).has_value()
|
||||
|| Resolution::unit_from_name(syntax_ident).has_value()
|
||||
|| Time::unit_from_name(syntax_ident).has_value()) {
|
||||
syntax = TypeSyntaxNode::create("number"_fly_string).release_nonnull<SyntaxNode>();
|
||||
unit_name = first_argument_tokens.consume_a_token().token().ident();
|
||||
} else {
|
||||
is_dimension_unit = syntax_ident == "%"sv
|
||||
|| Angle::unit_from_name(syntax_ident).has_value()
|
||||
|| Flex::unit_from_name(syntax_ident).has_value()
|
||||
|| Frequency::unit_from_name(syntax_ident).has_value()
|
||||
|| Length::unit_from_name(syntax_ident).has_value()
|
||||
|| Resolution::unit_from_name(syntax_ident).has_value()
|
||||
|| Time::unit_from_name(syntax_ident).has_value();
|
||||
if (is_dimension_unit)
|
||||
maybe_syntax = first_argument_tokens.consume_a_token().token().ident();
|
||||
return failure();
|
||||
}
|
||||
} else if (first_argument_tokens.next_token().is_function("type"sv)) {
|
||||
auto const& type_function = first_argument_tokens.consume_a_token().function();
|
||||
if (auto parsed_syntax = parse_as_syntax(type_function.value)) {
|
||||
syntax = parsed_syntax.release_nonnull();
|
||||
} else {
|
||||
return failure();
|
||||
}
|
||||
}
|
||||
first_argument_tokens.discard_whitespace();
|
||||
|
@ -145,29 +159,40 @@ static Vector<ComponentValue> replace_an_attr_function(DOM::AbstractElement& ele
|
|||
|
||||
// 4. If syntax is null or the keyword raw-string, return a CSS <string> whose value is attr value.
|
||||
// NOTE: No parsing or modification of any kind is performed on the value.
|
||||
if (!maybe_syntax.has_value() || maybe_syntax->equals_ignoring_ascii_case("raw-string"sv))
|
||||
if (syntax.visit(
|
||||
[](Empty) { return true; },
|
||||
[](RawString) { return true; },
|
||||
[](auto&) { return false; })) {
|
||||
return { Token::create_string(*attribute_value) };
|
||||
auto syntax = maybe_syntax.release_value();
|
||||
}
|
||||
|
||||
// 5. Substitute arbitrary substitution functions in attr value, with «"attribute", attr name» as the substitution
|
||||
// context, then parse with a attr value, with syntax and el. If that succeeds, return the result; otherwise,
|
||||
// jump to the last step (labeled FAILURE).
|
||||
// context, then parse with a <syntax> attr value, with syntax and el. If that succeeds, return the result;
|
||||
// otherwise, jump to the last step (labeled FAILURE).
|
||||
auto parser = Parser::create(ParsingParams { element.element().document() }, attribute_value.value());
|
||||
auto unsubstituted_values = parser.parse_as_list_of_component_values();
|
||||
auto substituted_values = substitute_arbitrary_substitution_functions(element, guarded_contexts, unsubstituted_values, SubstitutionContext { SubstitutionContext::DependencyType::Attribute, attribute_name.to_string() });
|
||||
auto substituted_values = substitute_arbitrary_substitution_functions(element, guarded_contexts, unsubstituted_values,
|
||||
SubstitutionContext { SubstitutionContext::DependencyType::Attribute, attribute_name.to_string() });
|
||||
|
||||
// FIXME: Parse using the syntax. For now we just handle `<attr-unit>` here.
|
||||
TokenStream value_tokens { substituted_values };
|
||||
value_tokens.discard_whitespace();
|
||||
auto const& component_value = value_tokens.consume_a_token();
|
||||
value_tokens.discard_whitespace();
|
||||
if (value_tokens.has_next_token())
|
||||
auto parsed_value = parse_with_a_syntax(ParsingParams { element.document() }, substituted_values, *syntax.get<NonnullOwnPtr<SyntaxNode>>(), element);
|
||||
if (parsed_value->is_guaranteed_invalid())
|
||||
return failure();
|
||||
|
||||
if (component_value.is(Token::Type::Number) && is_dimension_unit)
|
||||
return { Token::create_dimension(component_value.token().number_value(), syntax) };
|
||||
if (unit_name.has_value()) {
|
||||
// https://drafts.csswg.org/css-values-5/#ref-for-typedef-attr-type%E2%91%A0
|
||||
// If given as an <attr-unit> value, the value is first parsed as if type(<number>) was specified, then the
|
||||
// resulting numeric value is turned into a dimension with the corresponding unit, or a percentage if % was
|
||||
// given. Values that fail to parse as a <number> trigger fallback.
|
||||
|
||||
return failure();
|
||||
// FIXME: The spec is ambiguous about what we should do for non-number-literals.
|
||||
// Chromium treats them as invalid, so copy that for now.
|
||||
// Spec issue: https://github.com/w3c/csswg-drafts/issues/12479
|
||||
if (!parsed_value->is_number())
|
||||
return failure();
|
||||
return { Token::create_dimension(parsed_value->as_number().number(), unit_name.release_value()) };
|
||||
}
|
||||
|
||||
return parsed_value->tokenize();
|
||||
|
||||
// 6. FAILURE:
|
||||
// NB: Step 6 is a lambda defined at the top of the function.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue