/* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021-2022, Sam Atkins * Copyright (c) 2021, Tobias Christiansen * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include namespace Web::CSS { StyleValue::StyleValue(Type type) : m_type(type) { } AngleStyleValue const& StyleValue::as_angle() const { VERIFY(is_angle()); return static_cast(*this); } BackgroundStyleValue const& StyleValue::as_background() const { VERIFY(is_background()); return static_cast(*this); } BackgroundRepeatStyleValue const& StyleValue::as_background_repeat() const { VERIFY(is_background_repeat()); return static_cast(*this); } BackgroundSizeStyleValue const& StyleValue::as_background_size() const { VERIFY(is_background_size()); return static_cast(*this); } BorderStyleValue const& StyleValue::as_border() const { VERIFY(is_border()); return static_cast(*this); } BorderRadiusStyleValue const& StyleValue::as_border_radius() const { VERIFY(is_border_radius()); return static_cast(*this); } BorderRadiusShorthandStyleValue const& StyleValue::as_border_radius_shorthand() const { VERIFY(is_border_radius_shorthand()); return static_cast(*this); } ShadowStyleValue const& StyleValue::as_shadow() const { VERIFY(is_shadow()); return static_cast(*this); } CalculatedStyleValue const& StyleValue::as_calculated() const { VERIFY(is_calculated()); return static_cast(*this); } ColorStyleValue const& StyleValue::as_color() const { VERIFY(is_color()); return static_cast(*this); } ContentStyleValue const& StyleValue::as_content() const { VERIFY(is_content()); return static_cast(*this); } FlexStyleValue const& StyleValue::as_flex() const { VERIFY(is_flex()); return static_cast(*this); } FlexFlowStyleValue const& StyleValue::as_flex_flow() const { VERIFY(is_flex_flow()); return static_cast(*this); } FontStyleValue const& StyleValue::as_font() const { VERIFY(is_font()); return static_cast(*this); } FrequencyStyleValue const& StyleValue::as_frequency() const { VERIFY(is_frequency()); return static_cast(*this); } IdentifierStyleValue const& StyleValue::as_identifier() const { VERIFY(is_identifier()); return static_cast(*this); } ImageStyleValue const& StyleValue::as_image() const { VERIFY(is_image()); return static_cast(*this); } InheritStyleValue const& StyleValue::as_inherit() const { VERIFY(is_inherit()); return static_cast(*this); } InitialStyleValue const& StyleValue::as_initial() const { VERIFY(is_initial()); return static_cast(*this); } LengthStyleValue const& StyleValue::as_length() const { VERIFY(is_length()); return static_cast(*this); } ListStyleStyleValue const& StyleValue::as_list_style() const { VERIFY(is_list_style()); return static_cast(*this); } NumericStyleValue const& StyleValue::as_numeric() const { VERIFY(is_numeric()); return static_cast(*this); } OverflowStyleValue const& StyleValue::as_overflow() const { VERIFY(is_overflow()); return static_cast(*this); } PercentageStyleValue const& StyleValue::as_percentage() const { VERIFY(is_percentage()); return static_cast(*this); } PositionStyleValue const& StyleValue::as_position() const { VERIFY(is_position()); return static_cast(*this); } ResolutionStyleValue const& StyleValue::as_resolution() const { VERIFY(is_resolution()); return static_cast(*this); } StringStyleValue const& StyleValue::as_string() const { VERIFY(is_string()); return static_cast(*this); } TextDecorationStyleValue const& StyleValue::as_text_decoration() const { VERIFY(is_text_decoration()); return static_cast(*this); } TimeStyleValue const& StyleValue::as_time() const { VERIFY(is_time()); return static_cast(*this); } TransformationStyleValue const& StyleValue::as_transformation() const { VERIFY(is_transformation()); return static_cast(*this); } UnresolvedStyleValue const& StyleValue::as_unresolved() const { VERIFY(is_unresolved()); return static_cast(*this); } UnsetStyleValue const& StyleValue::as_unset() const { VERIFY(is_unset()); return static_cast(*this); } StyleValueList const& StyleValue::as_value_list() const { VERIFY(is_value_list()); return static_cast(*this); } BackgroundStyleValue::BackgroundStyleValue( NonnullRefPtr color, NonnullRefPtr image, NonnullRefPtr position, NonnullRefPtr size, NonnullRefPtr repeat, NonnullRefPtr attachment, NonnullRefPtr origin, NonnullRefPtr clip) : StyleValue(Type::Background) , m_color(color) , m_image(image) , m_position(position) , m_size(size) , m_repeat(repeat) , m_attachment(attachment) , m_origin(origin) , m_clip(clip) { auto layer_count = [](auto style_value) -> size_t { if (style_value->is_value_list()) return style_value->as_value_list().size(); else return 1; }; m_layer_count = max(layer_count(m_image), layer_count(m_position)); m_layer_count = max(m_layer_count, layer_count(m_size)); m_layer_count = max(m_layer_count, layer_count(m_repeat)); m_layer_count = max(m_layer_count, layer_count(m_attachment)); m_layer_count = max(m_layer_count, layer_count(m_origin)); m_layer_count = max(m_layer_count, layer_count(m_clip)); VERIFY(!m_color->is_value_list()); } String BackgroundStyleValue::to_string() const { if (m_layer_count == 1) { return String::formatted("{} {} {} {} {} {} {} {}", m_color->to_string(), m_image->to_string(), m_position->to_string(), m_size->to_string(), m_repeat->to_string(), m_attachment->to_string(), m_origin->to_string(), m_clip->to_string()); } auto get_layer_value_string = [](NonnullRefPtr const& style_value, size_t index) { if (style_value->is_value_list()) return style_value->as_value_list().value_at(index, true)->to_string(); return style_value->to_string(); }; StringBuilder builder; for (size_t i = 0; i < m_layer_count; i++) { if (i) builder.append(", "); if (i == m_layer_count - 1) builder.appendff("{} ", m_color->to_string()); builder.appendff("{} {} {} {} {} {} {}", get_layer_value_string(m_image, i), get_layer_value_string(m_position, i), get_layer_value_string(m_size, i), get_layer_value_string(m_repeat, i), get_layer_value_string(m_attachment, i), get_layer_value_string(m_origin, i), get_layer_value_string(m_clip, i)); } return builder.to_string(); } bool BackgroundStyleValue::equals(StyleValue const& other) const { if (type() != other.type()) return false; auto const& typed_other = other.as_background(); return m_color->equals(typed_other.m_color) && m_image->equals(typed_other.m_image) && m_position->equals(typed_other.m_position) && m_size->equals(typed_other.m_size) && m_repeat->equals(typed_other.m_repeat) && m_attachment->equals(typed_other.m_attachment) && m_origin->equals(typed_other.m_origin) && m_clip->equals(typed_other.m_clip); } String BackgroundRepeatStyleValue::to_string() const { return String::formatted("{} {}", CSS::to_string(m_repeat_x), CSS::to_string(m_repeat_y)); } bool BackgroundRepeatStyleValue::equals(StyleValue const& other) const { if (type() != other.type()) return false; auto const& typed_other = other.as_background_repeat(); return m_repeat_x == typed_other.m_repeat_x && m_repeat_y == typed_other.m_repeat_y; } String BackgroundSizeStyleValue::to_string() const { return String::formatted("{} {}", m_size_x.to_string(), m_size_y.to_string()); } bool BackgroundSizeStyleValue::equals(StyleValue const& other) const { if (type() != other.type()) return false; auto const& typed_other = other.as_background_size(); return m_size_x == typed_other.m_size_x && m_size_y == typed_other.m_size_y; } String BorderStyleValue::to_string() const { return String::formatted("{} {} {}", m_border_width->to_string(), m_border_style->to_string(), m_border_color->to_string()); } bool BorderStyleValue::equals(StyleValue const& other) const { if (type() != other.type()) return false; auto const& typed_other = other.as_border(); return m_border_width->equals(typed_other.m_border_width) && m_border_style->equals(typed_other.m_border_style) && m_border_color->equals(typed_other.m_border_color); } String BorderRadiusStyleValue::to_string() const { if (m_horizontal_radius == m_vertical_radius) return m_horizontal_radius.to_string(); return String::formatted("{} / {}", m_horizontal_radius.to_string(), m_vertical_radius.to_string()); } bool BorderRadiusStyleValue::equals(StyleValue const& other) const { if (type() != other.type()) return false; auto const& typed_other = other.as_border_radius(); return m_is_elliptical == typed_other.m_is_elliptical && m_horizontal_radius == typed_other.m_horizontal_radius && m_vertical_radius == typed_other.m_vertical_radius; } String BorderRadiusShorthandStyleValue::to_string() const { return String::formatted("{} {} {} {} / {} {} {} {}", m_top_left->horizontal_radius().to_string(), m_top_right->horizontal_radius().to_string(), m_bottom_right->horizontal_radius().to_string(), m_bottom_left->horizontal_radius().to_string(), m_top_left->vertical_radius().to_string(), m_top_right->vertical_radius().to_string(), m_bottom_right->vertical_radius().to_string(), m_bottom_left->vertical_radius().to_string()); } bool BorderRadiusShorthandStyleValue::equals(StyleValue const& other) const { if (type() != other.type()) return false; auto const& typed_other = other.as_border_radius_shorthand(); return m_top_left->equals(typed_other.m_top_left) && m_top_right->equals(typed_other.m_top_right) && m_bottom_right->equals(typed_other.m_bottom_right) && m_bottom_left->equals(typed_other.m_bottom_left); } void CalculatedStyleValue::CalculationResult::add(CalculationResult const& other, Layout::Node const* layout_node, PercentageBasis const& percentage_basis) { add_or_subtract_internal(SumOperation::Add, other, layout_node, percentage_basis); } void CalculatedStyleValue::CalculationResult::subtract(CalculationResult const& other, Layout::Node const* layout_node, PercentageBasis const& percentage_basis) { add_or_subtract_internal(SumOperation::Subtract, other, layout_node, percentage_basis); } void CalculatedStyleValue::CalculationResult::add_or_subtract_internal(SumOperation op, CalculationResult const& other, Layout::Node const* layout_node, PercentageBasis const& percentage_basis) { // We know from validation when resolving the type, that "both sides have the same type, or that one side is a and the other is an ". // Though, having the same type may mean that one side is a and the other a . // Note: This is almost identical to ::add() m_value.visit( [&](Number const& number) { auto other_number = other.m_value.get(); if (op == SumOperation::Add) { m_value = number + other_number; } else { m_value = number - other_number; } }, [&](Angle const& angle) { auto this_degrees = angle.to_degrees(); if (other.m_value.has()) { auto other_degrees = other.m_value.get().to_degrees(); if (op == SumOperation::Add) m_value = Angle::make_degrees(this_degrees + other_degrees); else m_value = Angle::make_degrees(this_degrees - other_degrees); } else { VERIFY(percentage_basis.has()); auto other_degrees = percentage_basis.get().percentage_of(other.m_value.get()).to_degrees(); if (op == SumOperation::Add) m_value = Angle::make_degrees(this_degrees + other_degrees); else m_value = Angle::make_degrees(this_degrees - other_degrees); } }, [&](Frequency const& frequency) { auto this_hertz = frequency.to_hertz(); if (other.m_value.has()) { auto other_hertz = other.m_value.get().to_hertz(); if (op == SumOperation::Add) m_value = Frequency::make_hertz(this_hertz + other_hertz); else m_value = Frequency::make_hertz(this_hertz - other_hertz); } else { VERIFY(percentage_basis.has()); auto other_hertz = percentage_basis.get().percentage_of(other.m_value.get()).to_hertz(); if (op == SumOperation::Add) m_value = Frequency::make_hertz(this_hertz + other_hertz); else m_value = Frequency::make_hertz(this_hertz - other_hertz); } }, [&](Length const& length) { auto this_px = length.to_px(*layout_node); if (other.m_value.has()) { auto other_px = other.m_value.get().to_px(*layout_node); if (op == SumOperation::Add) m_value = Length::make_px(this_px + other_px); else m_value = Length::make_px(this_px - other_px); } else { VERIFY(percentage_basis.has()); auto other_px = percentage_basis.get().percentage_of(other.m_value.get()).to_px(*layout_node); if (op == SumOperation::Add) m_value = Length::make_px(this_px + other_px); else m_value = Length::make_px(this_px - other_px); } }, [&](Time const& time) { auto this_seconds = time.to_seconds(); if (other.m_value.has