LibWeb: Validate literal numeric values at parse time

This brings a couple of advantages:
 - Previously we relied on the caller validating the parsed value was in
   bounds after the fact - this was usually fine but there are a couple
   of places that it was forgotten (see the tests added in this commit),
   requiring the bounds to be passed as arguments makes us consider the
   desired range more explicitly.
 - In a future commit we will use the passed bounds as the clamping
   bounds for computed values, removing the need for the existing
   `ValueParsingContext` based method we have at the moment.
 - Generating code is easier with this approach
This commit is contained in:
Callum Law
2026-04-16 14:15:53 +12:00
committed by Sam Atkins
parent ca1b433d27
commit 76250ba142
Notes: github-actions[bot] 2026-04-22 13:25:26 +00:00
12 changed files with 346 additions and 443 deletions

View File

@@ -13,7 +13,16 @@ namespace Web::CSS {
struct NumericRange {
double min;
double max;
bool contains(double value) const { return value >= min && value <= max; }
};
using NumericRangesByValueType = HashMap<ValueType, NumericRange>;
constexpr NumericRange infinite_range = { AK::NumericLimits<float>::lowest(), AK::NumericLimits<float>::max() };
constexpr NumericRange non_negative_range = { 0, AK::NumericLimits<float>::max() };
constexpr NumericRange infinite_integer_range = { AK::NumericLimits<i32>::min(), AK::NumericLimits<i32>::max() };
constexpr NumericRange non_negative_integer_range = { 0, AK::NumericLimits<i32>::max() };
}

View File

@@ -146,7 +146,7 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_descriptor_v
return CounterStyleSystemStyleValue::create(system.release_value());
if (keyword_value->to_keyword() == Keyword::Fixed) {
auto integer_value = parse_integer_value(tokens);
auto integer_value = parse_integer_value(tokens, infinite_integer_range);
return CounterStyleSystemStyleValue::create_fixed(integer_value);
}
@@ -200,7 +200,7 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_descriptor_v
if (auto keyword_value = parse_keyword_value(tokens); keyword_value && keyword_value->to_keyword() == Keyword::Infinite)
return keyword_value;
if (auto integer_value = parse_integer_value(tokens); integer_value)
if (auto integer_value = parse_integer_value(tokens, infinite_integer_range); integer_value)
return integer_value;
return nullptr;
@@ -317,7 +317,7 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_descriptor_v
return StyleValueList::create({ first.release_nonnull(), second.release_nonnull() }, StyleValueList::Separator::Space);
}
case DescriptorMetadata::ValueType::Length:
return parse_length_value(tokens);
return parse_length_value(tokens, infinite_range);
case DescriptorMetadata::ValueType::OptionalDeclarationValue: {
tokens.discard_whitespace();
@@ -340,18 +340,11 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_descriptor_v
return value.release_nonnull();
// <length [0,∞]>{1,2}
if (auto first_length = parse_length_value(tokens)) {
if (first_length->is_length() && first_length->as_length().raw_value() < 0)
return nullptr;
if (auto first_length = parse_length_value(tokens, non_negative_range)) {
tokens.discard_whitespace();
if (auto second_length = parse_length_value(tokens)) {
if (second_length->is_length() && second_length->as_length().raw_value() < 0)
return nullptr;
if (auto second_length = parse_length_value(tokens, non_negative_range))
return StyleValueList::create(StyleValueVector { first_length.release_nonnull(), second_length.release_nonnull() }, StyleValueList::Separator::Space);
}
return first_length.release_nonnull();
}
@@ -392,13 +385,12 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_descriptor_v
return page_size ? page_size.release_nonnull() : orientation.release_nonnull();
}
case DescriptorMetadata::ValueType::PositivePercentage: {
if (auto percentage_value = parse_percentage_value(tokens)) {
if (percentage_value->is_percentage()) {
if (percentage_value->as_percentage().raw_value() < 0)
return nullptr;
if (auto percentage_value = parse_percentage_value(tokens, non_negative_range)) {
if (percentage_value->is_percentage())
return percentage_value.release_nonnull();
}
// All calculations in descriptors must be resolvable at parse-time.
// FIXME: Support relative lengths within calcs here (i.e. by absolutizing and clamping rather
// than rejecting anything that doesn't resolve at parse time)
if (percentage_value->is_calculated()) {
auto percentage = percentage_value->as_calculated().resolve_percentage({});
if (percentage.has_value() && percentage->value() >= 0)

View File

@@ -117,7 +117,7 @@ Optional<Vector<ColorStopListElement>> Parser::parse_linear_color_stop_list(Toke
// <linear-color-stop> , [ <linear-color-hint>? , <linear-color-stop> ]#
return parse_color_stop_list(
tokens,
[&](auto& it) { return parse_length_percentage_value(it); });
[&](auto& it) { return parse_length_percentage_value(it, infinite_range, infinite_range); });
}
Optional<Vector<ColorStopListElement>> Parser::parse_angular_color_stop_list(TokenStream<ComponentValue>& tokens)
@@ -138,7 +138,7 @@ Optional<Vector<ColorStopListElement>> Parser::parse_angular_color_stop_list(Tok
}
}
return parse_angle_percentage_value(it);
return parse_angle_percentage_value(it, infinite_range, infinite_range);
});
}
@@ -207,7 +207,7 @@ RefPtr<LinearGradientStyleValue const> Parser::parse_linear_gradient_function(To
tokens.discard_whitespace();
auto const& first_param = tokens.next_token();
if (auto maybe_angle = parse_angle_value(tokens)) {
if (auto maybe_angle = parse_angle_value(tokens, infinite_range)) {
gradient_direction = maybe_angle.release_nonnull();
} else if (first_param.is(Token::Type::Number) && first_param.token().number_value() == 0) {
// <zero>
@@ -327,7 +327,7 @@ RefPtr<ConicGradientStyleValue const> Parser::parse_conic_gradient_function(Toke
// from [ <angle> | <zero> ]
if (from_angle || at_position)
return nullptr;
if (auto maybe_angle = parse_angle_value(tokens)) {
if (auto maybe_angle = parse_angle_value(tokens, infinite_range)) {
from_angle = maybe_angle.release_nonnull();
} else if (auto peek_token = tokens.next_token(); peek_token.is(Token::Type::Number) && peek_token.token().number_value() == 0) {
tokens.discard_a_token(); // 0

View File

@@ -510,7 +510,7 @@ Optional<MediaFeatureValue> Parser::parse_media_feature_value(MediaFeatureID med
if (media_feature_accepts_type(media_feature, MediaFeatureValueType::Boolean)) {
auto transaction = tokens.begin_transaction();
tokens.discard_whitespace();
if (auto integer = parse_integer_value(tokens)) {
if (auto integer = parse_integer_value(tokens, infinite_integer_range)) {
if (integer->is_calculated() || first_is_one_of(integer->as_integer().integer(), 0, 1)) {
transaction.commit();
return MediaFeatureValue(MediaFeatureValue::Type::Integer, integer.release_nonnull());
@@ -521,7 +521,7 @@ Optional<MediaFeatureValue> Parser::parse_media_feature_value(MediaFeatureID med
// Integer
if (media_feature_accepts_type(media_feature, MediaFeatureValueType::Integer)) {
auto transaction = tokens.begin_transaction();
if (auto integer = parse_integer_value(tokens)) {
if (auto integer = parse_integer_value(tokens, infinite_integer_range)) {
transaction.commit();
return MediaFeatureValue(MediaFeatureValue::Type::Integer, integer.release_nonnull());
}
@@ -531,7 +531,7 @@ Optional<MediaFeatureValue> Parser::parse_media_feature_value(MediaFeatureID med
if (media_feature_accepts_type(media_feature, MediaFeatureValueType::Length)) {
auto transaction = tokens.begin_transaction();
tokens.discard_whitespace();
if (auto length = parse_length_value(tokens)) {
if (auto length = parse_length_value(tokens, infinite_range)) {
transaction.commit();
return MediaFeatureValue(MediaFeatureValue::Type::Length, length.release_nonnull());
}
@@ -571,7 +571,7 @@ Optional<MediaFeatureValue> Parser::parse_media_feature_value(MediaFeatureID med
if (media_feature_accepts_type(media_feature, MediaFeatureValueType::Resolution)) {
auto transaction = tokens.begin_transaction();
tokens.discard_whitespace();
if (auto resolution = parse_resolution_value(tokens)) {
if (auto resolution = parse_resolution_value(tokens, infinite_range)) {
transaction.commit();
return MediaFeatureValue(MediaFeatureValue::Type::Resolution, resolution.release_nonnull());
}

View File

@@ -1876,13 +1876,12 @@ RefPtr<StyleValue const> Parser::parse_source_size_value(TokenStream<ComponentVa
return KeywordStyleValue::create(Keyword::Auto);
}
if (auto parsed = parse_length_value(tokens)) {
// https://html.spec.whatwg.org/multipage/images.html#valid-source-size-list
// "A <source-size-value> that is a <length> must not be negative,
// and must not use CSS functions other than the math functions."
if (parsed->is_length() && parsed->as_length().length().raw_value() < 0)
return {};
// https://html.spec.whatwg.org/multipage/images.html#valid-source-size-list
// "A <source-size-value> that is a <length> must not be negative,
// and must not use CSS functions other than the math functions."
if (auto parsed = parse_length_value(tokens, non_negative_range)) {
// FIXME: It seems odd that we disallow infinite calculated values here rather than clamping as we do for all
// other values - is this correct?
if (parsed->is_calculated()) {
// https://drafts.csswg.org/css-values-4/#calc-range
// "the value resulting from a top-level calculation must be

View File

@@ -458,21 +458,22 @@ private:
RefPtr<StyleValue const> parse_anchor(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_anchor_size(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_angle_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_angle_percentage_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_flex_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_frequency_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_frequency_percentage_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_integer_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_length_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_length_percentage_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_number_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_number_percentage_value(TokenStream<ComponentValue>& tokens);
RefPtr<StyleValue const> parse_angle_value(TokenStream<ComponentValue>&, NumericRange const& accepted_range);
RefPtr<StyleValue const> parse_angle_percentage_value(TokenStream<ComponentValue>&, NumericRange const& accepted_angle_range, NumericRange const& accepted_percentage_range);
RefPtr<StyleValue const> parse_flex_value(TokenStream<ComponentValue>&, NumericRange const& accepted_range);
RefPtr<StyleValue const> parse_frequency_value(TokenStream<ComponentValue>&, NumericRange const& accepted_range);
RefPtr<StyleValue const> parse_frequency_percentage_value(TokenStream<ComponentValue>&, NumericRange const& accepted_frequency_range, NumericRange const& accepted_percentage_range);
RefPtr<StyleValue const> parse_integer_value(TokenStream<ComponentValue>&, NumericRange const& accepted_range);
RefPtr<StyleValue const> parse_length_value(TokenStream<ComponentValue>&, NumericRange const& accepted_range);
RefPtr<StyleValue const> parse_length_percentage_value(TokenStream<ComponentValue>&, NumericRange const& accepted_length_range, NumericRange const& accepted_percentage_range);
RefPtr<StyleValue const> parse_number_value(TokenStream<ComponentValue>&, NumericRange const& accepted_range);
RefPtr<StyleValue const> parse_number_percentage_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_number_range, NumericRange const& accepted_percentage_range);
RefPtr<StyleValue const> parse_percentage_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range);
RefPtr<StyleValue const> parse_resolution_value(TokenStream<ComponentValue>&, NumericRange const& accepted_range);
RefPtr<StyleValue const> parse_time_value(TokenStream<ComponentValue>&, NumericRange const& accepted_range);
RefPtr<StyleValue const> parse_time_percentage_value(TokenStream<ComponentValue>&, NumericRange const& accepted_time_range, NumericRange const& accepted_percentage_range);
RefPtr<StyleValue const> parse_number_percentage_none_value(TokenStream<ComponentValue>& tokens);
RefPtr<StyleValue const> parse_percentage_value(TokenStream<ComponentValue>& tokens);
RefPtr<StyleValue const> parse_resolution_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_time_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_time_percentage_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_view_timeline_inset_value(TokenStream<ComponentValue>&);
RefPtr<ScrollFunctionStyleValue const> parse_scroll_function_value(TokenStream<ComponentValue>&);

View File

@@ -270,197 +270,93 @@ Optional<Parser::PropertyAndValue> Parser::parse_css_value_for_properties(Readon
// <integer>/<number> come before <length>, so that 0 is not interpreted as a <length> 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);
auto transaction = tokens.begin_transaction();
if (auto value = parse_integer_value(tokens)) {
if ((value->is_integer() && property_accepts_integer(*property, value->as_integer().integer())) || !value->is_integer()) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
if (auto value = parse_integer_value(tokens, property_accepted_ranges_by_value_type(*property).get(ValueType::Integer).value()))
return PropertyAndValue { *property, value };
}
if (auto property = any_property_accepts_type(property_ids, ValueType::Number); property.has_value()) {
auto context_guard = push_temporary_value_parsing_context(*property);
auto transaction = tokens.begin_transaction();
if (auto value = parse_number_value(tokens)) {
if ((value->is_number() && property_accepts_number(*property, value->as_number().number())) || !value->is_number()) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
if (auto value = parse_number_value(tokens, property_accepted_ranges_by_value_type(*property).get(ValueType::Number).value()))
return PropertyAndValue { *property, value };
}
if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value()) {
auto const& valid_ranges = property_accepted_ranges_by_value_type(*property);
auto const& angle_range = valid_ranges.get(ValueType::Angle).value();
auto context_guard = push_temporary_value_parsing_context(*property);
auto transaction = tokens.begin_transaction();
if (property_resolves_percentages_relative_to(*property) == ValueType::Angle) {
if (auto value = parse_angle_percentage_value(tokens)) {
if (value->is_calculated()) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_angle() && property_accepts_angle(*property, value->as_angle().angle())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
}
if (auto value = parse_angle_value(tokens)) {
if (value->is_calculated()) {
transaction.commit();
if (auto value = parse_angle_percentage_value(tokens, angle_range, valid_ranges.get(ValueType::Percentage).value()))
return PropertyAndValue { *property, value };
}
if (value->is_angle() && property_accepts_angle(*property, value->as_angle().angle())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
if (auto value = parse_angle_value(tokens, angle_range))
return PropertyAndValue { *property, value };
}
if (auto property = any_property_accepts_type(property_ids, ValueType::Flex); property.has_value()) {
auto context_guard = push_temporary_value_parsing_context(*property);
auto transaction = tokens.begin_transaction();
if (auto value = parse_flex_value(tokens)) {
if (value->is_calculated()) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_flex() && property_accepts_flex(*property, value->as_flex().flex())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
if (auto value = parse_flex_value(tokens, property_accepted_ranges_by_value_type(*property).get(ValueType::Flex).value()))
return PropertyAndValue { *property, value };
}
if (auto property = any_property_accepts_type(property_ids, ValueType::Frequency); property.has_value()) {
auto const& valid_ranges = property_accepted_ranges_by_value_type(*property);
auto const& frequency_range = valid_ranges.get(ValueType::Frequency).value();
auto context_guard = push_temporary_value_parsing_context(*property);
auto transaction = tokens.begin_transaction();
if (property_resolves_percentages_relative_to(*property) == ValueType::Frequency) {
if (auto value = parse_frequency_percentage_value(tokens)) {
if (value->is_calculated()) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_frequency() && property_accepts_frequency(*property, value->as_frequency().frequency())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
}
if (auto value = parse_frequency_value(tokens)) {
if (value->is_calculated()) {
transaction.commit();
if (auto value = parse_frequency_percentage_value(tokens, frequency_range, valid_ranges.get(ValueType::Percentage).value()))
return PropertyAndValue { *property, value };
}
if (value->is_frequency() && property_accepts_frequency(*property, value->as_frequency().frequency())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
if (auto value = parse_frequency_value(tokens, frequency_range))
return PropertyAndValue { *property, value };
}
if (auto parsed = parse_for_type(ValueType::FitContent); parsed.has_value())
return parsed.release_value();
if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value()) {
auto const& valid_ranges = property_accepted_ranges_by_value_type(*property);
auto const& length_range = valid_ranges.get(ValueType::Length).value();
auto context_guard = push_temporary_value_parsing_context(*property);
auto transaction = tokens.begin_transaction();
if (property_resolves_percentages_relative_to(*property) == ValueType::Length) {
if (auto value = parse_length_percentage_value(tokens)) {
if (value->is_calculated() || value->is_anchor_size()) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_length() && property_accepts_length(*property, value->as_length().length())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
}
if (auto value = parse_length_value(tokens)) {
if (value->is_calculated() || value->is_anchor_size()) {
transaction.commit();
if (auto value = parse_length_percentage_value(tokens, length_range, valid_ranges.get(ValueType::Percentage).value()))
return PropertyAndValue { *property, value };
}
if (value->is_length() && property_accepts_length(*property, value->as_length().length())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
if (auto value = parse_length_value(tokens, length_range))
return PropertyAndValue { *property, value };
}
if (auto property = any_property_accepts_type(property_ids, ValueType::Resolution); property.has_value()) {
auto context_guard = push_temporary_value_parsing_context(*property);
auto transaction = tokens.begin_transaction();
if (auto value = parse_resolution_value(tokens)) {
if (value->is_calculated()) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_resolution() && property_accepts_resolution(*property, value->as_resolution().resolution())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
if (auto value = parse_resolution_value(tokens, property_accepted_ranges_by_value_type(*property).get(ValueType::Resolution).value()))
return PropertyAndValue { *property, value };
}
if (auto property = any_property_accepts_type(property_ids, ValueType::Time); property.has_value()) {
auto const& valid_ranges = property_accepted_ranges_by_value_type(*property);
auto const& time_range = valid_ranges.get(ValueType::Time).value();
auto context_guard = push_temporary_value_parsing_context(*property);
auto transaction = tokens.begin_transaction();
if (property_resolves_percentages_relative_to(*property) == ValueType::Time) {
if (auto value = parse_time_percentage_value(tokens)) {
if (value->is_calculated()) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_time() && property_accepts_time(*property, value->as_time().time())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
}
if (auto value = parse_time_value(tokens)) {
if (value->is_calculated()) {
transaction.commit();
if (auto value = parse_time_percentage_value(tokens, time_range, valid_ranges.get(ValueType::Percentage).value()))
return PropertyAndValue { *property, value };
}
if (value->is_time() && property_accepts_time(*property, value->as_time().time())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
if (auto value = parse_time_value(tokens, time_range))
return PropertyAndValue { *property, value };
}
// <percentage> is checked after the <foo-percentage> types.
if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value()) {
auto context_guard = push_temporary_value_parsing_context(*property);
auto transaction = tokens.begin_transaction();
if (auto value = parse_percentage_value(tokens)) {
if (value->is_calculated()) {
transaction.commit();
return PropertyAndValue { *property, value };
}
if (value->is_percentage() && property_accepts_percentage(*property, value->as_percentage().percentage())) {
transaction.commit();
return PropertyAndValue { *property, value };
}
}
if (auto value = parse_percentage_value(tokens, property_accepted_ranges_by_value_type(*property).get(ValueType::Percentage).value()))
return PropertyAndValue { *property, value };
}
if (auto parsed = parse_for_type(ValueType::Paint); parsed.has_value())
@@ -991,7 +887,7 @@ RefPtr<StyleValue const> Parser::parse_counter_definitions_value(TokenStream<Com
tokens.discard_whitespace();
// <integer>?
definition.value = parse_integer_value(tokens);
definition.value = parse_integer_value(tokens, infinite_integer_range);
if (!definition.value && !definition.is_reversed)
definition.value = IntegerStyleValue::create(default_value_if_not_reversed);
@@ -1079,9 +975,9 @@ RefPtr<StyleValue const> Parser::parse_cursor_value(TokenStream<ComponentValue>&
if (part_tokens.has_next_token()) {
// <number>{2}, which are the x and y coordinates of the hotspot
auto x = parse_number_value(part_tokens);
auto x = parse_number_value(part_tokens, infinite_range);
part_tokens.discard_whitespace();
auto y = parse_number_value(part_tokens);
auto y = parse_number_value(part_tokens, infinite_range);
part_tokens.discard_whitespace();
if (!x || !y || part_tokens.has_next_token())
return nullptr;
@@ -1467,7 +1363,7 @@ RefPtr<StyleValue const> Parser::parse_single_background_position_x_or_y_value(T
return nullptr;
}
value = parse_length_percentage_value(tokens);
value = parse_length_percentage_value(tokens, infinite_range, infinite_range);
if (!value) {
transaction.commit();
return EdgeStyleValue::create(relative_edge, {});
@@ -1735,14 +1631,10 @@ RefPtr<StyleValue const> Parser::parse_border_image_slice_value(TokenStream<Comp
Vector<ValueComparingNonnullRefPtr<StyleValue const>> number_percentages;
while (number_percentages.size() <= 4 && tokens.has_next_token()) {
auto number_percentage = parse_number_percentage_value(tokens);
auto number_percentage = parse_number_percentage_value(tokens, non_negative_range, non_negative_range);
if (!number_percentage)
break;
if (number_percentage->is_number() && !property_accepts_number(PropertyID::BorderImageSlice, number_percentage->as_number().number()))
return nullptr;
if (number_percentage->is_percentage() && !property_accepts_percentage(PropertyID::BorderImageSlice, number_percentage->as_percentage().percentage()))
return nullptr;
number_percentages.append(number_percentage.release_nonnull());
tokens.discard_whitespace();
}
@@ -1804,7 +1696,7 @@ RefPtr<StyleValue const> Parser::parse_border_radius_value(TokenStream<Component
auto transaction = tokens.begin_transaction();
tokens.discard_whitespace();
auto horizontal = parse_length_percentage_value(tokens);
auto horizontal = parse_length_percentage_value(tokens, non_negative_range, non_negative_range);
tokens.discard_whitespace();
if (tokens.next_token().is_delim('/')) {
@@ -1812,7 +1704,7 @@ RefPtr<StyleValue const> Parser::parse_border_radius_value(TokenStream<Component
tokens.discard_whitespace();
}
auto vertical = parse_length_percentage_value(tokens);
auto vertical = parse_length_percentage_value(tokens, non_negative_range, non_negative_range);
if (horizontal && vertical) {
transaction.commit();
return BorderRadiusStyleValue::create(horizontal.release_nonnull(), vertical.release_nonnull());
@@ -1960,7 +1852,7 @@ RefPtr<StyleValue const> Parser::parse_single_shadow_value(TokenStream<Component
}
auto const& token = tokens.next_token();
if (auto maybe_offset_x = parse_length_value(tokens); maybe_offset_x) {
if (auto maybe_offset_x = parse_length_value(tokens, infinite_range); maybe_offset_x) {
// horizontal offset
if (offset_x)
return nullptr;
@@ -1970,7 +1862,7 @@ RefPtr<StyleValue const> Parser::parse_single_shadow_value(TokenStream<Component
tokens.discard_whitespace();
if (!tokens.has_next_token())
return nullptr;
auto maybe_offset_y = parse_length_value(tokens);
auto maybe_offset_y = parse_length_value(tokens, infinite_range);
if (!maybe_offset_y)
return nullptr;
offset_y = maybe_offset_y;
@@ -1981,21 +1873,17 @@ RefPtr<StyleValue const> Parser::parse_single_shadow_value(TokenStream<Component
break;
m_value_context.append(SpecialContext::ShadowBlurRadius);
auto maybe_blur_radius = parse_length_value(tokens);
auto maybe_blur_radius = parse_length_value(tokens, non_negative_range);
m_value_context.take_last();
if (!maybe_blur_radius)
continue;
blur_radius = maybe_blur_radius;
if (blur_radius->is_length() && blur_radius->as_length().raw_value() < 0)
return nullptr;
if (blur_radius->is_percentage() && blur_radius->as_percentage().raw_value() < 0)
return nullptr;
// spread distance (optional)
tokens.discard_whitespace();
if (!tokens.has_next_token())
break;
auto maybe_spread_distance = parse_length_value(tokens);
auto maybe_spread_distance = parse_length_value(tokens, infinite_range);
if (!maybe_spread_distance)
continue;
@@ -2100,7 +1988,7 @@ RefPtr<StyleValue const> Parser::parse_rotate_value(TokenStream<ComponentValue>&
auto transaction = tokens.begin_transaction();
auto angle = parse_angle_value(tokens);
auto angle = parse_angle_value(tokens, infinite_range);
tokens.discard_whitespace();
// <angle>
@@ -2127,7 +2015,7 @@ RefPtr<StyleValue const> Parser::parse_rotate_value(TokenStream<ComponentValue>&
tokens.discard_whitespace();
if (!angle)
angle = parse_angle_value(tokens);
angle = parse_angle_value(tokens, infinite_range);
if (angle) {
transaction.commit();
@@ -2147,7 +2035,7 @@ RefPtr<StyleValue const> Parser::parse_rotate_value(TokenStream<ComponentValue>&
auto numbers_transaction = tokens.begin_transaction();
StyleValueVector numbers;
for (size_t i = 0; i < 3; ++i) {
if (auto number = parse_number_value(tokens); number) {
if (auto number = parse_number_value(tokens, infinite_range); number) {
numbers.append(number.release_nonnull());
} else {
return {};
@@ -2162,7 +2050,7 @@ RefPtr<StyleValue const> Parser::parse_rotate_value(TokenStream<ComponentValue>&
tokens.discard_whitespace();
if (!angle)
angle = parse_angle_value(tokens);
angle = parse_angle_value(tokens, infinite_range);
if (angle) {
auto numbers = maybe_numbers.release_value();
@@ -2190,20 +2078,16 @@ RefPtr<StyleValue const> Parser::parse_stroke_dasharray_value(TokenStream<Compon
tokens.discard_whitespace();
// A <dasharray> is a list of comma and/or white space separated <number> or <length-percentage> values. A <number> value represents a value in user units.
auto value = parse_number_value(tokens);
if (value && value->is_number() && value->as_number().number() < 0)
return {};
// If any value in the list is negative, the <dasharray> value is invalid.
auto value = parse_number_value(tokens, non_negative_range);
if (value) {
dashes.append(value.release_nonnull());
} else {
auto value = parse_length_percentage_value(tokens);
auto value = parse_length_percentage_value(tokens, non_negative_range, non_negative_range);
if (!value)
return {};
if (value->is_percentage() && value->as_percentage().raw_value() < 0)
return {};
if (value->is_length() && value->as_length().raw_value() < 0)
return {};
dashes.append(value.release_nonnull());
}
@@ -2864,9 +2748,7 @@ RefPtr<StyleValue const> Parser::parse_font_feature_settings_value(TokenStream<C
tag_tokens.discard_whitespace();
RefPtr<StyleValue const> value;
if (tag_tokens.has_next_token()) {
if (auto integer = parse_integer_value(tag_tokens)) {
if (integer->is_integer() && integer->as_integer().integer() < 0)
return nullptr;
if (auto integer = parse_integer_value(tag_tokens, non_negative_integer_range)) {
value = integer;
} else {
// A value of on is synonymous with 1 and off is synonymous with 0.
@@ -2919,7 +2801,7 @@ RefPtr<StyleValue const> Parser::parse_font_variation_settings_value(TokenStream
tag_tokens.discard_whitespace();
auto opentype_tag = parse_opentype_tag_value(tag_tokens);
tag_tokens.discard_whitespace();
auto number = parse_number_value(tag_tokens);
auto number = parse_number_value(tag_tokens, infinite_range);
tag_tokens.discard_whitespace();
if (!opentype_tag || !number || tag_tokens.has_next_token())
@@ -3423,7 +3305,7 @@ RefPtr<StyleValue const> Parser::parse_math_depth_value(TokenStream<ComponentVal
auto add_tokens = TokenStream { function.value };
add_tokens.discard_whitespace();
if (auto integer_value = parse_integer_value(add_tokens)) {
if (auto integer_value = parse_integer_value(add_tokens, infinite_integer_range)) {
add_tokens.discard_whitespace();
if (add_tokens.has_next_token())
return nullptr;
@@ -3435,7 +3317,7 @@ RefPtr<StyleValue const> Parser::parse_math_depth_value(TokenStream<ComponentVal
}
// <integer>
if (auto integer_value = parse_integer_value(tokens)) {
if (auto integer_value = parse_integer_value(tokens, infinite_integer_range)) {
transaction.commit();
return integer_value;
}
@@ -3449,7 +3331,7 @@ RefPtr<StyleValue const> Parser::parse_overflow_clip_margin_value(TokenStream<Co
// <visual-box> || <length [0,∞]>
// FIXME: Implement the <visual-box> part of this.
if (auto length = parse_length_value(tokens)) {
if (auto length = parse_length_value(tokens, non_negative_range)) {
return length.release_nonnull();
}
@@ -4265,7 +4147,7 @@ RefPtr<StyleValue const> Parser::parse_text_indent_value(TokenStream<ComponentVa
while (tokens.has_next_token()) {
if (!length_percentage) {
if (auto parsed = parse_length_percentage_value(tokens)) {
if (auto parsed = parse_length_percentage_value(tokens, infinite_range, infinite_range)) {
length_percentage = parsed.release_nonnull();
tokens.discard_whitespace();
continue;
@@ -4494,7 +4376,7 @@ RefPtr<StyleValue const> Parser::parse_transform_origin_value(TokenStream<Compon
}
auto second_value = to_axis_offset(parse_css_value_for_property(PropertyID::TransformOrigin, tokens));
auto third_value = parse_length_value(tokens);
auto third_value = parse_length_value(tokens, infinite_range);
if (!first_value.has_value() || !second_value.has_value())
return nullptr;
@@ -4615,7 +4497,7 @@ RefPtr<StyleValue const> Parser::parse_translate_value(TokenStream<ComponentValu
auto transaction = tokens.begin_transaction();
// <length-percentage> [ <length-percentage> <length>? ]?
auto maybe_x = parse_length_percentage_value(tokens);
auto maybe_x = parse_length_percentage_value(tokens, infinite_range, infinite_range);
if (!maybe_x)
return nullptr;
@@ -4625,7 +4507,7 @@ RefPtr<StyleValue const> Parser::parse_translate_value(TokenStream<ComponentValu
return TransformationStyleValue::create(PropertyID::Translate, TransformFunction::Translate, { maybe_x.release_nonnull(), LengthStyleValue::create(Length::make_px(0)) });
}
auto maybe_y = parse_length_percentage_value(tokens);
auto maybe_y = parse_length_percentage_value(tokens, infinite_range, infinite_range);
if (!maybe_y)
return nullptr;
@@ -4636,7 +4518,7 @@ RefPtr<StyleValue const> Parser::parse_translate_value(TokenStream<ComponentValu
}
auto context_guard = push_temporary_value_parsing_context(SpecialContext::TranslateZArgument);
auto maybe_z = parse_length_value(tokens);
auto maybe_z = parse_length_value(tokens, infinite_range);
if (!maybe_z)
return nullptr;
@@ -4656,7 +4538,7 @@ RefPtr<StyleValue const> Parser::parse_scale_value(TokenStream<ComponentValue>&
auto transaction = tokens.begin_transaction();
// [ <number> | <percentage> ]{1,3}
auto maybe_x = parse_number_percentage_value(tokens);
auto maybe_x = parse_number_percentage_value(tokens, infinite_range, infinite_range);
if (!maybe_x)
return nullptr;
@@ -4666,7 +4548,7 @@ RefPtr<StyleValue const> Parser::parse_scale_value(TokenStream<ComponentValue>&
return TransformationStyleValue::create(PropertyID::Scale, TransformFunction::Scale, { *maybe_x, *maybe_x });
}
auto maybe_y = parse_number_percentage_value(tokens);
auto maybe_y = parse_number_percentage_value(tokens, infinite_range, infinite_range);
if (!maybe_y)
return nullptr;
@@ -4676,7 +4558,7 @@ RefPtr<StyleValue const> Parser::parse_scale_value(TokenStream<ComponentValue>&
return TransformationStyleValue::create(PropertyID::Scale, TransformFunction::Scale, { maybe_x.release_nonnull(), maybe_y.release_nonnull() });
}
auto maybe_z = parse_number_percentage_value(tokens);
auto maybe_z = parse_number_percentage_value(tokens, infinite_range, infinite_range);
if (!maybe_z)
return nullptr;
@@ -5494,11 +5376,10 @@ RefPtr<StyleValue const> Parser::parse_filter_value_list_value(TokenStream<Compo
return FilterOperation::Blur { LengthStyleValue::create(Length::make_px(0)) };
// Negative values are not allowed.
auto blur_radius = parse_length_value(tokens);
auto blur_radius = parse_length_value(tokens, non_negative_range);
tokens.discard_whitespace();
if (!blur_radius || (blur_radius->is_length() && blur_radius->as_length().raw_value() < 0))
if (!blur_radius)
return {};
return if_no_more_tokens_return(FilterOperation::Blur { blur_radius.release_nonnull() });
} else if (filter_token == FilterToken::DropShadow) {
if (!tokens.has_next_token())
@@ -5510,18 +5391,18 @@ RefPtr<StyleValue const> Parser::parse_filter_value_list_value(TokenStream<Compo
RefPtr<StyleValue const> maybe_radius;
auto maybe_color = parse_color_value(tokens);
tokens.discard_whitespace();
auto x_offset = parse_length_value(tokens);
auto x_offset = parse_length_value(tokens, infinite_range);
tokens.discard_whitespace();
if (!x_offset || !tokens.has_next_token())
return {};
auto y_offset = parse_length_value(tokens);
auto y_offset = parse_length_value(tokens, infinite_range);
tokens.discard_whitespace();
if (!y_offset)
return {};
if (tokens.has_next_token()) {
maybe_radius = parse_length_value(tokens);
maybe_radius = parse_length_value(tokens, infinite_range);
tokens.discard_whitespace();
if (!maybe_color && (!maybe_radius || tokens.has_next_token())) {
maybe_color = parse_color_value(tokens);
@@ -5550,7 +5431,7 @@ RefPtr<StyleValue const> Parser::parse_filter_value_list_value(TokenStream<Compo
return {};
}
if (auto angle = parse_angle_value(tokens))
if (auto angle = parse_angle_value(tokens, infinite_range))
return if_no_more_tokens_return(FilterOperation::HueRotate { angle.release_nonnull() });
return {};
@@ -5582,18 +5463,12 @@ RefPtr<StyleValue const> Parser::parse_filter_value_list_value(TokenStream<Compo
if (!tokens.has_next_token())
return FilterOperation::Color { filter_token_to_operation(filter_token), NumberStyleValue::create(1) };
auto amount = parse_number_percentage_value(tokens);
// Negative values are not allowed.
auto amount = parse_number_percentage_value(tokens, non_negative_range, non_negative_range);
if (!amount)
return {};
// Negative values are not allowed.
if (amount->is_percentage() && amount->as_percentage().percentage().value() < 0)
return {};
if (amount->is_number() && amount->as_number().number() < 0)
return {};
// Values of amount over 100% are allowed but UAs must clamp the values to 1.
// NB: Only for grayscale(), invert(), opacity() and sepia() functions
if (first_is_one_of(filter_token, FilterToken::Grayscale, FilterToken::Invert, FilterToken::Opacity, FilterToken::Sepia)) {

View File

@@ -578,12 +578,12 @@ RefPtr<UnicodeRangeStyleValue const> Parser::parse_unicode_range_value(TokenStre
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_integer_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_integer_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
auto const& peek_token = tokens.next_token();
if (peek_token.is(Token::Type::Number) && peek_token.token().is_integer()) {
if (peek_token.is(Token::Type::Number) && peek_token.token().is_integer() && accepted_range.contains(peek_token.token().to_integer())) {
tokens.discard_a_token(); // integer
return IntegerStyleValue::create(peek_token.token().to_integer());
}
@@ -599,12 +599,12 @@ RefPtr<StyleValue const> Parser::parse_integer_value(TokenStream<ComponentValue>
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_number_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_number_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
auto const& peek_token = tokens.next_token();
if (peek_token.is(Token::Type::Number)) {
if (peek_token.is(Token::Type::Number) && accepted_range.contains(peek_token.token().number_value())) {
tokens.discard_a_token(); // number
return NumberStyleValue::create(peek_token.token().number_value());
}
@@ -620,12 +620,12 @@ RefPtr<StyleValue const> Parser::parse_number_value(TokenStream<ComponentValue>&
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_number_percentage_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_number_percentage_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_number_range, NumericRange const& accepted_percentage_range)
{
// Parses [<percentage> | <number>] (which is equivalent to [<alpha-value>])
if (auto value = parse_number_value(tokens))
if (auto value = parse_number_value(tokens, accepted_number_range))
return value;
if (auto value = parse_percentage_value(tokens))
if (auto value = parse_percentage_value(tokens, accepted_percentage_range))
return value;
return nullptr;
}
@@ -633,9 +633,9 @@ RefPtr<StyleValue const> Parser::parse_number_percentage_value(TokenStream<Compo
RefPtr<StyleValue const> Parser::parse_number_percentage_none_value(TokenStream<ComponentValue>& tokens)
{
// Parses [<percentage> | <number> | none] (which is equivalent to [<alpha-value> | none])
if (auto value = parse_number_value(tokens))
if (auto value = parse_number_value(tokens, infinite_range))
return value;
if (auto value = parse_percentage_value(tokens))
if (auto value = parse_percentage_value(tokens, infinite_range))
return value;
if (tokens.next_token().is_ident("none"sv)) {
@@ -646,12 +646,12 @@ RefPtr<StyleValue const> Parser::parse_number_percentage_none_value(TokenStream<
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_percentage_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_percentage_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
auto const& peek_token = tokens.next_token();
if (peek_token.is(Token::Type::Percentage)) {
if (peek_token.is(Token::Type::Percentage) && accepted_range.contains(peek_token.token().percentage())) {
tokens.discard_a_token(); // percentage
return PercentageStyleValue::create(Percentage(peek_token.token().percentage()));
}
@@ -702,7 +702,7 @@ RefPtr<StyleValue const> Parser::parse_anchor(TokenStream<ComponentValue>& token
anchor_side_value = parse_keyword_value(argument_tokens);
if (!anchor_side_value) {
// FIXME: Only percentages are allowed here, but we parse a length-percentage so that calc values are handled.
anchor_side_value = parse_length_percentage_value(argument_tokens);
anchor_side_value = parse_length_percentage_value(argument_tokens, infinite_range, infinite_range);
if (!anchor_side_value)
return {};
@@ -716,7 +716,7 @@ RefPtr<StyleValue const> Parser::parse_anchor(TokenStream<ComponentValue>& token
if (argument_tokens.next_token().is(Token::Type::Comma)) {
argument_tokens.discard_a_token();
argument_tokens.discard_whitespace();
fallback_value = parse_length_percentage_value(argument_tokens);
fallback_value = parse_length_percentage_value(argument_tokens, infinite_range, infinite_range);
if (!fallback_value)
fallback_value = parse_anchor(argument_tokens);
if (!fallback_value)
@@ -850,7 +850,7 @@ RefPtr<StyleValue const> Parser::parse_anchor_size(TokenStream<ComponentValue>&
// FIXME: Nested anchor sizes should actually be handled by parse_length_percentage()
if (auto nested_anchor_size = parse_anchor_size(argument_tokens))
fallback_value = nested_anchor_size.release_nonnull();
else if (auto length_percentage = parse_length_percentage_value(argument_tokens))
else if (auto length_percentage = parse_length_percentage_value(argument_tokens, infinite_range, infinite_range))
fallback_value = length_percentage.release_nonnull();
if (!fallback_value && comma_present)
@@ -864,7 +864,7 @@ RefPtr<StyleValue const> Parser::parse_anchor_size(TokenStream<ComponentValue>&
return AnchorSizeStyleValue::create(anchor_name, anchor_size, fallback_value);
}
static RefPtr<AngleStyleValue const> parse_literal_angle_value(TokenStream<ComponentValue>& tokens, bool is_parsing_svg_presentation_attribute)
static RefPtr<AngleStyleValue const> parse_literal_angle_value(TokenStream<ComponentValue>& tokens, bool is_parsing_svg_presentation_attribute, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
@@ -872,8 +872,13 @@ static RefPtr<AngleStyleValue const> parse_literal_angle_value(TokenStream<Compo
auto transaction = tokens.begin_transaction();
auto& dimension_token = tokens.consume_a_token().token();
if (auto angle_type = string_to_angle_unit(dimension_token.dimension_unit()); angle_type.has_value()) {
Angle angle { dimension_token.dimension_value(), angle_type.release_value() };
if (!accepted_range.contains(angle.to_degrees()))
return nullptr;
transaction.commit();
return AngleStyleValue::create(Angle { (dimension_token.dimension_value()), angle_type.release_value() });
return AngleStyleValue::create(move(angle));
}
return nullptr;
}
@@ -883,26 +888,30 @@ static RefPtr<AngleStyleValue const> parse_literal_angle_value(TokenStream<Compo
// FIXME: How should these numbers be interpreted? https://github.com/w3c/svgwg/issues/792
// For now: Convert to an angle in degrees.
if (tokens.next_token().is(Token::Type::Number) && is_parsing_svg_presentation_attribute) {
auto numeric_value = tokens.consume_a_token().token().number_value();
return AngleStyleValue::create(Angle::make_degrees(numeric_value));
auto angle = Angle::make_degrees(tokens.consume_a_token().token().number_value());
if (!accepted_range.contains(angle.to_degrees()))
return nullptr;
return AngleStyleValue::create(move(angle));
}
return nullptr;
}
static RefPtr<PercentageStyleValue const> parse_literal_percentage_value(TokenStream<ComponentValue>& tokens)
static RefPtr<PercentageStyleValue const> parse_literal_percentage_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
if (tokens.next_token().is(Token::Type::Percentage))
if (tokens.next_token().is(Token::Type::Percentage) && accepted_range.contains(tokens.next_token().token().percentage()))
return PercentageStyleValue::create(Percentage { tokens.consume_a_token().token().percentage() });
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_angle_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_angle_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
if (auto literal_angle = parse_literal_angle_value(tokens, is_parsing_svg_presentation_attribute()))
if (auto literal_angle = parse_literal_angle_value(tokens, is_parsing_svg_presentation_attribute(), accepted_range))
return literal_angle;
auto transaction = tokens.begin_transaction();
@@ -913,12 +922,12 @@ RefPtr<StyleValue const> Parser::parse_angle_value(TokenStream<ComponentValue>&
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_angle_percentage_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_angle_percentage_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_angle_range, NumericRange const& accepted_percentage_range)
{
if (auto literal_angle = parse_literal_angle_value(tokens, is_parsing_svg_presentation_attribute()))
if (auto literal_angle = parse_literal_angle_value(tokens, is_parsing_svg_presentation_attribute(), accepted_angle_range))
return literal_angle;
if (auto literal_percentage = parse_literal_percentage_value(tokens))
if (auto literal_percentage = parse_literal_percentage_value(tokens, accepted_percentage_range))
return literal_percentage;
auto transaction = tokens.begin_transaction();
@@ -929,7 +938,7 @@ RefPtr<StyleValue const> Parser::parse_angle_percentage_value(TokenStream<Compon
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_flex_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_flex_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
@@ -937,8 +946,13 @@ RefPtr<StyleValue const> Parser::parse_flex_value(TokenStream<ComponentValue>& t
auto transaction = tokens.begin_transaction();
auto& dimension_token = tokens.consume_a_token().token();
if (auto flex_type = string_to_flex_unit(dimension_token.dimension_unit()); flex_type.has_value()) {
Flex flex { (dimension_token.dimension_value()), flex_type.release_value() };
if (!accepted_range.contains(flex.to_fr()))
return nullptr;
transaction.commit();
return FlexStyleValue::create(Flex { (dimension_token.dimension_value()), flex_type.release_value() });
return FlexStyleValue::create(move(flex));
}
return nullptr;
}
@@ -951,7 +965,7 @@ RefPtr<StyleValue const> Parser::parse_flex_value(TokenStream<ComponentValue>& t
return nullptr;
}
static RefPtr<FrequencyStyleValue const> parse_literal_frequency_value(TokenStream<ComponentValue>& tokens)
static RefPtr<FrequencyStyleValue const> parse_literal_frequency_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
@@ -959,17 +973,22 @@ static RefPtr<FrequencyStyleValue const> parse_literal_frequency_value(TokenStre
auto transaction = tokens.begin_transaction();
auto& dimension_token = tokens.consume_a_token().token();
if (auto frequency_type = string_to_frequency_unit(dimension_token.dimension_unit()); frequency_type.has_value()) {
Frequency frequency { dimension_token.dimension_value(), frequency_type.release_value() };
if (!accepted_range.contains(frequency.to_hertz()))
return nullptr;
transaction.commit();
return FrequencyStyleValue::create(Frequency { (dimension_token.dimension_value()), frequency_type.release_value() });
return FrequencyStyleValue::create(move(frequency));
}
}
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_frequency_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_frequency_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
if (auto literal_frequency = parse_literal_frequency_value(tokens))
if (auto literal_frequency = parse_literal_frequency_value(tokens, accepted_range))
return literal_frequency;
auto transaction = tokens.begin_transaction();
@@ -980,12 +999,12 @@ RefPtr<StyleValue const> Parser::parse_frequency_value(TokenStream<ComponentValu
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_frequency_percentage_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_frequency_percentage_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_frequency_range, NumericRange const& accepted_percentage_range)
{
if (auto literal_frequency = parse_literal_frequency_value(tokens))
if (auto literal_frequency = parse_literal_frequency_value(tokens, accepted_frequency_range))
return literal_frequency;
if (auto literal_percentage = parse_literal_percentage_value(tokens))
if (auto literal_percentage = parse_literal_percentage_value(tokens, accepted_percentage_range))
return literal_percentage;
auto transaction = tokens.begin_transaction();
@@ -996,7 +1015,7 @@ RefPtr<StyleValue const> Parser::parse_frequency_percentage_value(TokenStream<Co
return nullptr;
}
static RefPtr<LengthStyleValue const> parse_literal_length_value(TokenStream<ComponentValue>& tokens, bool context_allows_quirky_length, bool is_parsing_svg_presentation_attribute)
static RefPtr<LengthStyleValue const> parse_literal_length_value(TokenStream<ComponentValue>& tokens, bool context_allows_quirky_length, bool is_parsing_svg_presentation_attribute, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
@@ -1004,8 +1023,18 @@ static RefPtr<LengthStyleValue const> parse_literal_length_value(TokenStream<Com
auto transaction = tokens.begin_transaction();
auto const& dimension_token = tokens.consume_a_token().token();
if (auto length_type = string_to_length_unit(dimension_token.dimension_unit()); length_type.has_value()) {
Length length { dimension_token.dimension_value(), length_type.release_value() };
// NB: Since we can't convert font/viewport relative lengths to their canonical units at parse time it
// doesn't make sense to have non-zero/non-infinite bounds for lengths
VERIFY(accepted_range.min == AK::NumericLimits<float>::lowest() || accepted_range.min == 0);
VERIFY(accepted_range.max == AK::NumericLimits<float>::max() || accepted_range.max == 0);
if (!accepted_range.contains(length.raw_value()))
return nullptr;
transaction.commit();
return LengthStyleValue::create(Length { (dimension_token.dimension_value()), length_type.release_value() });
return LengthStyleValue::create(length);
}
return nullptr;
}
@@ -1014,12 +1043,20 @@ static RefPtr<LengthStyleValue const> parse_literal_length_value(TokenStream<Com
auto transaction = tokens.begin_transaction();
auto numeric_value = tokens.consume_a_token().token().number_value();
if (numeric_value == 0) {
if (!accepted_range.contains(0))
return nullptr;
transaction.commit();
return LengthStyleValue::create(Length::make_px(0));
}
if (context_allows_quirky_length) {
auto nearest_value = CSSPixels::nearest_value_for(numeric_value);
if (!accepted_range.contains(nearest_value.to_double()))
return nullptr;
transaction.commit();
return LengthStyleValue::create(Length::make_px(CSSPixels::nearest_value_for(numeric_value)));
return LengthStyleValue::create(Length::make_px(nearest_value));
}
// https://svgwg.org/svg2-draft/types.html#presentation-attribute-css-value
@@ -1027,17 +1064,22 @@ static RefPtr<LengthStyleValue const> parse_literal_length_value(TokenStream<Com
// FIXME: How should these numbers be interpreted? https://github.com/w3c/svgwg/issues/792
// For now: Convert to a length in pixels.
if (is_parsing_svg_presentation_attribute) {
auto nearest_value = CSSPixels::nearest_value_for(numeric_value);
if (!accepted_range.contains(nearest_value.to_double()))
return nullptr;
transaction.commit();
return LengthStyleValue::create(Length::make_px(CSSPixels::nearest_value_for(numeric_value)));
return LengthStyleValue::create(Length::make_px(nearest_value));
}
}
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_length_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_length_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
if (auto literal_length = parse_literal_length_value(tokens, context_allows_quirky_length(), is_parsing_svg_presentation_attribute()))
if (auto literal_length = parse_literal_length_value(tokens, context_allows_quirky_length(), is_parsing_svg_presentation_attribute(), accepted_range))
return literal_length;
if (tokens.next_token().is_function("anchor-size"sv))
@@ -1051,12 +1093,12 @@ RefPtr<StyleValue const> Parser::parse_length_value(TokenStream<ComponentValue>&
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_length_percentage_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_length_percentage_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_length_range, NumericRange const& accepted_percentage_range)
{
if (auto literal_length = parse_literal_length_value(tokens, context_allows_quirky_length(), is_parsing_svg_presentation_attribute()))
if (auto literal_length = parse_literal_length_value(tokens, context_allows_quirky_length(), is_parsing_svg_presentation_attribute(), accepted_length_range))
return literal_length;
if (auto literal_percentage = parse_literal_percentage_value(tokens))
if (auto literal_percentage = parse_literal_percentage_value(tokens, accepted_percentage_range))
return literal_percentage;
if (tokens.next_token().is_function("anchor-size"sv))
@@ -1070,21 +1112,24 @@ RefPtr<StyleValue const> Parser::parse_length_percentage_value(TokenStream<Compo
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_resolution_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_resolution_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
if (tokens.next_token().is(Token::Type::Dimension)) {
auto transaction = tokens.begin_transaction();
auto& dimension_token = tokens.consume_a_token().token();
// The allowed range of <resolution> values always excludes negative values, in addition to any explicit
// ranges that might be specified.
// https://drafts.csswg.org/css-values-4/#resolution
if (dimension_token.dimension_value() < 0)
return nullptr;
if (auto resolution_type = string_to_resolution_unit(dimension_token.dimension_unit()); resolution_type.has_value()) {
Resolution resolution { dimension_token.dimension_value(), resolution_type.release_value() };
// The allowed range of <resolution> values always excludes negative values, in addition to any explicit
// ranges that might be specified.
// https://drafts.csswg.org/css-values-4/#resolution
if (dimension_token.dimension_value() < 0 || !accepted_range.contains(resolution.to_dots_per_pixel()))
return nullptr;
transaction.commit();
return ResolutionStyleValue::create(Resolution { (dimension_token.dimension_value()), resolution_type.release_value() });
return ResolutionStyleValue::create(move(resolution));
}
return nullptr;
}
@@ -1097,7 +1142,7 @@ RefPtr<StyleValue const> Parser::parse_resolution_value(TokenStream<ComponentVal
return nullptr;
}
static RefPtr<TimeStyleValue const> parse_literal_time_value(TokenStream<ComponentValue>& tokens)
static RefPtr<TimeStyleValue const> parse_literal_time_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
tokens.discard_whitespace();
@@ -1105,17 +1150,22 @@ static RefPtr<TimeStyleValue const> parse_literal_time_value(TokenStream<Compone
auto transaction = tokens.begin_transaction();
auto const& dimension_token = tokens.consume_a_token().token();
if (auto time_type = string_to_time_unit(dimension_token.dimension_unit()); time_type.has_value()) {
Time time { dimension_token.dimension_value(), time_type.release_value() };
if (!accepted_range.contains(time.to_seconds()))
return nullptr;
transaction.commit();
return TimeStyleValue::create(Time { (dimension_token.dimension_value()), time_type.release_value() });
return TimeStyleValue::create(move(time));
}
}
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_time_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_time_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_range)
{
if (auto literal_time = parse_literal_time_value(tokens))
if (auto literal_time = parse_literal_time_value(tokens, accepted_range))
return literal_time;
auto transaction = tokens.begin_transaction();
@@ -1126,12 +1176,12 @@ RefPtr<StyleValue const> Parser::parse_time_value(TokenStream<ComponentValue>& t
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_time_percentage_value(TokenStream<ComponentValue>& tokens)
RefPtr<StyleValue const> Parser::parse_time_percentage_value(TokenStream<ComponentValue>& tokens, NumericRange const& accepted_time_range, NumericRange const& accepted_percentage_range)
{
if (auto literal_time = parse_literal_time_value(tokens))
if (auto literal_time = parse_literal_time_value(tokens, accepted_time_range))
return literal_time;
if (auto literal_percentage = parse_literal_percentage_value(tokens))
if (auto literal_percentage = parse_literal_percentage_value(tokens, accepted_percentage_range))
return literal_percentage;
auto transaction = tokens.begin_transaction();
@@ -1159,7 +1209,7 @@ RefPtr<StyleValue const> Parser::parse_view_timeline_inset_value(TokenStream<Com
continue;
}
if (auto length_percentage = parse_length_percentage_value(tokens)) {
if (auto length_percentage = parse_length_percentage_value(tokens, infinite_range, infinite_range)) {
inset_values.append(length_percentage.release_nonnull());
continue;
}
@@ -1343,7 +1393,7 @@ RefPtr<StyleValue const> Parser::parse_rect_value(TokenStream<ComponentValue>& t
(void)argument_tokens.consume_a_token(); // `auto`
params.append(KeywordStyleValue::create(Keyword::Auto));
} else {
auto maybe_length = parse_length_value(argument_tokens);
auto maybe_length = parse_length_value(argument_tokens, infinite_range);
if (!maybe_length)
return nullptr;
@@ -1389,9 +1439,9 @@ RefPtr<StyleValue const> Parser::parse_hue_none_value(TokenStream<ComponentValue
// Parses [<hue> | none]
// <hue> = <number> | <angle>
if (auto angle = parse_angle_value(tokens))
if (auto angle = parse_angle_value(tokens, infinite_range))
return angle;
if (auto number = parse_number_value(tokens))
if (auto number = parse_number_value(tokens, infinite_range))
return number;
if (tokens.next_token().is_ident("none"sv)) {
tokens.discard_a_token(); // keyword none
@@ -1474,7 +1524,7 @@ RefPtr<StyleValue const> Parser::parse_rgb_color_value(TokenStream<ComponentValu
inner_tokens.discard_a_token(); // comma
inner_tokens.discard_whitespace();
green = parse_number_percentage_value(inner_tokens);
green = parse_number_percentage_value(inner_tokens, infinite_range, infinite_range);
if (!green)
return {};
inner_tokens.discard_whitespace();
@@ -1483,7 +1533,7 @@ RefPtr<StyleValue const> Parser::parse_rgb_color_value(TokenStream<ComponentValu
return {};
inner_tokens.discard_whitespace();
blue = parse_number_percentage_value(inner_tokens);
blue = parse_number_percentage_value(inner_tokens, infinite_range, infinite_range);
if (!blue)
return {};
inner_tokens.discard_whitespace();
@@ -1494,7 +1544,7 @@ RefPtr<StyleValue const> Parser::parse_rgb_color_value(TokenStream<ComponentValu
return {};
inner_tokens.discard_whitespace();
alpha = parse_number_percentage_value(inner_tokens);
alpha = parse_number_percentage_value(inner_tokens, infinite_range, infinite_range);
if (!alpha)
return {};
@@ -1596,7 +1646,7 @@ RefPtr<StyleValue const> Parser::parse_hsl_color_value(TokenStream<ComponentValu
(void)inner_tokens.consume_a_token(); // comma
inner_tokens.discard_whitespace();
s = parse_percentage_value(inner_tokens);
s = parse_percentage_value(inner_tokens, infinite_range);
if (!s)
return {};
inner_tokens.discard_whitespace();
@@ -1605,7 +1655,7 @@ RefPtr<StyleValue const> Parser::parse_hsl_color_value(TokenStream<ComponentValu
return {};
inner_tokens.discard_whitespace();
l = parse_percentage_value(inner_tokens);
l = parse_percentage_value(inner_tokens, infinite_range);
if (!l)
return {};
inner_tokens.discard_whitespace();
@@ -1616,7 +1666,7 @@ RefPtr<StyleValue const> Parser::parse_hsl_color_value(TokenStream<ComponentValu
return {};
inner_tokens.discard_whitespace();
alpha = parse_number_percentage_value(inner_tokens);
alpha = parse_number_percentage_value(inner_tokens, infinite_range, infinite_range);
// The parser has consumed a comma, so the alpha value is now required
if (!alpha)
return {};
@@ -2014,21 +2064,16 @@ RefPtr<StyleValue const> Parser::parse_color_mix_function(TokenStream<ComponentV
{
auto parse_component = [this](TokenStream<ComponentValue>& function_tokens) -> Optional<ColorMixStyleValue::ColorMixComponent> {
function_tokens.discard_whitespace();
auto percentage_style_value = parse_percentage_value(function_tokens);
auto percentage_style_value = parse_percentage_value(function_tokens, { .min = 0, .max = 100 });
function_tokens.discard_whitespace();
auto color_style_value = parse_color_value(function_tokens);
if (!color_style_value)
return {};
function_tokens.discard_whitespace();
if (!percentage_style_value) {
percentage_style_value = parse_percentage_value(function_tokens);
percentage_style_value = parse_percentage_value(function_tokens, { .min = 0, .max = 100 });
function_tokens.discard_whitespace();
}
if (percentage_style_value && !percentage_style_value->is_calculated()) {
auto percentage = percentage_style_value->as_percentage().percentage().value();
if (percentage < 0 || percentage > 100)
return {};
}
return ColorMixStyleValue::ColorMixComponent {
.color = color_style_value.release_nonnull(),
.percentage = move(percentage_style_value),
@@ -2303,7 +2348,7 @@ RefPtr<StyleValue const> Parser::parse_corner_shape_value(TokenStream<ComponentV
return SuperellipseStyleValue::create(NumberStyleValue::create(AK::Infinity<double>));
}
if (auto number_value = parse_number_value(function_tokens); number_value) {
if (auto number_value = parse_number_value(function_tokens, infinite_range); number_value) {
function_tokens.discard_whitespace();
if (function_tokens.has_next_token())
@@ -2548,13 +2593,10 @@ RefPtr<StyleValue const> Parser::parse_nonnegative_integer_symbol_pair_value(Tok
RefPtr<StyleValue const> symbol;
while (tokens.has_next_token()) {
if (auto integer_value = parse_integer_value(tokens)) {
if (auto integer_value = parse_integer_value(tokens, non_negative_integer_range)) {
if (integer)
return nullptr;
if (integer_value->is_integer() && integer_value->as_integer().integer() < 0)
return nullptr;
integer = integer_value;
tokens.discard_whitespace();
continue;
@@ -2589,9 +2631,9 @@ RefPtr<StyleValue const> Parser::parse_ratio_value(TokenStream<ComponentValue>&
auto scope_guard = push_temporary_value_parsing_context(SpecialContext::RatioComponent);
tokens.discard_whitespace();
auto numerator = parse_number_value(tokens);
auto numerator = parse_number_value(tokens, non_negative_range);
if (!numerator || (numerator->is_number() && numerator->as_number().number() < 0))
if (!numerator)
return nullptr;
tokens.discard_whitespace();
@@ -2600,8 +2642,8 @@ RefPtr<StyleValue const> Parser::parse_ratio_value(TokenStream<ComponentValue>&
tokens.discard_a_token();
tokens.discard_whitespace();
auto denominator = parse_number_value(tokens);
if (!denominator || (denominator->is_number() && denominator->as_number().number() < 0))
auto denominator = parse_number_value(tokens, non_negative_range);
if (!denominator)
return nullptr;
transaction.commit();
@@ -2775,7 +2817,7 @@ RefPtr<PositionStyleValue const> Parser::parse_position_value(TokenStream<Compon
}
// [ <length-percentage> ]
if (auto maybe_percentage = parse_length_percentage_value(tokens)) {
if (auto maybe_percentage = parse_length_percentage_value(tokens, infinite_range, infinite_range)) {
transaction.commit();
return PositionStyleValue::create(EdgeStyleValue::create({}, maybe_percentage), EdgeStyleValue::create(PositionEdge::Center, {}));
}
@@ -2834,7 +2876,7 @@ RefPtr<PositionStyleValue const> Parser::parse_position_value(TokenStream<Compon
return EdgeStyleValue::create(position, {});
}
auto maybe_length = parse_length_percentage_value(tokens);
auto maybe_length = parse_length_percentage_value(tokens, infinite_range, infinite_range);
if (!maybe_length)
return nullptr;
@@ -2872,7 +2914,7 @@ RefPtr<PositionStyleValue const> Parser::parse_position_value(TokenStream<Compon
tokens.discard_whitespace();
auto maybe_length = parse_length_percentage_value(tokens);
auto maybe_length = parse_length_percentage_value(tokens, infinite_range, infinite_range);
if (!maybe_length)
return {};
@@ -2932,7 +2974,7 @@ RefPtr<PositionStyleValue const> Parser::parse_position_value(TokenStream<Compon
tokens.discard_whitespace();
auto maybe_length = parse_length_percentage_value(tokens);
auto maybe_length = parse_length_percentage_value(tokens, infinite_range, infinite_range);
if (maybe_length) {
// 'center' cannot be followed by a <length-percentage>
if (maybe_position.value() == PositionEdge::Center && maybe_length)
@@ -3050,17 +3092,17 @@ RefPtr<StyleValue const> Parser::parse_easing_value(TokenStream<ComponentValue>&
RefPtr<StyleValue const> first_input;
RefPtr<StyleValue const> second_input;
if (auto maybe_output = parse_number_value(argument_tokens))
if (auto maybe_output = parse_number_value(argument_tokens, infinite_range))
output = maybe_output;
if (auto maybe_first_input = parse_percentage_value(argument_tokens)) {
if (auto maybe_first_input = parse_percentage_value(argument_tokens, infinite_range)) {
first_input = maybe_first_input;
if (auto maybe_second_input = parse_percentage_value(argument_tokens)) {
if (auto maybe_second_input = parse_percentage_value(argument_tokens, infinite_range)) {
second_input = maybe_second_input;
}
}
if (auto maybe_output = parse_number_value(argument_tokens)) {
if (auto maybe_output = parse_number_value(argument_tokens, infinite_range)) {
if (output)
return nullptr;
output = maybe_output;
@@ -3090,24 +3132,20 @@ RefPtr<StyleValue const> Parser::parse_easing_value(TokenStream<ComponentValue>&
return nullptr;
}
auto parse_argument = [this, &comma_separated_arguments](auto index) {
auto parse_argument = [this, &comma_separated_arguments](auto index, NumericRange accepted_range) {
TokenStream<ComponentValue> argument_tokens { comma_separated_arguments[index] };
return parse_number_value(argument_tokens);
return parse_number_value(argument_tokens, accepted_range);
};
m_value_context.append(SpecialContext::CubicBezierFunctionXCoordinate);
auto x1 = parse_argument(0);
auto x2 = parse_argument(2);
auto x1 = parse_argument(0, { .min = 0, .max = 1 });
auto x2 = parse_argument(2, { .min = 0, .max = 1 });
m_value_context.take_last();
auto y1 = parse_argument(1);
auto y2 = parse_argument(3);
auto y1 = parse_argument(1, infinite_range);
auto y2 = parse_argument(3, infinite_range);
if (!x1 || !y1 || !x2 || !y2)
return nullptr;
if (x1->is_number() && (x1->as_number().number() < 0.0 || x1->as_number().number() > 1.0))
return nullptr;
if (x2->is_number() && (x2->as_number().number() < 0.0 || x2->as_number().number() > 1.0))
return nullptr;
EasingStyleValue::CubicBezier bezier {
x1.release_nonnull(),
@@ -3155,24 +3193,18 @@ RefPtr<StyleValue const> Parser::parse_easing_value(TokenStream<ComponentValue>&
auto const& intervals_argument = comma_separated_arguments[0][0];
auto intervals_token = TokenStream<ComponentValue>::of_single_token(intervals_argument);
m_value_context.append(position == StepPosition::JumpNone ? SpecialContext::StepsIntervalsJumpNone : SpecialContext::StepsIntervalsNormal);
auto intervals = parse_integer_value(intervals_token);
m_value_context.take_last();
if (!intervals)
return nullptr;
// Perform extra validation
// https://drafts.csswg.org/css-easing/#step-easing-functions
// If the <step-position> is jump-none, the <integer> must be at least 2, or the function is invalid.
// Otherwise, the <integer> must be at least 1, or the function is invalid.
if (intervals->is_integer()) {
if (position == StepPosition::JumpNone) {
if (intervals->as_integer().integer() <= 1)
return nullptr;
} else if (intervals->as_integer().integer() <= 0) {
return nullptr;
}
}
m_value_context.append(position == StepPosition::JumpNone ? SpecialContext::StepsIntervalsJumpNone : SpecialContext::StepsIntervalsNormal);
double min_internals = position == StepPosition::JumpNone ? 2 : 1;
auto intervals = parse_integer_value(intervals_token, NumericRange { .min = min_internals, .max = AK::NumericLimits<i32>::max() });
m_value_context.take_last();
if (!intervals)
return nullptr;
transaction.commit();
return EasingStyleValue::create(EasingStyleValue::Steps { intervals.release_nonnull(), position });
@@ -3366,13 +3398,9 @@ RefPtr<BorderRadiusRectStyleValue const> Parser::parse_border_radius_rect_value(
continue;
}
auto maybe_dimension = parse_length_percentage_value(tokens);
auto maybe_dimension = parse_length_percentage_value(tokens, non_negative_range, non_negative_range);
if (!maybe_dimension)
return nullptr;
if (maybe_dimension->is_length() && maybe_dimension->as_length().length().raw_value() < 0)
return nullptr;
if (maybe_dimension->is_percentage() && maybe_dimension->as_percentage().percentage().value() < 0)
return nullptr;
if (reading_vertical) {
vertical_radii.append(maybe_dimension.release_nonnull());
} else {
@@ -3429,16 +3457,10 @@ RefPtr<RadialSizeStyleValue const> Parser::parse_radial_size(TokenStream<Compone
auto context_guard = push_temporary_value_parsing_context(SpecialContext::RadialSizeLengthPercentage);
auto length_percentage_value = parse_length_percentage_value(tokens);
auto length_percentage_value = parse_length_percentage_value(tokens, non_negative_range, non_negative_range);
if (!length_percentage_value)
return nullptr;
if (length_percentage_value->is_length() && length_percentage_value->as_length().length().raw_value() < 0)
return nullptr;
if (length_percentage_value->is_percentage() && length_percentage_value->as_percentage().percentage().value() < 0)
return nullptr;
length_percentage_transaction.commit();
return length_percentage_value;
};
@@ -3487,7 +3509,7 @@ RefPtr<FitContentStyleValue const> Parser::parse_fit_content_value(TokenStream<C
return nullptr;
TokenStream argument_tokens { function.value };
argument_tokens.discard_whitespace();
auto length_percentage_value = parse_length_percentage_value(argument_tokens);
auto length_percentage_value = parse_length_percentage_value(argument_tokens, infinite_range, infinite_range);
if (!length_percentage_value)
return nullptr;
argument_tokens.discard_whitespace();
@@ -3515,14 +3537,7 @@ RefPtr<StyleValue const> Parser::parse_font_style_value(TokenStream<ComponentVal
if (tokens.has_next_token() && keyword_value->to_keyword() == Keyword::Oblique) {
auto context_guard = push_temporary_value_parsing_context(SpecialContext::FontStyleAngle);
if (auto angle_value = parse_angle_value(tokens)) {
if (angle_value->is_angle()) {
auto angle = angle_value->as_angle().angle();
auto angle_degrees = angle.to_degrees();
if (angle_degrees < -90 || angle_degrees > 90)
return nullptr;
}
if (auto angle_value = parse_angle_value(tokens, { .min = -90, .max = 90 })) {
transaction.commit();
return FontStyleStyleValue::create(font_style.release_value(), angle_value);
}
@@ -3886,22 +3901,22 @@ RefPtr<StyleValue const> Parser::parse_basic_shape_value(TokenStream<ComponentVa
// The four <length-percentage>s define the position of the top, right, bottom, and left edges of a rectangle.
arguments_tokens.discard_whitespace();
auto top = parse_length_percentage_value(arguments_tokens);
auto top = parse_length_percentage_value(arguments_tokens, infinite_range, infinite_range);
if (!top)
return nullptr;
arguments_tokens.discard_whitespace();
auto right = parse_length_percentage_value(arguments_tokens);
auto right = parse_length_percentage_value(arguments_tokens, infinite_range, infinite_range);
if (!right)
right = top;
arguments_tokens.discard_whitespace();
auto bottom = parse_length_percentage_value(arguments_tokens);
auto bottom = parse_length_percentage_value(arguments_tokens, infinite_range, infinite_range);
if (!bottom)
bottom = top;
arguments_tokens.discard_whitespace();
auto left = parse_length_percentage_value(arguments_tokens);
auto left = parse_length_percentage_value(arguments_tokens, infinite_range, infinite_range);
if (!left)
left = right;
@@ -3932,22 +3947,22 @@ RefPtr<StyleValue const> Parser::parse_basic_shape_value(TokenStream<ComponentVa
auto arguments_tokens = TokenStream { component_value.function().value };
arguments_tokens.discard_whitespace();
auto x = parse_length_percentage_value(arguments_tokens);
auto x = parse_length_percentage_value(arguments_tokens, infinite_range, infinite_range);
if (!x)
return nullptr;
arguments_tokens.discard_whitespace();
auto y = parse_length_percentage_value(arguments_tokens);
auto y = parse_length_percentage_value(arguments_tokens, infinite_range, infinite_range);
if (!y)
return nullptr;
arguments_tokens.discard_whitespace();
auto width = parse_length_percentage_value(arguments_tokens);
auto width = parse_length_percentage_value(arguments_tokens, non_negative_range, non_negative_range);
if (!width)
return nullptr;
arguments_tokens.discard_whitespace();
auto height = parse_length_percentage_value(arguments_tokens);
auto height = parse_length_percentage_value(arguments_tokens, non_negative_range, non_negative_range);
if (!height)
return nullptr;
@@ -3969,13 +3984,6 @@ RefPtr<StyleValue const> Parser::parse_basic_shape_value(TokenStream<ComponentVa
if (arguments_tokens.has_next_token())
return nullptr;
// Negative width or height is invalid.
if ((width->is_length() && width->as_length().raw_value() < 0)
|| (width->is_percentage() && width->as_percentage().raw_value() < 0)
|| (height->is_length() && height->as_length().raw_value() < 0)
|| (height->is_percentage() && height->as_percentage().raw_value() < 0))
return nullptr;
transaction.commit();
return BasicShapeStyleValue::create(Xywh { x.release_nonnull(), y.release_nonnull(), width.release_nonnull(), height.release_nonnull(), border_radius });
}
@@ -3986,7 +3994,7 @@ RefPtr<StyleValue const> Parser::parse_basic_shape_value(TokenStream<ComponentVa
auto parse_length_percentage_or_auto = [this](TokenStream<ComponentValue>& tokens) -> RefPtr<StyleValue const> {
tokens.discard_whitespace();
if (auto value = parse_length_percentage_value(tokens); value)
if (auto value = parse_length_percentage_value(tokens, infinite_range, infinite_range); value)
return value;
if (tokens.consume_a_token().is_ident("auto"sv))
return KeywordStyleValue::create(Keyword::Auto);
@@ -4117,12 +4125,12 @@ RefPtr<StyleValue const> Parser::parse_basic_shape_value(TokenStream<ComponentVa
TokenStream argument_tokens { argument };
argument_tokens.discard_whitespace();
auto x_pos = parse_length_percentage_value(argument_tokens);
auto x_pos = parse_length_percentage_value(argument_tokens, infinite_range, infinite_range);
if (!x_pos)
return nullptr;
argument_tokens.discard_whitespace();
auto y_pos = parse_length_percentage_value(argument_tokens);
auto y_pos = parse_length_percentage_value(argument_tokens, infinite_range, infinite_range);
if (!y_pos)
return nullptr;
@@ -4246,15 +4254,13 @@ RefPtr<RandomValueSharingStyleValue const> Parser::parse_random_value_sharing(To
tokens.discard_whitespace();
auto context_guard = push_temporary_value_parsing_context(SpecialContext::RandomValueSharingFixedValue);
if (auto fixed_value = parse_number_value(tokens)) {
// NB: Fixed values have to be less than one and numbers serialize with six digits of precision
if (auto fixed_value = parse_number_value(tokens, { .min = 0, .max = 0.999999 })) {
tokens.discard_whitespace();
if (tokens.has_next_token())
return nullptr;
if (fixed_value->is_number() && (fixed_value->as_number().number() < 0 || fixed_value->as_number().number() >= 1))
return nullptr;
transaction.commit();
return RandomValueSharingStyleValue::create_fixed(fixed_value.release_nonnull());
}
@@ -4341,12 +4347,8 @@ Optional<GridSize> Parser::parse_grid_track_breadth(TokenStream<ComponentValue>&
if (auto inflexible_breadth = parse_grid_inflexible_breadth(tokens); inflexible_breadth.has_value())
return inflexible_breadth;
if (auto flex_value = parse_flex_value(tokens)) {
if (flex_value->is_flex() && flex_value->as_flex().raw_value() < 0)
return {};
if (auto flex_value = parse_flex_value(tokens, non_negative_range))
return GridSize(flex_value.release_nonnull());
}
return {};
}
@@ -4387,13 +4389,9 @@ RefPtr<StyleValue const> Parser::parse_grid_fixed_breadth(TokenStream<ComponentV
// <fixed-breadth> = <length-percentage [0,∞]>
auto transaction = tokens.begin_transaction();
auto length_percentage = parse_length_percentage_value(tokens);
auto length_percentage = parse_length_percentage_value(tokens, non_negative_range, non_negative_range);
if (!length_percentage)
return {};
if (length_percentage->is_length() && length_percentage->as_length().raw_value() < 0)
return {};
if (length_percentage->is_percentage() && length_percentage->as_percentage().raw_value() < 0)
return {};
transaction.commit();
return length_percentage;
}
@@ -4560,8 +4558,8 @@ Optional<GridRepeat> Parser::parse_grid_track_repeat(TokenStream<ComponentValue>
GridRepeatTypeParser parse_repeat_type = [this](TokenStream<ComponentValue>& tokens) -> Optional<GridRepeatParams> {
auto context_guard = push_temporary_value_parsing_context(SpecialContext::GridTrackRepeatCount);
auto maybe_integer = parse_integer_value(tokens);
if (!maybe_integer || (maybe_integer->is_integer() && maybe_integer->as_integer().integer() < 1))
auto maybe_integer = parse_integer_value(tokens, { .min = 1, .max = NumericLimits<i32>::max() });
if (!maybe_integer)
return {};
return GridRepeatParams { GridRepeatType::Fixed, maybe_integer };
@@ -4604,8 +4602,8 @@ Optional<GridRepeat> Parser::parse_grid_fixed_repeat(TokenStream<ComponentValue>
GridRepeatTypeParser parse_repeat_type = [this](TokenStream<ComponentValue>& tokens) -> Optional<GridRepeatParams> {
auto context_guard = push_temporary_value_parsing_context(SpecialContext::GridTrackRepeatCount);
auto maybe_integer = parse_integer_value(tokens);
if (!maybe_integer || (maybe_integer->is_integer() && maybe_integer->as_integer().integer() < 1))
auto maybe_integer = parse_integer_value(tokens, { .min = 1, .max = NumericLimits<i32>::max() });
if (!maybe_integer)
return {};
return GridRepeatParams { GridRepeatType::Fixed, maybe_integer };
@@ -4829,7 +4827,7 @@ RefPtr<GridTrackPlacementStyleValue const> Parser::parse_grid_track_placement(To
// FIXME: Use the correct value parsing context here to clamp calculated values (note the non-contiguous valid
// range for integers for non-span)
if (auto maybe_parsed_integer = parse_integer_value(tokens)) {
if (auto maybe_parsed_integer = parse_integer_value(tokens, infinite_integer_range)) {
if (parsed_integer)
return nullptr;
@@ -5385,7 +5383,7 @@ OwnPtr<BooleanExpression> Parser::parse_if_condition(TokenStream<ComponentValue>
RefPtr<StyleValue const> Parser::parse_opacity_value_value(TokenStream<ComponentValue>& tokens)
{
// <opacity-value> = <number> | <percentage>
if (auto value = parse_number_percentage_value(tokens))
if (auto value = parse_number_percentage_value(tokens, infinite_range, infinite_range))
return OpacityValueStyleValue::create(value.release_nonnull());
return nullptr;
@@ -5687,7 +5685,7 @@ RefPtr<StyleValue const> Parser::parse_transform_function_value(TokenStream<Comp
switch (function_metadata.parameters[argument_index].type) {
case TransformFunctionParameterType::Angle: {
// These are `<angle> | <zero>` in the spec, so we have to check for both kinds.
if (auto angle_value = parse_angle_value(argument_tokens)) {
if (auto angle_value = parse_angle_value(argument_tokens, infinite_range)) {
values.append(angle_value.release_nonnull());
break;
}
@@ -5700,7 +5698,7 @@ RefPtr<StyleValue const> Parser::parse_transform_function_value(TokenStream<Comp
}
case TransformFunctionParameterType::Length:
case TransformFunctionParameterType::LengthNone: {
if (auto length_value = parse_length_value(argument_tokens)) {
if (auto length_value = parse_length_value(argument_tokens, infinite_range)) {
values.append(length_value.release_nonnull());
break;
}
@@ -5714,21 +5712,21 @@ RefPtr<StyleValue const> Parser::parse_transform_function_value(TokenStream<Comp
return nullptr;
}
case TransformFunctionParameterType::LengthPercentage: {
if (auto length_percentage_value = parse_length_percentage_value(argument_tokens)) {
if (auto length_percentage_value = parse_length_percentage_value(argument_tokens, infinite_range, infinite_range)) {
values.append(length_percentage_value.release_nonnull());
break;
}
return nullptr;
}
case TransformFunctionParameterType::Number: {
if (auto number_value = parse_number_value(argument_tokens)) {
if (auto number_value = parse_number_value(argument_tokens, infinite_range)) {
values.append(number_value.release_nonnull());
break;
}
return nullptr;
}
case TransformFunctionParameterType::NumberPercentage: {
if (auto number_percentage_value = parse_number_percentage_value(argument_tokens)) {
if (auto number_percentage_value = parse_number_percentage_value(argument_tokens, infinite_range, infinite_range)) {
values.append(number_percentage_value.release_nonnull());
break;
}
@@ -5774,9 +5772,9 @@ RefPtr<StyleValue const> Parser::parse_value(ValueType value_type, TokenStream<C
case ValueType::AnchorSize:
return parse_anchor_size(tokens);
case ValueType::Angle:
return parse_angle_value(tokens);
return parse_angle_value(tokens, infinite_range);
case ValueType::AnglePercentage:
return parse_angle_percentage_value(tokens);
return parse_angle_percentage_value(tokens, infinite_range, infinite_range);
case ValueType::BackgroundPosition:
return parse_position_value(tokens, PositionParsingMode::BackgroundPosition);
case ValueType::BasicShape:
@@ -5801,7 +5799,7 @@ RefPtr<StyleValue const> Parser::parse_value(ValueType value_type, TokenStream<C
case ValueType::FitContent:
return parse_fit_content_value(tokens);
case ValueType::Flex:
return parse_flex_value(tokens);
return parse_flex_value(tokens, infinite_range);
case ValueType::FontStyle:
return parse_font_style_value(tokens);
case ValueType::FontVariantAlternates:
@@ -5813,19 +5811,19 @@ RefPtr<StyleValue const> Parser::parse_value(ValueType value_type, TokenStream<C
case ValueType::FontVariantNumeric:
return parse_font_variant_numeric_value(tokens);
case ValueType::Frequency:
return parse_frequency_value(tokens);
return parse_frequency_value(tokens, infinite_range);
case ValueType::FrequencyPercentage:
return parse_frequency_percentage_value(tokens);
return parse_frequency_percentage_value(tokens, infinite_range, infinite_range);
case ValueType::Image:
return parse_image_value(tokens);
case ValueType::Integer:
return parse_integer_value(tokens);
return parse_integer_value(tokens, infinite_integer_range);
case ValueType::Length:
return parse_length_value(tokens);
return parse_length_value(tokens, infinite_range);
case ValueType::LengthPercentage:
return parse_length_percentage_value(tokens);
return parse_length_percentage_value(tokens, infinite_range, infinite_range);
case ValueType::Number:
return parse_number_value(tokens);
return parse_number_value(tokens, infinite_range);
case ValueType::OpacityValue:
return parse_opacity_value_value(tokens);
case ValueType::OpentypeTag:
@@ -5833,7 +5831,7 @@ RefPtr<StyleValue const> Parser::parse_value(ValueType value_type, TokenStream<C
case ValueType::Paint:
return parse_paint_value(tokens);
case ValueType::Percentage:
return parse_percentage_value(tokens);
return parse_percentage_value(tokens, infinite_range);
case ValueType::Position:
return parse_position_value(tokens);
case ValueType::Ratio:
@@ -5841,15 +5839,15 @@ RefPtr<StyleValue const> Parser::parse_value(ValueType value_type, TokenStream<C
case ValueType::Rect:
return parse_rect_value(tokens);
case ValueType::Resolution:
return parse_resolution_value(tokens);
return parse_resolution_value(tokens, infinite_range);
case ValueType::ScrollFunction:
return parse_scroll_function_value(tokens);
case ValueType::String:
return parse_string_value(tokens);
case ValueType::Time:
return parse_time_value(tokens);
return parse_time_value(tokens, infinite_range);
case ValueType::TimePercentage:
return parse_time_percentage_value(tokens);
return parse_time_percentage_value(tokens, infinite_range, infinite_range);
case ValueType::TransformFunction:
return parse_transform_function_value(tokens);
case ValueType::TransformList:

View File

@@ -0,0 +1 @@
#foo { }

View File

@@ -0,0 +1,15 @@
<!doctype html>
<style>
#foo {
border-top-left-radius: -1px;
border-top-right-radius: 1px -1px;
border-bottom-right-radius: -1px 1px;
border-bottom-left-radius: -1px -1px;
}
</style>
<script src="../include.js"></script>
<script>
test(() => {
println(document.styleSheets[0].cssRules[0].cssText);
});
</script>

View File

@@ -0,0 +1,12 @@
<!doctype html>
<style>
#foo {
overflow-clip-margin: -1px;
}
</style>
<script src="../include.js"></script>
<script>
test(() => {
println(document.styleSheets[0].cssRules[0].cssText);
});
</script>