diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h index b750570fa3..6aca2839cd 100644 --- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,8 +23,14 @@ namespace Web::CSS { +struct AspectRatio { + bool use_natural_aspect_ratio_if_available; + Optional preferred_ratio; +}; + class InitialValues { public: + static AspectRatio aspect_ratio() { return AspectRatio { true, {} }; } static float font_size() { return 16; } static int font_weight() { return 400; } static CSS::FontVariant font_variant() { return CSS::FontVariant::Normal; } @@ -211,6 +218,7 @@ inline Gfx::Painter::ScalingMode to_gfx_scaling_mode(CSS::ImageRendering css_val class ComputedValues { public: + AspectRatio aspect_ratio() const { return m_noninherited.aspect_ratio; } CSS::Float float_() const { return m_noninherited.float_; } CSS::Clear clear() const { return m_noninherited.clear; } CSS::Clip clip() const { return m_noninherited.clip; } @@ -344,6 +352,7 @@ protected: } m_inherited; struct { + AspectRatio aspect_ratio { InitialValues::aspect_ratio() }; CSS::Float float_ { InitialValues::float_() }; CSS::Clear clear { InitialValues::clear() }; CSS::Clip clip { InitialValues::clip() }; @@ -418,6 +427,7 @@ class ImmutableComputedValues final : public ComputedValues { class MutableComputedValues final : public ComputedValues { public: + void set_aspect_ratio(AspectRatio aspect_ratio) { m_noninherited.aspect_ratio = aspect_ratio; } void set_font_size(float font_size) { m_inherited.font_size = font_size; } void set_font_weight(int font_weight) { m_inherited.font_weight = font_weight; } void set_font_variant(CSS::FontVariant font_variant) { m_inherited.font_variant = font_variant; } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index d8b4c2c821..e169f13396 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -4457,6 +4457,51 @@ static void remove_property(Vector& properties, PropertyID property_ properties.remove_first_matching([&](auto it) { return it == property_to_remove; }); } +// https://www.w3.org/TR/css-sizing-4/#aspect-ratio +ErrorOr> Parser::parse_aspect_ratio_value(Vector const& component_values) +{ + // `auto || ` + RefPtr auto_value; + RefPtr ratio_value; + + auto tokens = TokenStream { component_values }; + while (tokens.has_next_token()) { + auto maybe_value = TRY(parse_css_value_for_property(PropertyID::AspectRatio, tokens)); + if (!maybe_value) + return nullptr; + + if (maybe_value->is_ratio()) { + if (ratio_value) + return nullptr; + ratio_value = maybe_value.release_nonnull(); + continue; + } + + if (maybe_value->is_identifier() && maybe_value->as_identifier().id() == ValueID::Auto) { + if (auto_value) + return nullptr; + auto_value = maybe_value.release_nonnull(); + continue; + } + + return nullptr; + } + + if (auto_value && ratio_value) { + return TRY(StyleValueList::create( + StyleValueVector { auto_value.release_nonnull(), ratio_value.release_nonnull() }, + StyleValueList::Separator::Space)); + } + + if (ratio_value) + return ratio_value.release_nonnull(); + + if (auto_value) + return auto_value.release_nonnull(); + + return nullptr; +} + ErrorOr> Parser::parse_background_value(Vector const& component_values) { StyleValueVector background_images; @@ -7307,6 +7352,10 @@ Parser::ParseErrorOr> Parser::parse_css_value(Property // Special-case property handling switch (property_id) { + case PropertyID::AspectRatio: + if (auto parsed_value = FIXME_TRY(parse_aspect_ratio_value(component_values))) + return parsed_value.release_nonnull(); + return ParseError::SyntaxError; case PropertyID::BackdropFilter: if (auto parsed_value = FIXME_TRY(parse_filter_value_list_value(component_values))) return parsed_value.release_nonnull(); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index d099ba4493..c696de0be1 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -307,6 +307,7 @@ private: ErrorOr> parse_simple_comma_separated_value_list(PropertyID, Vector const&); ErrorOr> parse_filter_value_list_value(Vector const&); + ErrorOr> parse_aspect_ratio_value(Vector const&); ErrorOr> parse_background_value(Vector const&); ErrorOr> parse_single_background_position_value(TokenStream&); ErrorOr> parse_single_background_position_x_or_y_value(TokenStream&, PropertyID); diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json index b481abb9f8..1326993dc3 100644 --- a/Userland/Libraries/LibWeb/CSS/Properties.json +++ b/Userland/Libraries/LibWeb/CSS/Properties.json @@ -138,6 +138,17 @@ "appearance" ] }, + "aspect-ratio": { + "affects-layout": true, + "inherited": false, + "initial": "auto", + "valid-types": [ + "ratio" + ], + "valid-identifiers":[ + "auto" + ] + }, "backdrop-filter": { "affects-layout": false, "affects-stacking-context": true, diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp index ab666f0612..12ad2f26bb 100644 --- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp +++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -263,6 +264,19 @@ ErrorOr> ResolvedCSSStyleDeclaration::style_value_for_p return TRY(IdentifierStyleValue::create(to_value_id(layout_node.computed_values().align_self()))); case PropertyID::Appearance: return TRY(IdentifierStyleValue::create(to_value_id(layout_node.computed_values().appearance()))); + case PropertyID::AspectRatio: { + auto aspect_ratio = layout_node.computed_values().aspect_ratio(); + if (aspect_ratio.use_natural_aspect_ratio_if_available && aspect_ratio.preferred_ratio.has_value()) { + return TRY(StyleValueList::create( + StyleValueVector { + TRY(IdentifierStyleValue::create(ValueID::Auto)), + TRY(RatioStyleValue::create(aspect_ratio.preferred_ratio.value())) }, + StyleValueList::Separator::Space)); + } + if (aspect_ratio.preferred_ratio.has_value()) + return TRY(RatioStyleValue::create(aspect_ratio.preferred_ratio.value())); + return TRY(IdentifierStyleValue::create(ValueID::Auto)); + } case PropertyID::Background: { auto maybe_background_color = property(PropertyID::BackgroundColor); auto maybe_background_image = property(PropertyID::BackgroundImage); diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index ccc74bb40a..20d1955cbd 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2023, Andreas Kling + * Copyright (c) 2021-2023, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,9 +11,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -707,6 +710,20 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style) if (auto border_collapse = computed_style.border_collapse(); border_collapse.has_value()) computed_values.set_border_collapse(border_collapse.value()); + + auto aspect_ratio = computed_style.property(CSS::PropertyID::AspectRatio); + if (aspect_ratio->is_value_list()) { + auto& values_list = aspect_ratio->as_value_list().values(); + if (values_list.size() == 2 + && values_list[0]->is_identifier() && values_list[0]->as_identifier().id() == CSS::ValueID::Auto + && values_list[1]->is_ratio()) { + computed_values.set_aspect_ratio({ true, values_list[1]->as_ratio().ratio() }); + } + } else if (aspect_ratio->is_identifier() && aspect_ratio->as_identifier().id() == CSS::ValueID::Auto) { + computed_values.set_aspect_ratio({ true, {} }); + } else if (aspect_ratio->is_ratio()) { + computed_values.set_aspect_ratio({ false, aspect_ratio->as_ratio().ratio() }); + } } bool Node::is_root_element() const