From ab5ac82db6ea8b41e9b6bc24059abb1452007c4f Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 7 Jan 2025 11:16:31 +0000 Subject: [PATCH] LibWeb/CSS: Parse dimension and number types individually An upcoming change requires that we can determine which property we are parsing before we parse the value. That's the opposite of what this code previously did, which was to parse a generic dimension or calc() and then figure out what property would accept it. --- Libraries/LibWeb/CSS/Parser/Parser.cpp | 274 +++++++++++-------------- 1 file changed, 116 insertions(+), 158 deletions(-) diff --git a/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Libraries/LibWeb/CSS/Parser/Parser.cpp index 12cc6e10232..dc7c8b810aa 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -8834,13 +8834,6 @@ Optional Parser::parse_css_value_for_properties(Readon } return {}; }; - auto any_property_accepts_type_percentage = [](ReadonlySpan property_ids, ValueType value_type) -> Optional { - for (auto const& property : property_ids) { - if (property_accepts_type(property, value_type) && property_accepts_type(property, ValueType::Percentage)) - return property; - } - return {}; - }; auto any_property_accepts_keyword = [](ReadonlySpan property_ids, Keyword keyword) -> Optional { for (auto const& property : property_ids) { if (property_accepts_keyword(property, keyword)) @@ -8909,40 +8902,11 @@ Optional Parser::parse_css_value_for_properties(Readon return PropertyAndValue { *property, maybe_ratio }; } - auto property_accepting_integer = any_property_accepts_type(property_ids, ValueType::Integer); - auto property_accepting_number = any_property_accepts_type(property_ids, ValueType::Number); - bool property_accepts_numeric = property_accepting_integer.has_value() || property_accepting_number.has_value(); - - if (peek_token.is(Token::Type::Number) && property_accepts_numeric) { - if (peek_token.token().number().is_integer() && property_accepting_integer.has_value()) { - auto integer = IntegerStyleValue::create(peek_token.token().number().integer_value()); - if (property_accepts_integer(*property_accepting_integer, integer->as_integer().integer())) { - tokens.discard_a_token(); // integer - return PropertyAndValue { *property_accepting_integer, integer }; - } - } - if (property_accepting_number.has_value()) { - auto number = NumberStyleValue::create(peek_token.token().number().value()); - if (property_accepts_number(*property_accepting_number, number->as_number().number())) { - tokens.discard_a_token(); // number - return PropertyAndValue { *property_accepting_number, number }; - } - } - } - if (auto property = any_property_accepts_type(property_ids, ValueType::OpenTypeTag); property.has_value()) { if (auto maybe_rect = parse_opentype_tag_value(tokens)) return PropertyAndValue { *property, maybe_rect }; } - if (peek_token.is(Token::Type::Percentage)) { - auto percentage = Percentage(peek_token.token().percentage()); - if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value() && property_accepts_percentage(*property, percentage)) { - tokens.discard_a_token(); - return PropertyAndValue { *property, PercentageStyleValue::create(percentage) }; - } - } - if (auto property = any_property_accepts_type(property_ids, ValueType::Rect); property.has_value()) { if (auto maybe_rect = parse_rect_value(tokens)) return PropertyAndValue { *property, maybe_rect }; @@ -8958,133 +8922,127 @@ Optional Parser::parse_css_value_for_properties(Readon return PropertyAndValue { *property, url }; } - bool property_accepts_dimension = any_property_accepts_type(property_ids, ValueType::Angle).has_value() - || any_property_accepts_type(property_ids, ValueType::Flex).has_value() - || any_property_accepts_type(property_ids, ValueType::Frequency).has_value() - || any_property_accepts_type(property_ids, ValueType::Length).has_value() - || any_property_accepts_type(property_ids, ValueType::Percentage).has_value() - || any_property_accepts_type(property_ids, ValueType::Resolution).has_value() - || any_property_accepts_type(property_ids, ValueType::Time).has_value(); - - if (property_accepts_dimension) { - if (peek_token.is(Token::Type::Number) && m_context.is_parsing_svg_presentation_attribute()) { - auto transaction = tokens.begin_transaction(); - auto const& token = tokens.consume_a_token(); - // https://svgwg.org/svg2-draft/types.html#presentation-attribute-css-value - // We need to allow in any place that expects a or . - // FIXME: How should these numbers be interpreted? https://github.com/w3c/svgwg/issues/792 - // For now: Convert them to px lengths, or deg angles. - auto angle = Angle::make_degrees(token.token().number_value()); - if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value() && property_accepts_angle(*property, angle)) { - transaction.commit(); - return PropertyAndValue { *property, AngleStyleValue::create(angle) }; - } - auto length = Length::make_px(CSSPixels::nearest_value_for(token.token().number_value())); - if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value() && property_accepts_length(*property, length)) { - transaction.commit(); - return PropertyAndValue { *property, LengthStyleValue::create(length) }; - } - } - - auto transaction = tokens.begin_transaction(); - if (auto maybe_dimension = parse_dimension(peek_token); maybe_dimension.has_value()) { - tokens.discard_a_token(); - auto dimension = maybe_dimension.release_value(); - if (dimension.is_angle()) { - auto angle = dimension.angle(); - if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value() && property_accepts_angle(*property, angle)) { - transaction.commit(); - return PropertyAndValue { *property, AngleStyleValue::create(angle) }; - } - } - if (dimension.is_flex()) { - auto flex = dimension.flex(); - if (auto property = any_property_accepts_type(property_ids, ValueType::Flex); property.has_value() && property_accepts_flex(*property, flex)) { - transaction.commit(); - return PropertyAndValue { *property, FlexStyleValue::create(flex) }; - } - } - if (dimension.is_frequency()) { - auto frequency = dimension.frequency(); - if (auto property = any_property_accepts_type(property_ids, ValueType::Frequency); property.has_value() && property_accepts_frequency(*property, frequency)) { - transaction.commit(); - return PropertyAndValue { *property, FrequencyStyleValue::create(frequency) }; - } - } - if (dimension.is_length()) { - auto length = dimension.length(); - if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value() && property_accepts_length(*property, length)) { - transaction.commit(); - return PropertyAndValue { *property, LengthStyleValue::create(length) }; - } - } - if (dimension.is_resolution()) { - auto resolution = dimension.resolution(); - if (auto property = any_property_accepts_type(property_ids, ValueType::Resolution); property.has_value() && property_accepts_resolution(*property, resolution)) { - transaction.commit(); - return PropertyAndValue { *property, ResolutionStyleValue::create(resolution) }; - } - } - if (dimension.is_time()) { - auto time = dimension.time(); - if (auto property = any_property_accepts_type(property_ids, ValueType::Time); property.has_value() && property_accepts_time(*property, time)) { - transaction.commit(); - return PropertyAndValue { *property, TimeStyleValue::create(time) }; - } - } + // / come before , so that 0 is not interpreted as a in case both are allowed. + if (auto property = any_property_accepts_type(property_ids, ValueType::Integer); property.has_value()) { + if (auto value = parse_integer_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_integer() && property_accepts_integer(*property, value->as_integer().integer())) + return PropertyAndValue { *property, value }; } } - // In order to not end up parsing `calc()` and other math expressions multiple times, - // we parse it once, and then see if its resolved type matches what the property accepts. - if (peek_token.is_function() && (property_accepts_dimension || property_accepts_numeric)) { - if (auto maybe_calculated = parse_calculated_value(peek_token); maybe_calculated) { - tokens.discard_a_token(); - auto& calculated = *maybe_calculated; - // This is a bit sensitive to ordering: `` and `` have to be checked before ``. - // FIXME: When parsing SVG presentation attributes, is permitted wherever , , or are. - // The specifics are unclear, so I'm ignoring this for calculated values for now. - // See https://github.com/w3c/svgwg/issues/792 - if (calculated.resolves_to_percentage()) { - if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_angle()) { - if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_angle_percentage()) { - if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Angle); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_flex()) { - if (auto property = any_property_accepts_type(property_ids, ValueType::Flex); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_frequency()) { - if (auto property = any_property_accepts_type(property_ids, ValueType::Frequency); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_frequency_percentage()) { - if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Frequency); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_number()) { - if (property_accepts_numeric) { - auto property_or_resolved = property_accepting_integer.value_or_lazy_evaluated([property_accepting_number]() { return property_accepting_number.value(); }); - return PropertyAndValue { property_or_resolved, calculated }; - } - } else if (calculated.resolves_to_number_percentage()) { - if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Number); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_length()) { - if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_length_percentage()) { - if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Length); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_time()) { - if (auto property = any_property_accepts_type(property_ids, ValueType::Time); property.has_value()) - return PropertyAndValue { *property, calculated }; - } else if (calculated.resolves_to_time_percentage()) { - if (auto property = any_property_accepts_type_percentage(property_ids, ValueType::Time); property.has_value()) - return PropertyAndValue { *property, calculated }; + if (auto property = any_property_accepts_type(property_ids, ValueType::Number); property.has_value()) { + if (auto value = parse_number_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_number() && property_accepts_number(*property, value->as_number().number())) + return PropertyAndValue { *property, value }; + } + } + + if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value()) { + if (property_accepts_type(*property, ValueType::Percentage)) { + if (auto value = parse_angle_percentage_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_angle() && property_accepts_angle(*property, value->as_angle().angle())) + return PropertyAndValue { *property, value }; + if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) + return PropertyAndValue { *property, value }; } } + if (auto value = parse_angle_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_angle() && property_accepts_angle(*property, value->as_angle().angle())) + return PropertyAndValue { *property, value }; + } + } + + if (auto property = any_property_accepts_type(property_ids, ValueType::Flex); property.has_value()) { + if (auto value = parse_flex_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_flex() && property_accepts_flex(*property, value->as_flex().flex())) + return PropertyAndValue { *property, value }; + } + } + + if (auto property = any_property_accepts_type(property_ids, ValueType::Frequency); property.has_value()) { + if (property_accepts_type(*property, ValueType::Percentage)) { + if (auto value = parse_frequency_percentage_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_frequency() && property_accepts_frequency(*property, value->as_frequency().frequency())) + return PropertyAndValue { *property, value }; + if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) + return PropertyAndValue { *property, value }; + } + } + if (auto value = parse_frequency_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_frequency() && property_accepts_frequency(*property, value->as_frequency().frequency())) + return PropertyAndValue { *property, value }; + } + } + + if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value()) { + if (property_accepts_type(*property, ValueType::Percentage)) { + if (auto value = parse_length_percentage_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_length() && property_accepts_length(*property, value->as_length().length())) + return PropertyAndValue { *property, value }; + if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) + return PropertyAndValue { *property, value }; + } + } + if (auto value = parse_length_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_length() && property_accepts_length(*property, value->as_length().length())) + return PropertyAndValue { *property, value }; + } + } + + if (auto property = any_property_accepts_type(property_ids, ValueType::Resolution); property.has_value()) { + if (auto value = parse_resolution_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_resolution() && property_accepts_resolution(*property, value->as_resolution().resolution())) + return PropertyAndValue { *property, value }; + } + } + + if (auto property = any_property_accepts_type(property_ids, ValueType::Time); property.has_value()) { + if (property_accepts_type(*property, ValueType::Percentage)) { + if (auto value = parse_time_percentage_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_time() && property_accepts_time(*property, value->as_time().time())) + return PropertyAndValue { *property, value }; + if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) + return PropertyAndValue { *property, value }; + } + } + if (auto value = parse_time_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_time() && property_accepts_time(*property, value->as_time().time())) + return PropertyAndValue { *property, value }; + } + } + + // is checked after the types. + if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value()) { + if (auto value = parse_percentage_value(tokens)) { + if (value->is_calculated()) + return PropertyAndValue { *property, value }; + if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) + return PropertyAndValue { *property, value }; + } } if (auto property = any_property_accepts_type(property_ids, ValueType::Paint); property.has_value()) {