/* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Tobias Christiansen * Copyright (c) 2021-2022, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Web::CSS { enum class BackgroundSize { Contain, Cover, LengthPercentage, }; enum class ShadowPlacement { Outer, Inner, }; enum class FlexBasis { Content, LengthPercentage, Auto, }; // Note: The sides must be before the corners in this enum (as this order is used in parsing). enum class SideOrCorner { Top, Bottom, Left, Right, TopLeft, TopRight, BottomLeft, BottomRight }; struct GradientColorStop { Color color; Optional position; Optional second_position = {}; }; struct GradientColorHint { LengthPercentage value; }; struct ColorStopListElement { Optional transition_hint; GradientColorStop color_stop; }; struct EdgeRect { Length top_edge; Length right_edge; Length bottom_edge; Length left_edge; Gfx::FloatRect resolved(Layout::Node const&, Gfx::FloatRect) const; }; // FIXME: Find a better place for this helper. inline Gfx::Painter::ScalingMode to_gfx_scaling_mode(CSS::ImageRendering css_value) { switch (css_value) { case CSS::ImageRendering::Auto: case CSS::ImageRendering::HighQuality: case CSS::ImageRendering::Smooth: return Gfx::Painter::ScalingMode::BilinearBlend; case CSS::ImageRendering::CrispEdges: return Gfx::Painter::ScalingMode::NearestNeighbor; case CSS::ImageRendering::Pixelated: return Gfx::Painter::ScalingMode::SmoothPixels; } VERIFY_NOT_REACHED(); } class StyleValue : public RefCounted { public: virtual ~StyleValue() = default; enum class Type { Angle, Background, BackgroundRepeat, BackgroundSize, Border, BorderRadius, BorderRadiusShorthand, Calculated, Color, Content, Flex, FlexFlow, Font, Frequency, Identifier, Image, Inherit, Initial, Invalid, Length, LinearGradient, ListStyle, Numeric, Overflow, Percentage, Position, Rect, Resolution, Shadow, String, TextDecoration, Time, Transformation, Unresolved, Unset, ValueList }; Type type() const { return m_type; } bool is_abstract_image() const { return AK::first_is_one_of(type(), Type::Image, Type::LinearGradient); } bool is_angle() const { return type() == Type::Angle; } bool is_background() const { return type() == Type::Background; } bool is_background_repeat() const { return type() == Type::BackgroundRepeat; } bool is_background_size() const { return type() == Type::BackgroundSize; } bool is_border() const { return type() == Type::Border; } bool is_border_radius() const { return type() == Type::BorderRadius; } bool is_border_radius_shorthand() const { return type() == Type::BorderRadiusShorthand; } bool is_calculated() const { return type() == Type::Calculated; } bool is_color() const { return type() == Type::Color; } bool is_content() const { return type() == Type::Content; } bool is_flex() const { return type() == Type::Flex; } bool is_flex_flow() const { return type() == Type::FlexFlow; } bool is_font() const { return type() == Type::Font; } bool is_frequency() const { return type() == Type::Frequency; } bool is_identifier() const { return type() == Type::Identifier; } bool is_image() const { return type() == Type::Image; } bool is_inherit() const { return type() == Type::Inherit; } bool is_initial() const { return type() == Type::Initial; } bool is_length() const { return type() == Type::Length; } bool is_linear_gradient() const { return type() == Type::LinearGradient; } bool is_list_style() const { return type() == Type::ListStyle; } bool is_numeric() const { return type() == Type::Numeric; } bool is_overflow() const { return type() == Type::Overflow; } bool is_percentage() const { return type() == Type::Percentage; } bool is_position() const { return type() == Type::Position; } bool is_rect() const { return type() == Type::Rect; } bool is_resolution() const { return type() == Type::Resolution; } bool is_shadow() const { return type() == Type::Shadow; } bool is_string() const { return type() == Type::String; } bool is_text_decoration() const { return type() == Type::TextDecoration; } bool is_time() const { return type() == Type::Time; } bool is_transformation() const { return type() == Type::Transformation; } bool is_unresolved() const { return type() == Type::Unresolved; } bool is_unset() const { return type() == Type::Unset; } bool is_value_list() const { return type() == Type::ValueList; } bool is_builtin() const { return is_inherit() || is_initial() || is_unset(); } AbstractImageStyleValue const& as_abstract_image() const; AngleStyleValue const& as_angle() const; BackgroundStyleValue const& as_background() const; BackgroundRepeatStyleValue const& as_background_repeat() const; BackgroundSizeStyleValue const& as_background_size() const; BorderRadiusStyleValue const& as_border_radius() const; BorderRadiusShorthandStyleValue const& as_border_radius_shorthand() const; BorderStyleValue const& as_border() const; CalculatedStyleValue const& as_calculated() const; ColorStyleValue const& as_color() const; ContentStyleValue const& as_content() const; FlexFlowStyleValue const& as_flex_flow() const; FlexStyleValue const& as_flex() const; FontStyleValue const& as_font() const; FrequencyStyleValue const& as_frequency() const; IdentifierStyleValue const& as_identifier() const; ImageStyleValue const& as_image() const; InheritStyleValue const& as_inherit() const; InitialStyleValue const& as_initial() const; LengthStyleValue const& as_length() const; LinearGradientStyleValue const& as_linear_gradient() const; ListStyleStyleValue const& as_list_style() const; NumericStyleValue const& as_numeric() const; OverflowStyleValue const& as_overflow() const; PercentageStyleValue const& as_percentage() const; PositionStyleValue const& as_position() const; RectStyleValue const& as_rect() const; ResolutionStyleValue const& as_resolution() const; ShadowStyleValue const& as_shadow() const; StringStyleValue const& as_string() const; TextDecorationStyleValue const& as_text_decoration() const; TimeStyleValue const& as_time() const; TransformationStyleValue const& as_transformation() const; UnresolvedStyleValue const& as_unresolved() const; UnsetStyleValue const& as_unset() const; StyleValueList const& as_value_list() const; AbstractImageStyleValue& as_abstract_image() { return const_cast(const_cast(*this).as_abstract_image()); } AngleStyleValue& as_angle() { return const_cast(const_cast(*this).as_angle()); } BackgroundStyleValue& as_background() { return const_cast(const_cast(*this).as_background()); } BackgroundRepeatStyleValue& as_background_repeat() { return const_cast(const_cast(*this).as_background_repeat()); } BackgroundSizeStyleValue& as_background_size() { return const_cast(const_cast(*this).as_background_size()); } BorderRadiusStyleValue& as_border_radius() { return const_cast(const_cast(*this).as_border_radius()); } BorderRadiusShorthandStyleValue& as_border_radius_shorthand() { return const_cast(const_cast(*this).as_border_radius_shorthand()); } BorderStyleValue& as_border() { return const_cast(const_cast(*this).as_border()); } CalculatedStyleValue& as_calculated() { return const_cast(const_cast(*this).as_calculated()); } ColorStyleValue& as_color() { return const_cast(const_cast(*this).as_color()); } ContentStyleValue& as_content() { return const_cast(const_cast(*this).as_content()); } FlexFlowStyleValue& as_flex_flow() { return const_cast(const_cast(*this).as_flex_flow()); } FlexStyleValue& as_flex() { return const_cast(const_cast(*this).as_flex()); } FontStyleValue& as_font() { return const_cast(const_cast(*this).as_font()); } FrequencyStyleValue& as_frequency() { return const_cast(const_cast(*this).as_frequency()); } IdentifierStyleValue& as_identifier() { return const_cast(const_cast(*this).as_identifier()); } ImageStyleValue& as_image() { return const_cast(const_cast(*this).as_image()); } InheritStyleValue& as_inherit() { return const_cast(const_cast(*this).as_inherit()); } InitialStyleValue& as_initial() { return const_cast(const_cast(*this).as_initial()); } LengthStyleValue& as_length() { return const_cast(const_cast(*this).as_length()); } LinearGradientStyleValue& as_linear_gradient() { return const_cast(const_cast(*this).as_linear_gradient()); } ListStyleStyleValue& as_list_style() { return const_cast(const_cast(*this).as_list_style()); } NumericStyleValue& as_numeric() { return const_cast(const_cast(*this).as_numeric()); } OverflowStyleValue& as_overflow() { return const_cast(const_cast(*this).as_overflow()); } PercentageStyleValue& as_percentage() { return const_cast(const_cast(*this).as_percentage()); } PositionStyleValue& as_position() { return const_cast(const_cast(*this).as_position()); } RectStyleValue& as_rect() { return const_cast(const_cast(*this).as_rect()); } ResolutionStyleValue& as_resolution() { return const_cast(const_cast(*this).as_resolution()); } ShadowStyleValue& as_shadow() { return const_cast(const_cast(*this).as_shadow()); } StringStyleValue& as_string() { return const_cast(const_cast(*this).as_string()); } TextDecorationStyleValue& as_text_decoration() { return const_cast(const_cast(*this).as_text_decoration()); } TimeStyleValue& as_time() { return const_cast(const_cast(*this).as_time()); } TransformationStyleValue& as_transformation() { return const_cast(const_cast(*this).as_transformation()); } UnresolvedStyleValue& as_unresolved() { return const_cast(const_cast(*this).as_unresolved()); } UnsetStyleValue& as_unset() { return const_cast(const_cast(*this).as_unset()); } StyleValueList& as_value_list() { return const_cast(const_cast(*this).as_value_list()); } virtual bool has_auto() const { return false; } virtual bool has_color() const { return false; } virtual bool has_identifier() const { return false; } virtual bool has_length() const { return false; } virtual bool has_rect() const { return false; } virtual bool has_number() const { return false; } virtual bool has_integer() const { return false; } virtual NonnullRefPtr absolutized(Gfx::IntRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, float font_size, float root_font_size) const; virtual Color to_color(Layout::NodeWithStyle const&) const { return {}; } virtual EdgeRect to_rect() const { VERIFY_NOT_REACHED(); } virtual CSS::ValueID to_identifier() const { return ValueID::Invalid; } virtual Length to_length() const { VERIFY_NOT_REACHED(); } virtual float to_number() const { return 0; } virtual float to_integer() const { return 0; } virtual String to_string() const = 0; bool operator==(StyleValue const& other) const { return equals(other); } bool operator!=(StyleValue const& other) const { return !(*this == other); } virtual bool equals(StyleValue const& other) const = 0; protected: explicit StyleValue(Type); private: Type m_type { Type::Invalid }; }; class AngleStyleValue : public StyleValue { public: static NonnullRefPtr create(Angle angle) { return adopt_ref(*new AngleStyleValue(move(angle))); } virtual ~AngleStyleValue() override { } Angle const& angle() const { return m_angle; } virtual String to_string() const override { return m_angle.to_string(); } virtual bool equals(StyleValue const& other) const override { if (type() != other.type()) return false; return m_angle == static_cast(other).m_angle; } private: explicit AngleStyleValue(Angle angle) : StyleValue(Type::Angle) , m_angle(move(angle)) { } Angle m_angle; }; class BackgroundStyleValue final : public StyleValue { public: static NonnullRefPtr create( NonnullRefPtr color, NonnullRefPtr image, NonnullRefPtr position, NonnullRefPtr size, NonnullRefPtr repeat, NonnullRefPtr attachment, NonnullRefPtr origin, NonnullRefPtr clip) { return adopt_ref(*new BackgroundStyleValue(color, image, position, size, repeat, attachment, origin, clip)); } virtual ~BackgroundStyleValue() override = default; size_t layer_count() const { return m_layer_count; } NonnullRefPtr attachment() const { return m_attachment; } NonnullRefPtr clip() const { return m_clip; } NonnullRefPtr color() const { return m_color; } NonnullRefPtr image() const { return m_image; } NonnullRefPtr origin() const { return m_origin; } NonnullRefPtr position() const { return m_position; } NonnullRefPtr repeat() const { return m_repeat; } NonnullRefPtr size() const { return m_size; } virtual String to_string() const override; virtual bool equals(StyleValue const& other) const override; private: BackgroundStyleValue( NonnullRefPtr color, NonnullRefPtr image, NonnullRefPtr position, NonnullRefPtr size, NonnullRefPtr repeat, NonnullRefPtr attachment, NonnullRefPtr origin, NonnullRefPtr clip); NonnullRefPtr m_color; NonnullRefPtr m_image; NonnullRefPtr m_position; NonnullRefPtr m_size; NonnullRefPtr m_repeat; NonnullRefPtr m_attachment; NonnullRefPtr m_origin; NonnullRefPtr m_clip; size_t m_layer_count; }; class BackgroundRepeatStyleValue final : public StyleValue { public: static NonnullRefPtr create(Repeat repeat_x, Repeat repeat_y) { return adopt_ref(*new BackgroundRepeatStyleValue(repeat_x, repeat_y)); } virtual ~BackgroundRepeatStyleValue() override = default; Repeat repeat_x() const { return m_repeat_x; } Repeat repeat_y() const { return m_repeat_y; } virtual String to_string() const override; virtual bool equals(StyleValue const& other) const override; private: BackgroundRepeatStyleValue(Repeat repeat_x, Repeat repeat_y) : StyleValue(Type::BackgroundRepeat) , m_repeat_x(repeat_x) , m_repeat_y(repeat_y) { } Repeat m_repeat_x; Repeat m_repeat_y; }; // NOTE: This is not used for identifier sizes, like `cover` and `contain`. class BackgroundSizeStyleValue final : public StyleValue { public: static NonnullRefPtr create(LengthPercentage size_x, LengthPercentage size_y) { return adopt_ref(*new BackgroundSizeStyleValue(size_x, size_y)); } virtual ~BackgroundSizeStyleValue() override = default; LengthPercentage size_x() const { return m_size_x; } LengthPercentage size_y() const { return m_size_y; } virtual String to_string() const override; virtual bool equals(StyleValue const& other) const override; private: BackgroundSizeStyleValue(LengthPercentage size_x, LengthPercentage size_y) : StyleValue(Type::BackgroundSize) , m_size_x(size_x) , m_size_y(size_y) { } LengthPercentage m_size_x; LengthPercentage m_size_y; }; class BorderStyleValue final : public StyleValue { public: static NonnullRefPtr create( NonnullRefPtr border_width, NonnullRefPtr border_style, NonnullRefPtr border_color) { return adopt_ref(*new BorderStyleValue(border_width, border_style, border_color)); } virtual ~BorderStyleValue() override = default; NonnullRefPtr border_width() const { return m_border_width; } NonnullRefPtr border_style() const { return m_border_style; } NonnullRefPtr border_color() const { return m_border_color; } virtual String to_string() const override; virtual bool equals(StyleValue const& other) const override; private: BorderStyleValue( NonnullRefPtr border_width, NonnullRefPtr border_style, NonnullRefPtr border_color) : StyleValue(Type::Border) , m_border_width(border_width) , m_border_style(border_style) , m_border_color(border_color) { } NonnullRefPtr m_border_width; NonnullRefPtr m_border_style; NonnullRefPtr m_border_color; }; class BorderRadiusStyleValue final : public StyleValue { public: static NonnullRefPtr create(LengthPercentage const& horizontal_radius, LengthPercentage const& vertical_radius) { return adopt_ref(*new BorderRadiusStyleValue(horizontal_radius, vertical_radius)); } virtual ~BorderRadiusStyleValue() override = default; LengthPercentage const& horizontal_radius() const { return m_horizontal_radius; } LengthPercentage const& vertical_radius() const { return m_vertical_radius; } bool is_elliptical() const { return m_is_elliptical; } virtual String to_string() const override; virtual bool equals(StyleValue const& other) const override; private: BorderRadiusStyleValue(LengthPercentage const& horizontal_radius, LengthPercentage const& vertical_radius) : StyleValue(Type::BorderRadius) , m_horizontal_radius(horizontal_radius) , m_vertical_radius(vertical_radius) { m_is_elliptical = (m_horizontal_radius != m_vertical_radius); } virtual NonnullRefPtr absolutized(Gfx::IntRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, float font_size, float root_font_size) const override; bool m_is_elliptical; LengthPercentage m_horizontal_radius; LengthPercentage m_vertical_radius; }; class BorderRadiusShorthandStyleValue final : public StyleValue { public: static NonnullRefPtr create(NonnullRefPtr top_left, NonnullRefPtr top_right, NonnullRefPtr bottom_right, NonnullRefPtr bottom_left) { return adopt_ref(*new BorderRadiusShorthandStyleValue(top_left, top_right, bottom_right, bottom_left)); } virtual ~BorderRadiusShorthandStyleValue() override = default; NonnullRefPtr top_left() const { return m_top_left; } NonnullRefPtr top_right() const { return m_top_right; } NonnullRefPtr bottom_right() const { return m_bottom_right; } NonnullRefPtr bottom_left() const { return m_bottom_left; } virtual String to_string() const override; virtual bool equals(StyleValue const& other) const override; private: BorderRadiusShorthandStyleValue(NonnullRefPtr top_left, NonnullRefPtr top_right, NonnullRefPtr bottom_right, NonnullRefPtr bottom_left) : StyleValue(Type::BorderRadiusShorthand) , m_top_left(top_left) , m_top_right(top_right) , m_bottom_right(bottom_right) , m_bottom_left(bottom_left) { } NonnullRefPtr m_top_left; NonnullRefPtr m_top_right; NonnullRefPtr m_bottom_right; NonnullRefPtr m_bottom_left; }; class CalculatedStyleValue : public StyleValue { public: enum class ResolvedType { Angle, Frequency, Integer, Length, Number, Percentage, Time, }; enum class SumOperation { Add, Subtract, }; enum class ProductOperation { Multiply, Divide, }; using PercentageBasis = Variant; class CalculationResult { public: using Value = Variant; CalculationResult(Value value) : m_value(move(value)) { } void add(CalculationResult const& other, Layout::Node const*, PercentageBasis const& percentage_basis); void subtract(CalculationResult const& other, Layout::Node const*, PercentageBasis const& percentage_basis); void multiply_by(CalculationResult const& other, Layout::Node const*); void divide_by(CalculationResult const& other, Layout::Node const*); Value const& value() const { return m_value; } private: void add_or_subtract_internal(SumOperation op, CalculationResult const& other, Layout::Node const*, PercentageBasis const& percentage_basis); Value m_value; }; struct CalcSum; struct CalcSumPartWithOperator; struct CalcProduct; struct CalcProductPartWithOperator; struct CalcNumberSum; struct CalcNumberSumPartWithOperator; struct CalcNumberProduct; struct CalcNumberProductPartWithOperator; struct CalcNumberValue { Variant> value; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; struct CalcValue { Variant> value; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; // This represents that: https://www.w3.org/TR/css-values-3/#calc-syntax struct CalcSum { CalcSum(NonnullOwnPtr first_calc_product, NonnullOwnPtrVector additional) : first_calc_product(move(first_calc_product)) , zero_or_more_additional_calc_products(move(additional)) {}; NonnullOwnPtr first_calc_product; NonnullOwnPtrVector zero_or_more_additional_calc_products; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; struct CalcNumberSum { CalcNumberSum(NonnullOwnPtr first_calc_number_product, NonnullOwnPtrVector additional) : first_calc_number_product(move(first_calc_number_product)) , zero_or_more_additional_calc_number_products(move(additional)) {}; NonnullOwnPtr first_calc_number_product; NonnullOwnPtrVector zero_or_more_additional_calc_number_products; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; struct CalcProduct { CalcValue first_calc_value; NonnullOwnPtrVector zero_or_more_additional_calc_values; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; struct CalcSumPartWithOperator { CalcSumPartWithOperator(SumOperation op, NonnullOwnPtr calc_product) : op(op) , value(move(calc_product)) {}; SumOperation op; NonnullOwnPtr value; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; struct CalcProductPartWithOperator { ProductOperation op; Variant value; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; struct CalcNumberProduct { CalcNumberValue first_calc_number_value; NonnullOwnPtrVector zero_or_more_additional_calc_number_values; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; struct CalcNumberProductPartWithOperator { ProductOperation op; CalcNumberValue value; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; struct CalcNumberSumPartWithOperator { CalcNumberSumPartWithOperator(SumOperation op, NonnullOwnPtr calc_number_product) : op(op) , value(move(calc_number_product)) {}; SumOperation op; NonnullOwnPtr value; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; }; static NonnullRefPtr create(NonnullOwnPtr calc_sum, ResolvedType resolved_type) { return adopt_ref(*new CalculatedStyleValue(move(calc_sum), resolved_type)); } String to_string() const override; virtual bool equals(StyleValue const& other) const override; ResolvedType resolved_type() const { return m_resolved_type; } NonnullOwnPtr const& expression() const { return m_expression; } bool resolves_to_angle() const { return m_resolved_type == ResolvedType::Angle; } Optional resolve_angle() const; Optional resolve_angle_percentage(Angle const& percentage_basis) const; bool resolves_to_frequency() const { return m_resolved_type == ResolvedType::Frequency; } Optional resolve_frequency() const; Optional resolve_frequency_percentage(Frequency const& percentage_basis) const; bool resolves_to_length() const { return m_resolved_type == ResolvedType::Length; } Optional resolve_length(Layout::Node const& layout_node) const; Optional resolve_length_percentage(Layout::Node const&, Length const& percentage_basis) const; bool resolves_to_percentage() const { return m_resolved_type == ResolvedType::Percentage; } Optional resolve_percentage() const; bool resolves_to_time() const { return m_resolved_type == ResolvedType::Time; } Optional