/* * 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 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 BoxShadowPlacement { 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 ImageRendering { Auto, Pixelated }; 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 TextTransform { None, Capitalize, Uppercase, Lowercase, FullWidth, FullSizeKana, }; enum class TransformFunction { TranslateY, }; enum class WhiteSpace { Normal, Pre, Nowrap, PreLine, PreWrap, }; enum class PointerEvents { Auto, All, None }; class StyleValue : public RefCounted { public: virtual ~StyleValue(); enum class Type { Angle, Background, BackgroundRepeat, BackgroundSize, Border, BorderRadius, BoxShadow, Calculated, Color, CombinedBorderRadius, Content, Flex, FlexFlow, Font, Frequency, Identifier, Image, Inherit, Initial, Invalid, Length, ListStyle, Numeric, Overflow, Percentage, Position, Resolution, 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_box_shadow() const { return type() == Type::BoxShadow; } 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_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; BoxShadowStyleValue const& as_box_shadow() 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; 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()); } BoxShadowStyleValue& as_box_shadow() { return const_cast(const_cast(*this).as_box_shadow()); } 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()); } 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 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(); } virtual void visit_lengths(Function) { } 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 { } 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 { } Repeat repeat_x() const { return m_repeat_x; } Repeat repeat_y() const { return m_repeat_y; } virtual String to_string() 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 { } LengthPercentage size_x() const { return m_size_x; } LengthPercentage size_y() const { return m_size_y; } virtual String to_string() 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 { } 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 { } 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 void visit_lengths(Function visitor) override { if (!m_horizontal_radius.is_percentage()) { Length temp = m_horizontal_radius.length(); visitor(temp); m_horizontal_radius = move(temp); } if (!m_vertical_radius.is_percentage()) { Length temp = m_vertical_radius.length(); visitor(temp); m_vertical_radius = move(temp); } } bool m_is_elliptical; LengthPercentage m_horizontal_radius; LengthPercentage m_vertical_radius; }; class BoxShadowStyleValue final : public StyleValue { public: static NonnullRefPtr create(Color const& color, Length const& offset_x, Length const& offset_y, Length const& blur_radius, Length const& spread_distance, BoxShadowPlacement placement) { return adopt_ref(*new BoxShadowStyleValue(color, offset_x, offset_y, blur_radius, spread_distance, placement)); } virtual ~BoxShadowStyleValue() override { } Color const& color() const { return m_color; } Length const& offset_x() const { return m_offset_x; } Length const& offset_y() const { return m_offset_y; } Length const& blur_radius() const { return m_blur_radius; } Length const& spread_distance() const { return m_spread_distance; } BoxShadowPlacement placement() const { return m_placement; } virtual String to_string() const override; private: explicit BoxShadowStyleValue(Color const& color, Length const& offset_x, Length const& offset_y, Length const& blur_radius, Length const& spread_distance, BoxShadowPlacement placement) : StyleValue(Type::BoxShadow) , m_color(color) , m_offset_x(offset_x) , m_offset_y(offset_y) , m_blur_radius(blur_radius) , m_spread_distance(spread_distance) , m_placement(placement) { } virtual void visit_lengths(Function visitor) override { visitor(m_offset_x); visitor(m_offset_y); visitor(m_blur_radius); visitor(m_spread_distance); } Color m_color; Length m_offset_x; Length m_offset_y; Length m_blur_radius; Length m_spread_distance; BoxShadowPlacement m_placement; }; 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, }; struct Number { bool is_integer; float value; }; 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