From bc00ef8314c25422cf65c4b14a31d64ad1e5efc2 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Mon, 6 Jan 2025 12:48:17 +0000 Subject: [PATCH] LibWeb/CSS: Replace Parser "current property" with a stack of contexts `current_property_id()` is insufficient to determine if a quirk is allowed. For example, unitless lengths are allowed in certain properties, but NOT if they are inside a calc() or other function. It's also incorrect when we are parsing a longhand inside a shorthand. So instead, replace that with a stack of value-parsing contexts. For now, this is either properties or CSS functions, but in future can be expanded to include media features and other places. This lets us disallow quirks inside functions, like we're supposed to. It also lays the groundwork for being able to more easily determine what type a percentage inside a calculation should become, as this is based on the same stack of contexts. --- .../LibWeb/CSS/Parser/GradientParsing.cpp | 4 + Libraries/LibWeb/CSS/Parser/Parser.cpp | 250 +++++++++++++----- Libraries/LibWeb/CSS/Parser/Parser.h | 12 + Libraries/LibWeb/CSS/Parser/ParsingContext.h | 6 +- 4 files changed, 196 insertions(+), 76 deletions(-) diff --git a/Libraries/LibWeb/CSS/Parser/GradientParsing.cpp b/Libraries/LibWeb/CSS/Parser/GradientParsing.cpp index ca616dc4ee5..4f65c95e429 100644 --- a/Libraries/LibWeb/CSS/Parser/GradientParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/GradientParsing.cpp @@ -150,6 +150,8 @@ RefPtr Parser::parse_linear_gradient_function(TokenStream Parser::parse_conic_gradient_function(TokenStream Parser::parse_radial_gradient_function(TokenStream Parser::parse_calculated_value(ComponentValue const OwnPtr Parser::parse_a_calc_function_node(Function const& function) { + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function.name }); + if (function.name.equals_ignoring_ascii_case("calc"sv)) return parse_a_calculation(function.value); @@ -1976,13 +1978,9 @@ Optional Parser::parse_dimension(ComponentValue const& component_valu auto numeric_value = component_value.token().number_value(); if (numeric_value == 0) return Length::make_px(0); - if (m_context.in_quirks_mode() && property_has_quirk(m_context.current_property_id(), Quirk::UnitlessLength)) { - // https://quirks.spec.whatwg.org/#quirky-length-value - // FIXME: Disallow quirk when inside a CSS sub-expression (like `calc()`) - // "The value must not be supported in arguments to CSS expressions other than the rect() - // expression, and must not be supported in the supports() static method of the CSS interface." + + if (context_allows_quirky_length()) return Length::make_px(CSSPixels::nearest_value_for(numeric_value)); - } } return {}; @@ -2663,6 +2661,38 @@ RefPtr Parser::parse_frequency_percentage_value(TokenStream is a type of that is only valid in certain properties:" + // (NOTE: List skipped for brevity; quirks data is assigned in Properties.json) + // "It is not valid in properties that include or reference these properties, such as the background shorthand, + // or inside functional notations such as calc(), except that they must be allowed in rect() in the clip property." + + // So, it must be allowed in the top-level ValueParsingContext, and then not disallowed by any child contexts. + + Optional top_level_property; + if (!m_value_context.is_empty()) { + top_level_property = m_value_context.first().visit( + [](PropertyID const& property_id) -> Optional { return property_id; }, + [](auto const&) -> Optional { return OptionalNone {}; }); + } + + bool unitless_length_allowed = top_level_property.has_value() && property_has_quirk(top_level_property.value(), Quirk::UnitlessLength); + for (auto i = 1u; i < m_value_context.size() && unitless_length_allowed; i++) { + unitless_length_allowed = m_value_context[i].visit( + [](PropertyID const& property_id) { return property_has_quirk(property_id, Quirk::UnitlessLength); }, + [top_level_property](Parser::FunctionContext const& function_context) { + return function_context.name == "rect"sv && top_level_property == PropertyID::Clip; + }); + } + + return unitless_length_allowed; +} + RefPtr Parser::parse_length_value(TokenStream& tokens) { if (tokens.next_token().is(Token::Type::Dimension)) { @@ -2682,11 +2712,7 @@ RefPtr Parser::parse_length_value(TokenStream& to transaction.commit(); return LengthStyleValue::create(Length::make_px(0)); } - if (m_context.in_quirks_mode() && property_has_quirk(m_context.current_property_id(), Quirk::UnitlessLength)) { - // https://quirks.spec.whatwg.org/#quirky-length-value - // FIXME: Disallow quirk when inside a CSS sub-expression (like `calc()`) - // "The value must not be supported in arguments to CSS expressions other than the rect() - // expression, and must not be supported in the supports() static method of the CSS interface." + if (context_allows_quirky_length()) { transaction.commit(); return LengthStyleValue::create(Length::make_px(CSSPixels::nearest_value_for(numeric_value))); } @@ -2733,11 +2759,7 @@ RefPtr Parser::parse_length_percentage_value(TokenStream value must not be supported in arguments to CSS expressions other than the rect() - // expression, and must not be supported in the supports() static method of the CSS interface." + if (context_allows_quirky_length()) { transaction.commit(); return LengthStyleValue::create(Length::make_px(CSSPixels::nearest_value_for(numeric_value))); } @@ -2853,6 +2875,8 @@ RefPtr Parser::parse_rect_value(TokenStream& toke if (!function_token.is_function("rect"sv)) return nullptr; + auto context_guard = push_temporary_value_parsing_context(FunctionContext { "rect"sv }); + Vector params; auto argument_tokens = TokenStream { function_token.function().value }; @@ -2988,6 +3012,8 @@ RefPtr Parser::parse_rgb_color_value(TokenStream& if (!function_token.is_function("rgb"sv) && !function_token.is_function("rgba"sv)) return {}; + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function_token.function().name }); + RefPtr red; RefPtr green; RefPtr blue; @@ -3110,6 +3136,8 @@ RefPtr Parser::parse_hsl_color_value(TokenStream& if (!function_token.is_function("hsl"sv) && !function_token.is_function("hsla"sv)) return {}; + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function_token.function().name }); + RefPtr h; RefPtr s; RefPtr l; @@ -3211,6 +3239,8 @@ RefPtr Parser::parse_hwb_color_value(TokenStream& if (!function_token.is_function("hwb"sv)) return {}; + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function_token.function().name }); + RefPtr h; RefPtr w; RefPtr b; @@ -3444,6 +3474,8 @@ RefPtr Parser::parse_color_function(TokenStream& if (!function_token.is_function("color"sv)) return {}; + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function_token.function().name }); + auto inner_tokens = TokenStream { function_token.function().value }; inner_tokens.discard_whitespace(); @@ -3579,66 +3611,89 @@ RefPtr Parser::parse_color_value(TokenStream& tok return {}; } - // https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk - if (m_context.in_quirks_mode() && property_has_quirk(m_context.current_property_id(), Quirk::HashlessHexColor)) { - // The value of a quirky color is obtained from the possible component values using the following algorithm, - // aborting on the first step that returns a value: + // https://drafts.csswg.org/css-color-4/#quirky-color + if (m_context.in_quirks_mode()) { + // "When CSS is being parsed in quirks mode, is a type of that is only valid in certain properties:" + // (NOTE: List skipped for brevity; quirks data is assigned in Properties.json) + // "It is not valid in properties that include or reference these properties, such as the background shorthand, + // or inside functional notations such as color-mix()" - // 1. Let cv be the component value. - auto const& cv = component_value; - String serialization; - // 2. If cv is a or a , follow these substeps: - if (cv.is(Token::Type::Number) || cv.is(Token::Type::Dimension)) { - // 1. If cv’s type flag is not "integer", return an error. - // This means that values that happen to use scientific notation, e.g., 5e5e5e, will fail to parse. - if (!cv.token().number().is_integer()) - return {}; + bool quirky_color_allowed = false; + if (!m_value_context.is_empty()) { + quirky_color_allowed = m_value_context.first().visit( + [](PropertyID const& property_id) { return property_has_quirk(property_id, Quirk::HashlessHexColor); }, + [](FunctionContext const&) { return false; }); + } + for (auto i = 1u; i < m_value_context.size() && quirky_color_allowed; i++) { + quirky_color_allowed = m_value_context[i].visit( + [](PropertyID const& property_id) { return property_has_quirk(property_id, Quirk::UnitlessLength); }, + [](FunctionContext const&) { + return false; + }); + } + if (quirky_color_allowed) { + // NOTE: This algorithm is no longer in the spec, since the concept got moved and renamed. However, it works, + // and so we might as well keep using it. - // 2. If cv’s value is less than zero, return an error. - auto value = cv.is(Token::Type::Number) ? cv.token().to_integer() : cv.token().dimension_value_int(); - if (value < 0) - return {}; + // The value of a quirky color is obtained from the possible component values using the following algorithm, + // aborting on the first step that returns a value: - // 3. Let serialization be the serialization of cv’s value, as a base-ten integer using digits 0-9 (U+0030 to U+0039) in the shortest form possible. - StringBuilder serialization_builder; - serialization_builder.appendff("{}", value); + // 1. Let cv be the component value. + auto const& cv = component_value; + String serialization; + // 2. If cv is a or a , follow these substeps: + if (cv.is(Token::Type::Number) || cv.is(Token::Type::Dimension)) { + // 1. If cv’s type flag is not "integer", return an error. + // This means that values that happen to use scientific notation, e.g., 5e5e5e, will fail to parse. + if (!cv.token().number().is_integer()) + return {}; - // 4. If cv is a , append the unit to serialization. - if (cv.is(Token::Type::Dimension)) - serialization_builder.append(cv.token().dimension_unit()); + // 2. If cv’s value is less than zero, return an error. + auto value = cv.is(Token::Type::Number) ? cv.token().to_integer() : cv.token().dimension_value_int(); + if (value < 0) + return {}; - // 5. If serialization consists of fewer than six characters, prepend zeros (U+0030) so that it becomes six characters. - serialization = MUST(serialization_builder.to_string()); - if (serialization_builder.length() < 6) { - StringBuilder builder; - for (size_t i = 0; i < (6 - serialization_builder.length()); i++) - builder.append('0'); - builder.append(serialization_builder.string_view()); - serialization = MUST(builder.to_string()); + // 3. Let serialization be the serialization of cv’s value, as a base-ten integer using digits 0-9 (U+0030 to U+0039) in the shortest form possible. + StringBuilder serialization_builder; + serialization_builder.appendff("{}", value); + + // 4. If cv is a , append the unit to serialization. + if (cv.is(Token::Type::Dimension)) + serialization_builder.append(cv.token().dimension_unit()); + + // 5. If serialization consists of fewer than six characters, prepend zeros (U+0030) so that it becomes six characters. + serialization = MUST(serialization_builder.to_string()); + if (serialization_builder.length() < 6) { + StringBuilder builder; + for (size_t i = 0; i < (6 - serialization_builder.length()); i++) + builder.append('0'); + builder.append(serialization_builder.string_view()); + serialization = MUST(builder.to_string()); + } } - } - // 3. Otherwise, cv is an ; let serialization be cv’s value. - else { - if (!cv.is(Token::Type::Ident)) + // 3. Otherwise, cv is an ; let serialization be cv’s value. + else { + if (!cv.is(Token::Type::Ident)) + return {}; + serialization = cv.token().ident().to_string(); + } + + // 4. If serialization does not consist of three or six characters, return an error. + if (serialization.bytes().size() != 3 && serialization.bytes().size() != 6) return {}; - serialization = cv.token().ident().to_string(); - } - // 4. If serialization does not consist of three or six characters, return an error. - if (serialization.bytes().size() != 3 && serialization.bytes().size() != 6) - return {}; + // 5. If serialization contains any characters not in the range [0-9A-Fa-f] (U+0030 to U+0039, U+0041 to U+0046, U+0061 to U+0066), return an error. + for (auto c : serialization.bytes_as_string_view()) { + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) + return {}; + } - // 5. If serialization contains any characters not in the range [0-9A-Fa-f] (U+0030 to U+0039, U+0041 to U+0046, U+0061 to U+0066), return an error. - for (auto c : serialization.bytes_as_string_view()) { - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) - return {}; - } - - // 6. Return the concatenation of "#" (U+0023) and serialization. - auto color = Color::from_string(MUST(String::formatted("#{}", serialization))); - if (color.has_value()) { - transaction.commit(); - return CSSColorValue::create_from_color(color.release_value()); + // 6. Return the concatenation of "#" (U+0023) and serialization. + auto color = Color::from_string(MUST(String::formatted("#{}", serialization))); + if (color.has_value()) { + transaction.commit(); + return CSSColorValue::create_from_color(color.release_value()); + } } } @@ -3759,6 +3814,8 @@ RefPtr Parser::parse_counter_value(TokenStream& t if (token.is_function("counter"sv)) { // counter() = counter( , ? ) auto& function = token.function(); + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function.name }); + TokenStream function_tokens { function.value }; auto function_values = parse_a_comma_separated_list_of_component_values(function_tokens); if (function_values.is_empty() || function_values.size() > 2) @@ -3787,6 +3844,8 @@ RefPtr Parser::parse_counter_value(TokenStream& t if (token.is_function("counters"sv)) { // counters() = counters( , , ? ) auto& function = token.function(); + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function.name }); + TokenStream function_tokens { function.value }; auto function_values = parse_a_comma_separated_list_of_component_values(function_tokens); if (function_values.size() < 2 || function_values.size() > 3) @@ -5649,6 +5708,8 @@ RefPtr Parser::parse_filter_value_list_value(TokenStream Parser::parse_font_face_src(TokenStream& compo auto const& function = maybe_function.function(); if (function.name.equals_ignoring_ascii_case("format"sv)) { + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function.name }); + TokenStream format_tokens { function.value }; format_tokens.discard_whitespace(); auto const& format_name_token = format_tokens.consume_a_token(); @@ -6815,6 +6878,8 @@ RefPtr Parser::parse_math_depth_value(TokenStream // add() if (token.is_function("add"sv)) { + auto context_guard = push_temporary_value_parsing_context(FunctionContext { token.function().name }); + auto add_tokens = TokenStream { token.function().value }; add_tokens.discard_whitespace(); auto const& integer_token = add_tokens.consume_a_token(); @@ -7105,6 +7170,8 @@ RefPtr Parser::parse_easing_value(TokenStream& to argument.remove_all_matching([](auto& value) { return value.is(Token::Type::Whitespace); }); auto name = part.function().name; + auto context_guard = push_temporary_value_parsing_context(FunctionContext { name }); + if (name.equals_ignoring_ascii_case("linear"sv)) { // linear() = linear( [ && {0,2} ]# ) Vector stops; @@ -7255,6 +7322,9 @@ RefPtr Parser::parse_transform_value(TokenStream& auto maybe_function = transform_function_from_string(part.function().name); if (!maybe_function.has_value()) return nullptr; + + auto context_guard = push_temporary_value_parsing_context(FunctionContext { part.function().name }); + auto function = maybe_function.release_value(); auto function_metadata = transform_function_metadata(function); @@ -7783,6 +7853,8 @@ Optional Parser::parse_track_sizing_function(ComponentVa { if (token.is_function()) { auto const& function_token = token.function(); + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function_token.name }); + if (function_token.name.equals_ignoring_ascii_case("repeat"sv)) { auto maybe_repeat = parse_repeat(function_token.value); if (maybe_repeat.has_value()) @@ -8398,7 +8470,8 @@ bool block_contains_var_or_attr(SimpleBlock const& block) Parser::ParseErrorOr> Parser::parse_css_value(PropertyID property_id, TokenStream& unprocessed_tokens, Optional original_source_text) { - m_context.set_current_property_id(property_id); + auto context_guard = push_temporary_value_parsing_context(property_id); + Vector component_values; bool contains_var_or_attr = false; bool const property_accepts_custom_ident = property_accepts_type(property_id, ValueType::CustomIdent); @@ -8824,6 +8897,7 @@ Optional Parser::parse_css_value_for_properties(Readon auto& peek_token = tokens.next_token(); if (auto property = any_property_accepts_type(property_ids, ValueType::EasingFunction); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_easing_function = parse_easing_value(tokens)) return PropertyAndValue { *property, maybe_easing_function }; } @@ -8841,68 +8915,82 @@ Optional Parser::parse_css_value_for_properties(Readon // Custom idents if (auto property = any_property_accepts_type(property_ids, ValueType::CustomIdent); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto custom_ident = parse_custom_ident_value(tokens, {})) return PropertyAndValue { *property, custom_ident }; } } if (auto property = any_property_accepts_type(property_ids, ValueType::Color); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_color = parse_color_value(tokens)) return PropertyAndValue { *property, maybe_color }; } if (auto property = any_property_accepts_type(property_ids, ValueType::Counter); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_counter = parse_counter_value(tokens)) return PropertyAndValue { *property, maybe_counter }; } if (auto property = any_property_accepts_type(property_ids, ValueType::Image); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_image = parse_image_value(tokens)) return PropertyAndValue { *property, maybe_image }; } if (auto property = any_property_accepts_type(property_ids, ValueType::Position); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_position = parse_position_value(tokens)) return PropertyAndValue { *property, maybe_position }; } if (auto property = any_property_accepts_type(property_ids, ValueType::BackgroundPosition); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_position = parse_position_value(tokens, PositionParsingMode::BackgroundPosition)) return PropertyAndValue { *property, maybe_position }; } if (auto property = any_property_accepts_type(property_ids, ValueType::BasicShape); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_basic_shape = parse_basic_shape_value(tokens)) return PropertyAndValue { *property, maybe_basic_shape }; } if (auto property = any_property_accepts_type(property_ids, ValueType::Ratio); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_ratio = parse_ratio_value(tokens)) return PropertyAndValue { *property, maybe_ratio }; } if (auto property = any_property_accepts_type(property_ids, ValueType::OpenTypeTag); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_rect = parse_opentype_tag_value(tokens)) return PropertyAndValue { *property, maybe_rect }; } if (auto property = any_property_accepts_type(property_ids, ValueType::Rect); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto maybe_rect = parse_rect_value(tokens)) return PropertyAndValue { *property, maybe_rect }; } if (peek_token.is(Token::Type::String)) { - if (auto property = any_property_accepts_type(property_ids, ValueType::String); property.has_value()) + if (auto property = any_property_accepts_type(property_ids, ValueType::String); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); return PropertyAndValue { *property, StringStyleValue::create(tokens.consume_a_token().token().string()) }; + } } if (auto property = any_property_accepts_type(property_ids, ValueType::Url); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto url = parse_url_value(tokens)) return PropertyAndValue { *property, url }; } // / 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()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto value = parse_integer_value(tokens)) { if (value->is_calculated()) return PropertyAndValue { *property, value }; @@ -8912,6 +9000,7 @@ Optional Parser::parse_css_value_for_properties(Readon } if (auto property = any_property_accepts_type(property_ids, ValueType::Number); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto value = parse_number_value(tokens)) { if (value->is_calculated()) return PropertyAndValue { *property, value }; @@ -8921,6 +9010,7 @@ Optional Parser::parse_css_value_for_properties(Readon } if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (property_accepts_type(*property, ValueType::Percentage)) { if (auto value = parse_angle_percentage_value(tokens)) { if (value->is_calculated()) @@ -8940,6 +9030,7 @@ Optional Parser::parse_css_value_for_properties(Readon } if (auto property = any_property_accepts_type(property_ids, ValueType::Flex); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto value = parse_flex_value(tokens)) { if (value->is_calculated()) return PropertyAndValue { *property, value }; @@ -8949,6 +9040,7 @@ Optional Parser::parse_css_value_for_properties(Readon } if (auto property = any_property_accepts_type(property_ids, ValueType::Frequency); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (property_accepts_type(*property, ValueType::Percentage)) { if (auto value = parse_frequency_percentage_value(tokens)) { if (value->is_calculated()) @@ -8968,6 +9060,7 @@ Optional Parser::parse_css_value_for_properties(Readon } if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (property_accepts_type(*property, ValueType::Percentage)) { if (auto value = parse_length_percentage_value(tokens)) { if (value->is_calculated()) @@ -8987,6 +9080,7 @@ Optional Parser::parse_css_value_for_properties(Readon } if (auto property = any_property_accepts_type(property_ids, ValueType::Resolution); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto value = parse_resolution_value(tokens)) { if (value->is_calculated()) return PropertyAndValue { *property, value }; @@ -8996,6 +9090,7 @@ Optional Parser::parse_css_value_for_properties(Readon } if (auto property = any_property_accepts_type(property_ids, ValueType::Time); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (property_accepts_type(*property, ValueType::Percentage)) { if (auto value = parse_time_percentage_value(tokens)) { if (value->is_calculated()) @@ -9016,6 +9111,7 @@ Optional Parser::parse_css_value_for_properties(Readon // is checked after the types. if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto value = parse_percentage_value(tokens)) { if (value->is_calculated()) return PropertyAndValue { *property, value }; @@ -9025,6 +9121,7 @@ Optional Parser::parse_css_value_for_properties(Readon } if (auto property = any_property_accepts_type(property_ids, ValueType::Paint); property.has_value()) { + auto context_guard = push_temporary_value_parsing_context(*property); if (auto value = parse_paint_value(tokens)) return PropertyAndValue { *property, value.release_nonnull() }; } @@ -9245,8 +9342,19 @@ OwnPtr Parser::convert_to_calculation_node(CalcParsing::Node co } if (component_value->is(Token::Type::Percentage)) { - // FIXME: Figure this out in non-property contexts - auto percentage_resolved_type = property_resolves_percentages_relative_to(m_context.current_property_id()); + Optional percentage_resolved_type; + for (auto const& value_context : m_value_context.in_reverse()) { + percentage_resolved_type = value_context.visit( + [](PropertyID property_id) -> Optional { + return property_resolves_percentages_relative_to(property_id); + }, + [](FunctionContext const&) -> Optional { + // FIXME: Some functions provide this. The spec mentions `media-progress()` as an example. + return {}; + }); + if (percentage_resolved_type.has_value()) + break; + } return NumericCalculationNode::create(Percentage { component_value->token().percentage() }, percentage_resolved_type); } diff --git a/Libraries/LibWeb/CSS/Parser/Parser.h b/Libraries/LibWeb/CSS/Parser/Parser.h index 358e9836d79..d66b67bea35 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Libraries/LibWeb/CSS/Parser/Parser.h @@ -443,6 +443,18 @@ private: Vector m_tokens; TokenStream m_token_stream; + struct FunctionContext { + StringView name; + }; + using ValueParsingContext = Variant; + Vector m_value_context; + auto push_temporary_value_parsing_context(ValueParsingContext&& context) + { + m_value_context.append(context); + return ScopeGuard { [&] { m_value_context.take_last(); } }; + } + bool context_allows_quirky_length() const; + enum class ContextType { Unknown, Style, diff --git a/Libraries/LibWeb/CSS/Parser/ParsingContext.h b/Libraries/LibWeb/CSS/Parser/ParsingContext.h index 6783db1247d..131f37d86fe 100644 --- a/Libraries/LibWeb/CSS/Parser/ParsingContext.h +++ b/Libraries/LibWeb/CSS/Parser/ParsingContext.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2020-2021, the SerenityOS developers. - * Copyright (c) 2021-2023, Sam Atkins + * Copyright (c) 2021-2024, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -34,9 +34,6 @@ public: HTML::Window const* window() const; URL::URL complete_url(StringView) const; - PropertyID current_property_id() const { return m_current_property_id; } - void set_current_property_id(PropertyID property_id) { m_current_property_id = property_id; } - JS::Realm& realm() const { VERIFY(m_realm); @@ -46,7 +43,6 @@ public: private: GC::Ptr m_realm; GC::Ptr m_document; - PropertyID m_current_property_id { PropertyID::Invalid }; URL::URL m_url; Mode m_mode { Mode::Normal }; };