LibWeb: Parse transition property as a coordinating list shorthand

We don't need all this specific logic for parsing the `transition`
property - we also now maintain `none` as such until use time which
gains us a couple extra tests
This commit is contained in:
Callum Law 2025-10-19 19:48:23 +13:00 committed by Sam Atkins
parent 94c788f2e0
commit 8417d74328
Notes: github-actions[bot] 2025-10-23 09:11:05 +00:00
10 changed files with 27 additions and 247 deletions

View file

@ -59,7 +59,6 @@
#include <LibWeb/CSS/StyleValues/TextUnderlinePositionStyleValue.h>
#include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/CSS/StyleValues/TransitionStyleValue.h>
#include <LibWeb/CSS/StyleValues/URLStyleValue.h>
#include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h>
#include <LibWeb/Dump.h>
@ -5082,122 +5081,38 @@ RefPtr<StyleValue const> Parser::parse_transform_origin_value(TokenStream<Compon
return make_list(x_value.release_nonnull(), y_value.release_nonnull(), third_value.release_nonnull());
}
// https://drafts.csswg.org/css-transitions-2/#transition-shorthand-property
RefPtr<StyleValue const> Parser::parse_transition_value(TokenStream<ComponentValue>& tokens)
{
if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
return none;
// [ [ none | <single-transition-property> ] || <time> || <easing-function> || <time> || <transition-behavior-value> ]#
Vector<PropertyID> longhand_ids {
PropertyID::TransitionProperty,
PropertyID::TransitionDuration,
PropertyID::TransitionTimingFunction,
PropertyID::TransitionDelay,
PropertyID::TransitionBehavior
};
Vector<TransitionStyleValue::Transition> transitions;
auto transaction = tokens.begin_transaction();
auto parsed_value = parse_coordinating_value_list_shorthand(tokens, PropertyID::Transition, longhand_ids);
while (tokens.has_next_token()) {
TransitionStyleValue::Transition transition;
auto time_value_count = 0;
bool transition_behavior_found = false;
while (tokens.has_next_token() && !tokens.next_token().is(Token::Type::Comma)) {
if (auto maybe_time = parse_time(tokens); maybe_time.has_value()) {
auto time = maybe_time.release_value();
switch (time_value_count) {
case 0:
if (!time.is_calculated() && !property_accepts_time(PropertyID::TransitionDuration, time.value()))
return nullptr;
transition.duration = move(time);
break;
case 1:
if (!time.is_calculated() && !property_accepts_time(PropertyID::TransitionDelay, time.value()))
return nullptr;
transition.delay = move(time);
break;
default:
ErrorReporter::the().report(InvalidPropertyError {
.property_name = "transition"_fly_string,
.value_string = tokens.dump_string(),
.description = "Contains more than two time values"_string,
});
return {};
}
time_value_count++;
continue;
}
if (!parsed_value)
return nullptr;
if (auto easing = parse_css_value_for_property(PropertyID::TransitionTimingFunction, tokens)) {
if (transition.easing) {
ErrorReporter::the().report(InvalidPropertyError {
.property_name = "transition"_fly_string,
.value_string = tokens.dump_string(),
.description = "Contains multiple easing values"_string,
});
return {};
}
// https://drafts.csswg.org/css-transitions-1/#transition-shorthand-property
// If there is more than one <single-transition> in the shorthand, and any of the transitions has none as the
// <single-transition-property>, then the declaration is invalid.
auto const& transition_properties_style_value = parsed_value->as_shorthand().longhand(PropertyID::TransitionProperty);
transition.easing = easing;
continue;
}
// FIXME: This can be removed once parse_coordinating_value_list_shorthand returns a list for single values too.
if (!transition_properties_style_value->is_value_list())
return parsed_value;
if (!transition_behavior_found && (tokens.peek_token().is_ident("normal"sv) || tokens.peek_token().is_ident("allow-discrete"sv))) {
transition_behavior_found = true;
auto ident = tokens.consume_a_token().token().ident();
if (ident == "allow-discrete"sv)
transition.transition_behavior = TransitionBehavior::AllowDiscrete;
continue;
}
auto const& transition_properties = transition_properties_style_value->as_value_list().values();
if (auto token = tokens.peek_token(); token.is_ident("all"sv)) {
auto transition_keyword = parse_keyword_value(tokens);
VERIFY(transition_keyword->to_keyword() == Keyword::All);
if (transition.property_name) {
ErrorReporter::the().report(InvalidPropertyError {
.property_name = "transition"_fly_string,
.value_string = tokens.dump_string(),
.description = "Contains multiple property identifiers"_string,
});
return {};
}
transition.property_name = transition_keyword.release_nonnull();
continue;
}
if (transition_properties.size() > 1 && transition_properties.find_first_index_if([](auto const& transition_property) { return transition_property->to_keyword() == Keyword::None; }).has_value())
return nullptr;
if (auto transition_property = parse_custom_ident_value(tokens, { { "all"sv, "none"sv } })) {
if (transition.property_name) {
ErrorReporter::the().report(InvalidPropertyError {
.property_name = "transition"_fly_string,
.value_string = tokens.dump_string(),
.description = "Contains multiple property identifiers"_string,
});
return {};
}
auto custom_ident = transition_property->custom_ident();
if (auto property = property_id_from_string(custom_ident); property.has_value()) {
transition.property_name = CustomIdentStyleValue::create(custom_ident);
continue;
}
}
ErrorReporter::the().report(InvalidPropertyError {
.property_name = "transition"_fly_string,
.value_string = tokens.dump_string(),
.description = MUST(String::formatted("Unexpected token \"{}\"", tokens.next_token().to_string())),
});
return {};
}
if (!transition.property_name)
transition.property_name = KeywordStyleValue::create(Keyword::All);
if (!transition.easing)
transition.easing = KeywordStyleValue::create(Keyword::Ease);
transitions.append(move(transition));
if (!tokens.next_token().is(Token::Type::Comma))
break;
tokens.discard_a_token();
}
transaction.commit();
return TransitionStyleValue::create(move(transitions));
return parsed_value;
}
RefPtr<StyleValue const> Parser::parse_list_of_time_values(PropertyID property_id, TokenStream<ComponentValue>& tokens)