/* * 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 namespace Web::CSS { enum class AlignItems { FlexStart, FlexEnd, Center, Baseline, Stretch, }; enum class BackgroundAttachment { Fixed, Local, Scroll, }; enum class BackgroundBox { BorderBox, ContentBox, PaddingBox, }; enum class BackgroundSize { Contain, Cover, LengthPercentage, }; enum class BoxSizing { BorderBox, ContentBox, }; enum class ShadowPlacement { Outer, Inner, }; enum class Clear { None, Left, Right, Both, }; enum class Cursor { Auto, Default, None, ContextMenu, Help, Pointer, Progress, Wait, Cell, Crosshair, Text, VerticalText, Alias, Copy, Move, NoDrop, NotAllowed, Grab, Grabbing, EResize, NResize, NeResize, NwResize, SResize, SeResize, SwResize, WResize, EwResize, NsResize, NeswResize, NwseResize, ColResize, RowResize, AllScroll, ZoomIn, ZoomOut, }; enum class FlexBasis { Content, LengthPercentage, Auto, }; enum class FlexDirection { Row, RowReverse, Column, ColumnReverse, }; enum class FlexWrap { Nowrap, Wrap, WrapReverse }; enum class Float { None, Left, Right, }; enum class FontVariant { Normal, SmallCaps, }; enum class ImageRendering { Auto, CrispEdges, HighQuality, Pixelated, Smooth, }; // 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: case CSS::ImageRendering::Pixelated: return Gfx::Painter::ScalingMode::NearestNeighbor; } VERIFY_NOT_REACHED(); } enum class JustifyContent { FlexStart, FlexEnd, Center, SpaceBetween, SpaceAround, }; enum class LineStyle { None, Hidden, Dotted, Dashed, Solid, Double, Groove, Ridge, Inset, Outset, }; enum class ListStyleType { None, Disc, Circle, Square, Decimal, DecimalLeadingZero, LowerAlpha, LowerLatin, LowerRoman, UpperAlpha, UpperLatin, UpperRoman, }; enum class Overflow : u8 { Auto, Clip, Hidden, Scroll, Visible, }; enum class Position { Static, Relative, Absolute, Fixed, Sticky, }; enum class PositionEdge { Left, Right, Top, Bottom, }; enum class Repeat : u8 { NoRepeat, Repeat, Round, Space, }; constexpr StringView to_string(Repeat value) { switch (value) { case Repeat::NoRepeat: return "no-repeat"sv; case Repeat::Repeat: return "repeat"sv; case Repeat::Round: return "round"sv; case Repeat::Space: return "space"sv; default: VERIFY_NOT_REACHED(); } } enum class TextAlign { Left, Center, Right, Justify, LibwebCenter, }; enum class TextDecorationLine { None, Underline, Overline, LineThrough, Blink, }; enum class TextDecorationStyle { Solid, Double, Dotted, Dashed, Wavy, }; enum class TextJustify { Auto, None, InterWord, InterCharacter, }; enum class TextTransform { None, Capitalize, Uppercase, Lowercase, FullWidth, FullSizeKana, }; enum class TransformFunction { Matrix, Translate, TranslateX, TranslateY, Scale, ScaleX, ScaleY, Rotate, Skew, SkewX, SkewY, }; enum class VerticalAlign { Baseline, Bottom, Middle, Sub, Super, TextBottom, TextTop, Top, }; enum class Visibility { Visible, Hidden, Collapse, }; enum class WhiteSpace { Normal, Pre, Nowrap, PreLine, PreWrap, }; enum class PointerEvents { Auto, All, None }; class StyleValue : public RefCounted { public: virtual ~StyleValue() = default; enum class Type { Angle, Background, BackgroundRepeat, BackgroundSize, Border, BorderRadius, Calculated, Color, CombinedBorderRadius, Content, Flex, FlexFlow, Font, Frequency, Identifier, Image, Inherit, Initial, Invalid, Length, ListStyle, Numeric, Overflow, Percentage, Position, Resolution, Shadow, String, TextDecoration, Time, Transformation, Unresolved, Unset, ValueList, }; Type type() const { return m_type; } 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_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_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_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(); } 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; 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; 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; 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; 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()); } 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()); } 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()); } 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_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 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 { if (type() != other.type()) return false; return to_string() == other.to_string(); } 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; 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 { if (type() != other.type()) return false; auto& other_value = static_cast(other); return m_repeat_x == other_value.m_repeat_x && m_repeat_y == other_value.m_repeat_y; } 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 { if (type() != other.type()) return false; auto& other_value = static_cast(other); return m_size_x == other_value.m_size_x && m_size_y == other_value.m_size_y; } 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; 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 { if (type() != other.type()) return false; auto& other_value = static_cast(other); return m_is_elliptical == other_value.m_is_elliptical && m_horizontal_radius == other_value.m_horizontal_radius && m_vertical_radius == other_value.m_vertical_radius; } 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 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; ResolvedType resolved_type() const { return m_resolved_type; } NonnullOwnPtr const& expression() const { return m_expression; } Optional resolve_angle() const; Optional resolve_angle_percentage(Angle const& percentage_basis) const; Optional resolve_frequency() const; Optional resolve_frequency_percentage(Frequency const& percentage_basis) const; Optional resolve_length(Layout::Node const& layout_node) const; Optional resolve_length_percentage(Layout::Node const&, Length const& percentage_basis) const; Optional resolve_percentage() const; Optional