/* * Copyright (c) 2018-2023, Andreas Kling * Copyright (c) 2021, Tobias Christiansen * Copyright (c) 2021-2024, Sam Atkins * Copyright (c) 2022-2023, MacDue * * 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 namespace Web::CSS { template struct ValueComparingNonnullRefPtr : public NonnullRefPtr { using NonnullRefPtr::NonnullRefPtr; ValueComparingNonnullRefPtr(NonnullRefPtr const& other) : NonnullRefPtr(other) { } ValueComparingNonnullRefPtr(NonnullRefPtr&& other) : NonnullRefPtr(move(other)) { } bool operator==(ValueComparingNonnullRefPtr const& other) const { return this->ptr() == other.ptr() || this->ptr()->equals(*other); } private: using NonnullRefPtr::operator==; }; template struct ValueComparingRefPtr : public RefPtr { using RefPtr::RefPtr; ValueComparingRefPtr(RefPtr const& other) : RefPtr(other) { } ValueComparingRefPtr(RefPtr&& other) : RefPtr(move(other)) { } template bool operator==(ValueComparingNonnullRefPtr const& other) const { return this->ptr() == other.ptr() || (this->ptr() && this->ptr()->equals(*other)); } bool operator==(ValueComparingRefPtr const& other) const { return this->ptr() == other.ptr() || (this->ptr() && other.ptr() && this->ptr()->equals(*other)); } private: using RefPtr::operator==; }; using StyleValueVector = Vector>; // https://drafts.css-houdini.org/css-typed-om-1/#cssstylevalue class CSSStyleValue : public RefCounted { public: virtual ~CSSStyleValue() = default; enum class Type { Angle, BackgroundRepeat, BackgroundSize, BasicShape, BorderRadius, Calculated, Color, ColorScheme, ConicGradient, Content, Counter, CounterDefinitions, CustomIdent, Display, Easing, Edge, FilterValueList, Flex, FontVariant, Frequency, GridAutoFlow, GridTemplateArea, GridTrackPlacement, GridTrackSizeList, Image, Integer, Keyword, Length, LinearGradient, MathDepth, Number, OpenTypeTagged, Percentage, Position, RadialGradient, Ratio, Rect, Resolution, Rotation, ScrollbarGutter, Shadow, Shorthand, String, Time, Transformation, Transition, Unresolved, URL, ValueList, }; Type type() const { return m_type; } bool is_abstract_image() const { return AK::first_is_one_of(type(), Type::Image, Type::LinearGradient, Type::ConicGradient, Type::RadialGradient); } AbstractImageStyleValue const& as_abstract_image() const; AbstractImageStyleValue& as_abstract_image() { return const_cast(const_cast(*this).as_abstract_image()); } bool is_angle() const { return type() == Type::Angle; } AngleStyleValue const& as_angle() const; AngleStyleValue& as_angle() { return const_cast(const_cast(*this).as_angle()); } bool is_background_repeat() const { return type() == Type::BackgroundRepeat; } BackgroundRepeatStyleValue const& as_background_repeat() const; BackgroundRepeatStyleValue& as_background_repeat() { return const_cast(const_cast(*this).as_background_repeat()); } bool is_background_size() const { return type() == Type::BackgroundSize; } BackgroundSizeStyleValue const& as_background_size() const; BackgroundSizeStyleValue& as_background_size() { return const_cast(const_cast(*this).as_background_size()); } bool is_basic_shape() const { return type() == Type::BasicShape; } BasicShapeStyleValue const& as_basic_shape() const; BasicShapeStyleValue& as_basic_shape() { return const_cast(const_cast(*this).as_basic_shape()); } bool is_border_radius() const { return type() == Type::BorderRadius; } BorderRadiusStyleValue const& as_border_radius() const; BorderRadiusStyleValue& as_border_radius() { return const_cast(const_cast(*this).as_border_radius()); } bool is_calculated() const { return type() == Type::Calculated; } CalculatedStyleValue const& as_calculated() const; CalculatedStyleValue& as_calculated() { return const_cast(const_cast(*this).as_calculated()); } bool is_color() const { return type() == Type::Color; } CSSColorValue const& as_color() const; CSSColorValue& as_color() { return const_cast(const_cast(*this).as_color()); } bool is_color_scheme() const { return type() == Type::ColorScheme; } ColorSchemeStyleValue const& as_color_scheme() const; ColorSchemeStyleValue& as_color_scheme() { return const_cast(const_cast(*this).as_color_scheme()); } bool is_conic_gradient() const { return type() == Type::ConicGradient; } ConicGradientStyleValue const& as_conic_gradient() const; ConicGradientStyleValue& as_conic_gradient() { return const_cast(const_cast(*this).as_conic_gradient()); } bool is_content() const { return type() == Type::Content; } ContentStyleValue const& as_content() const; ContentStyleValue& as_content() { return const_cast(const_cast(*this).as_content()); } bool is_counter() const { return type() == Type::Counter; } CounterStyleValue const& as_counter() const; CounterStyleValue& as_counter() { return const_cast(const_cast(*this).as_counter()); } bool is_counter_definitions() const { return type() == Type::CounterDefinitions; } CounterDefinitionsStyleValue const& as_counter_definitions() const; CounterDefinitionsStyleValue& as_counter_definitions() { return const_cast(const_cast(*this).as_counter_definitions()); } bool is_custom_ident() const { return type() == Type::CustomIdent; } CustomIdentStyleValue const& as_custom_ident() const; CustomIdentStyleValue& as_custom_ident() { return const_cast(const_cast(*this).as_custom_ident()); } bool is_display() const { return type() == Type::Display; } DisplayStyleValue const& as_display() const; DisplayStyleValue& as_display() { return const_cast(const_cast(*this).as_display()); } bool is_easing() const { return type() == Type::Easing; } EasingStyleValue const& as_easing() const; EasingStyleValue& as_easing() { return const_cast(const_cast(*this).as_easing()); } bool is_edge() const { return type() == Type::Edge; } EdgeStyleValue const& as_edge() const; EdgeStyleValue& as_edge() { return const_cast(const_cast(*this).as_edge()); } bool is_filter_value_list() const { return type() == Type::FilterValueList; } FilterValueListStyleValue const& as_filter_value_list() const; FilterValueListStyleValue& as_filter_value_list() { return const_cast(const_cast(*this).as_filter_value_list()); } bool is_flex() const { return type() == Type::Flex; } FlexStyleValue const& as_flex() const; FlexStyleValue& as_flex() { return const_cast(const_cast(*this).as_flex()); } bool is_frequency() const { return type() == Type::Frequency; } FrequencyStyleValue const& as_frequency() const; FrequencyStyleValue& as_frequency() { return const_cast(const_cast(*this).as_frequency()); } bool is_grid_auto_flow() const { return type() == Type::GridAutoFlow; } GridAutoFlowStyleValue const& as_grid_auto_flow() const; GridAutoFlowStyleValue& as_grid_auto_flow() { return const_cast(const_cast(*this).as_grid_auto_flow()); } bool is_grid_template_area() const { return type() == Type::GridTemplateArea; } GridTemplateAreaStyleValue const& as_grid_template_area() const; GridTemplateAreaStyleValue& as_grid_template_area() { return const_cast(const_cast(*this).as_grid_template_area()); } bool is_grid_track_placement() const { return type() == Type::GridTrackPlacement; } GridTrackPlacementStyleValue const& as_grid_track_placement() const; GridTrackPlacementStyleValue& as_grid_track_placement() { return const_cast(const_cast(*this).as_grid_track_placement()); } bool is_grid_track_size_list() const { return type() == Type::GridTrackSizeList; } GridTrackSizeListStyleValue const& as_grid_track_size_list() const; GridTrackSizeListStyleValue& as_grid_track_size_list() { return const_cast(const_cast(*this).as_grid_track_size_list()); } bool is_image() const { return type() == Type::Image; } ImageStyleValue const& as_image() const; ImageStyleValue& as_image() { return const_cast(const_cast(*this).as_image()); } bool is_integer() const { return type() == Type::Integer; } IntegerStyleValue const& as_integer() const; IntegerStyleValue& as_integer() { return const_cast(const_cast(*this).as_integer()); } bool is_keyword() const { return type() == Type::Keyword; } CSSKeywordValue const& as_keyword() const; CSSKeywordValue& as_keyword() { return const_cast(const_cast(*this).as_keyword()); } bool is_length() const { return type() == Type::Length; } LengthStyleValue const& as_length() const; LengthStyleValue& as_length() { return const_cast(const_cast(*this).as_length()); } bool is_linear_gradient() const { return type() == Type::LinearGradient; } LinearGradientStyleValue const& as_linear_gradient() const; LinearGradientStyleValue& as_linear_gradient() { return const_cast(const_cast(*this).as_linear_gradient()); } bool is_math_depth() const { return type() == Type::MathDepth; } MathDepthStyleValue const& as_math_depth() const; MathDepthStyleValue& as_math_depth() { return const_cast(const_cast(*this).as_math_depth()); } bool is_number() const { return type() == Type::Number; } NumberStyleValue const& as_number() const; NumberStyleValue& as_number() { return const_cast(const_cast(*this).as_number()); } bool is_open_type_tagged() const { return type() == Type::OpenTypeTagged; } OpenTypeTaggedStyleValue const& as_open_type_tagged() const; OpenTypeTaggedStyleValue& as_open_type_tagged() { return const_cast(const_cast(*this).as_open_type_tagged()); } bool is_percentage() const { return type() == Type::Percentage; } PercentageStyleValue const& as_percentage() const; PercentageStyleValue& as_percentage() { return const_cast(const_cast(*this).as_percentage()); } bool is_position() const { return type() == Type::Position; } PositionStyleValue const& as_position() const; PositionStyleValue& as_position() { return const_cast(const_cast(*this).as_position()); } bool is_radial_gradient() const { return type() == Type::RadialGradient; } RadialGradientStyleValue const& as_radial_gradient() const; RadialGradientStyleValue& as_radial_gradient() { return const_cast(const_cast(*this).as_radial_gradient()); } bool is_ratio() const { return type() == Type::Ratio; } RatioStyleValue const& as_ratio() const; RatioStyleValue& as_ratio() { return const_cast(const_cast(*this).as_ratio()); } bool is_rect() const { return type() == Type::Rect; } RectStyleValue const& as_rect() const; RectStyleValue& as_rect() { return const_cast(const_cast(*this).as_rect()); } bool is_resolution() const { return type() == Type::Resolution; } ResolutionStyleValue const& as_resolution() const; ResolutionStyleValue& as_resolution() { return const_cast(const_cast(*this).as_resolution()); } bool is_rotation() const { return type() == Type::Rotation; } RotationStyleValue const& as_rotation() const; RotationStyleValue& as_rotation() { return const_cast(const_cast(*this).as_rotation()); } bool is_scrollbar_gutter() const { return type() == Type::ScrollbarGutter; } ScrollbarGutterStyleValue const& as_scrollbar_gutter() const; ScrollbarGutterStyleValue& as_scrollbar_gutter() { return const_cast(const_cast(*this).as_scrollbar_gutter()); } bool is_shadow() const { return type() == Type::Shadow; } ShadowStyleValue const& as_shadow() const; ShadowStyleValue& as_shadow() { return const_cast(const_cast(*this).as_shadow()); } bool is_shorthand() const { return type() == Type::Shorthand; } ShorthandStyleValue const& as_shorthand() const; ShorthandStyleValue& as_shorthand() { return const_cast(const_cast(*this).as_shorthand()); } bool is_string() const { return type() == Type::String; } StringStyleValue const& as_string() const; StringStyleValue& as_string() { return const_cast(const_cast(*this).as_string()); } bool is_time() const { return type() == Type::Time; } TimeStyleValue const& as_time() const; TimeStyleValue& as_time() { return const_cast(const_cast(*this).as_time()); } bool is_transformation() const { return type() == Type::Transformation; } TransformationStyleValue const& as_transformation() const; TransformationStyleValue& as_transformation() { return const_cast(const_cast(*this).as_transformation()); } bool is_transition() const { return type() == Type::Transition; } TransitionStyleValue const& as_transition() const; TransitionStyleValue& as_transition() { return const_cast(const_cast(*this).as_transition()); } bool is_unresolved() const { return type() == Type::Unresolved; } UnresolvedStyleValue const& as_unresolved() const; UnresolvedStyleValue& as_unresolved() { return const_cast(const_cast(*this).as_unresolved()); } bool is_url() const { return type() == Type::URL; } URLStyleValue const& as_url() const; URLStyleValue& as_url() { return const_cast(const_cast(*this).as_url()); } bool is_value_list() const { return type() == Type::ValueList; } StyleValueList const& as_value_list() const; StyleValueList& as_value_list() { return const_cast(const_cast(*this).as_value_list()); } // https://www.w3.org/TR/css-values-4/#common-keywords // https://drafts.csswg.org/css-cascade-4/#valdef-all-revert bool is_css_wide_keyword() const { return is_inherit() || is_initial() || is_revert() || is_unset() || is_revert_layer(); } bool is_inherit() const { return to_keyword() == Keyword::Inherit; } bool is_initial() const { return to_keyword() == Keyword::Initial; } bool is_revert() const { return to_keyword() == Keyword::Revert; } bool is_revert_layer() const { return to_keyword() == Keyword::RevertLayer; } bool is_unset() const { return to_keyword() == Keyword::Unset; } bool has_auto() const; virtual bool has_color() const { return false; } virtual ValueComparingNonnullRefPtr absolutized(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const; virtual Color to_color(Optional) const { return {}; } Keyword to_keyword() const; enum class SerializationMode { Normal, ResolvedValue, }; virtual String to_string(SerializationMode) const = 0; [[nodiscard]] int to_font_weight() const; [[nodiscard]] int to_font_slope() const; [[nodiscard]] int to_font_width() const; [[nodiscard]] Optional to_font_variant_alternates() const; [[nodiscard]] Optional to_font_variant_caps() const; [[nodiscard]] Optional to_font_variant_east_asian() const; [[nodiscard]] Optional to_font_variant_emoji() const; [[nodiscard]] Optional to_font_variant_ligatures() const; [[nodiscard]] Optional to_font_variant_numeric() const; [[nodiscard]] Optional to_font_variant_position() const; virtual bool equals(CSSStyleValue const& other) const = 0; bool operator==(CSSStyleValue const& other) const { return this->equals(other); } protected: explicit CSSStyleValue(Type); private: Type m_type; }; template struct StyleValueWithDefaultOperators : public CSSStyleValue { using CSSStyleValue::CSSStyleValue; virtual bool equals(CSSStyleValue const& other) const override { if (type() != other.type()) return false; auto const& typed_other = static_cast(other); return static_cast(*this).properties_equal(typed_other); } }; } template<> struct AK::Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Web::CSS::CSSStyleValue const& style_value) { return Formatter::format(builder, style_value.to_string(Web::CSS::CSSStyleValue::SerializationMode::Normal)); } };