LibWeb/CSS: Introduce helper methods for parsing numeric values

"Parse a style value for <foo>", where we don't care if it's a literal
<foo> or a calculated one, is a really common thing that we previously
didn't have methods for.

A couple of methods we had have been extended to parse calc(), and the
others have been filled in.

The method for parsing the `flex` property's value is renamed
`parse_flex_shorthand_value()` as it conflicted.
This commit is contained in:
Sam Atkins 2024-08-16 09:41:10 +01:00 committed by Sam Atkins
commit 27be8678c9
Notes: github-actions[bot] 2024-08-21 09:52:50 +00:00
3 changed files with 207 additions and 30 deletions

View file

@ -2310,33 +2310,43 @@ Vector<Gfx::UnicodeRange> Parser::parse_unicode_ranges(TokenStream<ComponentValu
RefPtr<CSSStyleValue> Parser::parse_dimension_value(TokenStream<ComponentValue>& tokens) RefPtr<CSSStyleValue> Parser::parse_dimension_value(TokenStream<ComponentValue>& tokens)
{ {
auto dimension = parse_dimension(tokens.peek_token()); if (auto dimension = parse_dimension(tokens.peek_token()); dimension.has_value()) {
if (!dimension.has_value()) (void)tokens.next_token(); // dimension
return nullptr;
(void)tokens.next_token(); // dimension
if (dimension->is_angle()) if (dimension->is_angle())
return AngleStyleValue::create(dimension->angle()); return AngleStyleValue::create(dimension->angle());
if (dimension->is_frequency()) if (dimension->is_frequency())
return FrequencyStyleValue::create(dimension->frequency()); return FrequencyStyleValue::create(dimension->frequency());
if (dimension->is_length()) if (dimension->is_length())
return LengthStyleValue::create(dimension->length()); return LengthStyleValue::create(dimension->length());
if (dimension->is_percentage()) if (dimension->is_percentage())
return PercentageStyleValue::create(dimension->percentage()); return PercentageStyleValue::create(dimension->percentage());
if (dimension->is_resolution()) if (dimension->is_resolution())
return ResolutionStyleValue::create(dimension->resolution()); return ResolutionStyleValue::create(dimension->resolution());
if (dimension->is_time()) if (dimension->is_time())
return TimeStyleValue::create(dimension->time()); return TimeStyleValue::create(dimension->time());
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
}
if (auto calc = parse_calculated_value(tokens.peek_token()); calc && calc->resolves_to_dimension()) {
(void)tokens.next_token(); // calc
return calc;
}
return nullptr;
} }
RefPtr<CSSStyleValue> Parser::parse_integer_value(TokenStream<ComponentValue>& tokens) RefPtr<CSSStyleValue> Parser::parse_integer_value(TokenStream<ComponentValue>& tokens)
{ {
auto peek_token = tokens.peek_token(); auto peek_token = tokens.peek_token();
if (peek_token.is(Token::Type::Number) && peek_token.token().number().is_integer()) { if (peek_token.is(Token::Type::Number) && peek_token.token().number().is_integer()) {
(void)tokens.next_token(); (void)tokens.next_token(); // integer
return IntegerStyleValue::create(peek_token.token().number().integer_value()); return IntegerStyleValue::create(peek_token.token().number().integer_value());
} }
if (auto calc = parse_calculated_value(peek_token); calc && calc->resolves_to_number()) {
(void)tokens.next_token(); // calc
return calc;
}
return nullptr; return nullptr;
} }
@ -2345,28 +2355,181 @@ RefPtr<CSSStyleValue> Parser::parse_number_value(TokenStream<ComponentValue>& to
{ {
auto peek_token = tokens.peek_token(); auto peek_token = tokens.peek_token();
if (peek_token.is(Token::Type::Number)) { if (peek_token.is(Token::Type::Number)) {
(void)tokens.next_token(); (void)tokens.next_token(); // number
return NumberStyleValue::create(peek_token.token().number().value()); return NumberStyleValue::create(peek_token.token().number().value());
} }
if (auto calc = parse_calculated_value(peek_token); calc && calc->resolves_to_number()) {
(void)tokens.next_token(); // calc
return calc;
}
return nullptr; return nullptr;
} }
RefPtr<CSSStyleValue> Parser::parse_number_or_percentage_value(TokenStream<ComponentValue>& tokens) RefPtr<CSSStyleValue> Parser::parse_number_percentage_value(TokenStream<ComponentValue>& tokens)
{ {
auto peek_token = tokens.peek_token(); auto peek_token = tokens.peek_token();
if (peek_token.is(Token::Type::Number)) { if (peek_token.is(Token::Type::Number)) {
(void)tokens.next_token(); (void)tokens.next_token(); // number
return NumberStyleValue::create(peek_token.token().number().value()); return NumberStyleValue::create(peek_token.token().number().value());
} }
if (peek_token.is(Token::Type::Percentage)) { if (peek_token.is(Token::Type::Percentage)) {
(void)tokens.next_token(); (void)tokens.next_token(); // percentage
return PercentageStyleValue::create(Percentage(peek_token.token().percentage())); return PercentageStyleValue::create(Percentage(peek_token.token().percentage()));
} }
if (auto calc = parse_calculated_value(peek_token); calc && calc->resolves_to_number_percentage()) {
(void)tokens.next_token(); // calc
return calc;
}
return nullptr; return nullptr;
} }
RefPtr<CSSStyleValue> Parser::parse_percentage_value(TokenStream<ComponentValue>& tokens)
{
auto peek_token = tokens.peek_token();
if (peek_token.is(Token::Type::Percentage)) {
(void)tokens.next_token(); // percentage
return PercentageStyleValue::create(Percentage(peek_token.token().percentage()));
}
if (auto calc = parse_calculated_value(peek_token); calc && calc->resolves_to_percentage()) {
(void)tokens.next_token(); // calc
return calc;
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_angle_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_angle()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_angle())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_angle_percentage_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_angle() || dimension_value->is_percentage()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_angle_percentage())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_flex_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_flex()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_flex())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_frequency_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_frequency()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_frequency())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_frequency_percentage_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_frequency() || dimension_value->is_percentage()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_frequency_percentage())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_length_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_length()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_length())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_length_percentage_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_length() || dimension_value->is_percentage()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_length_percentage())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_resolution_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_resolution()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_resolution())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_time_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_time()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_time())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_time_percentage_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();
if (auto dimension_value = parse_dimension_value(tokens)) {
if (dimension_value->is_time() || dimension_value->is_percentage()
|| (dimension_value->is_calculated() && dimension_value->as_calculated().resolves_to_time_percentage())) {
transaction.commit();
return dimension_value;
}
}
return nullptr;
}
RefPtr<CSSStyleValue> Parser::parse_keyword_value(TokenStream<ComponentValue>& tokens) RefPtr<CSSStyleValue> Parser::parse_keyword_value(TokenStream<ComponentValue>& tokens)
{ {
auto peek_token = tokens.peek_token(); auto peek_token = tokens.peek_token();
@ -4782,7 +4945,7 @@ RefPtr<CSSStyleValue> Parser::parse_filter_value_list_value(TokenStream<Componen
return FilterValueListStyleValue::create(move(filter_value_list)); return FilterValueListStyleValue::create(move(filter_value_list));
} }
RefPtr<CSSStyleValue> Parser::parse_flex_value(TokenStream<ComponentValue>& tokens) RefPtr<CSSStyleValue> Parser::parse_flex_shorthand_value(TokenStream<ComponentValue>& tokens)
{ {
auto transaction = tokens.begin_transaction(); auto transaction = tokens.begin_transaction();
@ -5964,7 +6127,7 @@ RefPtr<CSSStyleValue> Parser::parse_transform_value(TokenStream<ComponentValue>&
} else { } else {
// FIXME: Remove this reconsume once all parsing functions are TokenStream-based. // FIXME: Remove this reconsume once all parsing functions are TokenStream-based.
argument_tokens.reconsume_current_input_token(); argument_tokens.reconsume_current_input_token();
auto number_or_percentage = parse_number_or_percentage_value(argument_tokens); auto number_or_percentage = parse_number_percentage_value(argument_tokens);
if (!number_or_percentage) if (!number_or_percentage)
return nullptr; return nullptr;
values.append(number_or_percentage.release_nonnull()); values.append(number_or_percentage.release_nonnull());
@ -7077,7 +7240,7 @@ Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue>> Parser::parse_css_value(Prope
return ParseError::SyntaxError; return ParseError::SyntaxError;
case PropertyID::Flex: case PropertyID::Flex:
case PropertyID::WebkitFlex: case PropertyID::WebkitFlex:
if (auto parsed_value = parse_flex_value(tokens); parsed_value && !tokens.has_next_token()) if (auto parsed_value = parse_flex_shorthand_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull(); return parsed_value.release_nonnull();
return ParseError::SyntaxError; return ParseError::SyntaxError;
case PropertyID::FlexFlow: case PropertyID::FlexFlow:

View file

@ -238,10 +238,6 @@ private:
// NOTE: Implemented in generated code. (GenerateCSSMathFunctions.cpp) // NOTE: Implemented in generated code. (GenerateCSSMathFunctions.cpp)
OwnPtr<CalculationNode> parse_math_function(PropertyID, Function const&); OwnPtr<CalculationNode> parse_math_function(PropertyID, Function const&);
OwnPtr<CalculationNode> parse_a_calc_function_node(Function const&); OwnPtr<CalculationNode> parse_a_calc_function_node(Function const&);
RefPtr<CSSStyleValue> parse_dimension_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_integer_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_number_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_number_or_percentage_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_keyword_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_keyword_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_color_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_color_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_counter_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_counter_value(TokenStream<ComponentValue>&);
@ -262,6 +258,22 @@ private:
RefPtr<PositionStyleValue> parse_position_value(TokenStream<ComponentValue>&, PositionParsingMode = PositionParsingMode::Normal); RefPtr<PositionStyleValue> parse_position_value(TokenStream<ComponentValue>&, PositionParsingMode = PositionParsingMode::Normal);
RefPtr<CSSStyleValue> parse_filter_value_list_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_filter_value_list_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_dimension_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_angle_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_angle_percentage_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_flex_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_frequency_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_frequency_percentage_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_integer_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_length_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_length_percentage_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_number_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_number_percentage_value(TokenStream<ComponentValue>& tokens);
RefPtr<CSSStyleValue> parse_percentage_value(TokenStream<ComponentValue>& tokens);
RefPtr<CSSStyleValue> parse_resolution_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_time_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_time_percentage_value(TokenStream<ComponentValue>&);
template<typename ParseFunction> template<typename ParseFunction>
RefPtr<CSSStyleValue> parse_comma_separated_value_list(TokenStream<ComponentValue>&, ParseFunction); RefPtr<CSSStyleValue> parse_comma_separated_value_list(TokenStream<ComponentValue>&, ParseFunction);
RefPtr<CSSStyleValue> parse_simple_comma_separated_value_list(PropertyID, TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_simple_comma_separated_value_list(PropertyID, TokenStream<ComponentValue>&);
@ -280,7 +292,7 @@ private:
RefPtr<CSSStyleValue> parse_counter_reset_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_counter_reset_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_counter_set_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_counter_set_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_display_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_display_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_flex_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_flex_shorthand_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_flex_flow_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_flex_flow_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_font_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_font_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_font_family_value(TokenStream<ComponentValue>&); RefPtr<CSSStyleValue> parse_font_family_value(TokenStream<ComponentValue>&);

View file

@ -117,6 +117,8 @@ public:
Optional<double> resolve_number() const; Optional<double> resolve_number() const;
Optional<i64> resolve_integer() const; Optional<i64> resolve_integer() const;
bool resolves_to_dimension() const { return m_resolved_type.matches_dimension(); }
bool contains_percentage() const; bool contains_percentage() const;
private: private: