diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 57e3ecc9a8..f549b6dc34 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -28,6 +28,7 @@ set(SOURCES CSS/CSSSupportsRule.cpp CSS/DefaultStyleSheetSource.cpp CSS/Display.cpp + CSS/Frequency.cpp CSS/Length.cpp CSS/MediaList.cpp CSS/MediaQuery.cpp diff --git a/Userland/Libraries/LibWeb/CSS/Frequency.cpp b/Userland/Libraries/LibWeb/CSS/Frequency.cpp new file mode 100644 index 0000000000..31d1eb3ab4 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Frequency.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Frequency.h" +#include + +namespace Web::CSS { + +Frequency::Frequency(int value, Type type) + : m_type(type) + , m_value(value) +{ +} + +Frequency::Frequency(float value, Type type) + : m_type(type) + , m_value(value) +{ +} + +Frequency Frequency::make_calculated(NonnullRefPtr calculated_style_value) +{ + Frequency frequency { 0, Type::Calculated }; + frequency.m_calculated_style = move(calculated_style_value); + return frequency; +} + +Frequency Frequency::make_hertz(float value) +{ + return { value, Type::Hz }; +} + +Frequency Frequency::percentage_of(Percentage const& percentage) const +{ + VERIFY(!is_calculated()); + + return Frequency { percentage.as_fraction() * m_value, m_type }; +} + +String Frequency::to_string() const +{ + if (is_calculated()) + return m_calculated_style->to_string(); + return String::formatted("{}{}", m_value, unit_name()); +} + +float Frequency::to_hertz() const +{ + switch (m_type) { + case Type::Calculated: + return m_calculated_style->resolve_frequency()->to_hertz(); + case Type::Hz: + return m_value; + case Type::kHz: + return m_value * 1000; + } + VERIFY_NOT_REACHED(); +} + +StringView Frequency::unit_name() const +{ + switch (m_type) { + case Type::Calculated: + return "calculated"sv; + case Type::Hz: + return "hz"sv; + case Type::kHz: + return "khz"sv; + } + VERIFY_NOT_REACHED(); +} + +Optional Frequency::unit_from_name(StringView name) +{ + if (name.equals_ignoring_case("hz"sv)) { + return Type::Hz; + } else if (name.equals_ignoring_case("khz"sv)) { + return Type::kHz; + } + return {}; +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/Frequency.h b/Userland/Libraries/LibWeb/CSS/Frequency.h new file mode 100644 index 0000000000..c71cc67a1a --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Frequency.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::CSS { +class Frequency { +public: + enum class Type { + Calculated, + Hz, + kHz + }; + + static Optional unit_from_name(StringView); + + Frequency(int value, Type type); + Frequency(float value, Type type); + static Frequency make_calculated(NonnullRefPtr); + static Frequency make_hertz(float); + Frequency percentage_of(Percentage const&) const; + + bool is_calculated() const { return m_type == Type::Calculated; } + + String to_string() const; + float to_hertz() const; + + bool operator==(Frequency const& other) const + { + if (is_calculated()) + return m_calculated_style == other.m_calculated_style; + return m_type == other.m_type && m_value == other.m_value; + } + + bool operator!=(Frequency const& other) const + { + return !(*this == other); + } + +private: + StringView unit_name() const; + + Type m_type; + float m_value { 0 }; + RefPtr m_calculated_style; +}; +} diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 4628a97779..14974066a8 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -4300,6 +4300,8 @@ Optional Parser::parse_calc_value(TokenStream co return calculated->resolve_angle_percentage(reference_value)->resolved(layout_node, reference_value); } +Frequency FrequencyPercentage::resolve_calculated(NonnullRefPtr const& calculated, Layout::Node const& layout_node, Frequency const& reference_value) const +{ + return calculated->resolve_frequency_percentage(reference_value)->resolved(layout_node, reference_value); +} + Length LengthPercentage::resolve_calculated(NonnullRefPtr const& calculated, Layout::Node const& layout_node, Length const& reference_value) const { return calculated->resolve_length_percentage(layout_node, reference_value)->resolved(layout_node, reference_value); diff --git a/Userland/Libraries/LibWeb/CSS/Percentage.h b/Userland/Libraries/LibWeb/CSS/Percentage.h index 2bdda93138..692bef76c2 100644 --- a/Userland/Libraries/LibWeb/CSS/Percentage.h +++ b/Userland/Libraries/LibWeb/CSS/Percentage.h @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace Web::CSS { @@ -160,6 +161,16 @@ public: Angle const& angle() const { return get_t(); } virtual Angle resolve_calculated(NonnullRefPtr const&, Layout::Node const&, Angle const& reference_value) const override; }; + +class FrequencyPercentage : public PercentageOr { +public: + using PercentageOr::PercentageOr; + + bool is_frequency() const { return is_t(); } + Frequency const& frequency() const { return get_t(); } + virtual Frequency resolve_calculated(NonnullRefPtr const&, Layout::Node const&, Frequency const& reference_value) const override; +}; + class LengthPercentage : public PercentageOr { public: using PercentageOr::PercentageOr; @@ -187,6 +198,15 @@ struct AK::Formatter : Formatter { } }; +template<> +struct AK::Formatter : Formatter { + ErrorOr format(FormatBuilder& builder, Web::CSS::FrequencyPercentage const& frequency_percentage) + { + return Formatter::format(builder, frequency_percentage.to_string()); + } +}; + +template<> struct AK::Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Web::CSS::LengthPercentage const& length_percentage) { diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index 914fd669ac..235096a2e0 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -99,6 +99,12 @@ FontStyleValue const& StyleValue::as_font() const 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()); @@ -333,6 +339,24 @@ void CalculatedStyleValue::CalculationResult::add_or_subtract_internal(SumOperat 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()) { @@ -396,6 +420,9 @@ void CalculatedStyleValue::CalculationResult::multiply_by(CalculationResult cons [&](Angle const& angle) { m_value = Angle::make_degrees(angle.to_degrees() * other.m_value.get().value); }, + [&](Frequency const& frequency) { + m_value = Frequency::make_hertz(frequency.to_hertz() * other.m_value.get().value); + }, [&](Length const& length) { VERIFY(layout_node); m_value = Length::make_px(length.to_px(*layout_node) * other.m_value.get().value); @@ -423,6 +450,9 @@ void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const& [&](Angle const& angle) { m_value = Angle::make_degrees(angle.to_degrees() / denominator); }, + [&](Frequency const& frequency) { + m_value = Frequency::make_hertz(frequency.to_hertz() / denominator); + }, [&](Length const& length) { VERIFY(layout_node); m_value = Length::make_px(length.to_px(*layout_node) / denominator); @@ -536,6 +566,31 @@ Optional CalculatedStyleValue::resolve_angle_percentage(Angle c }); } +Optional CalculatedStyleValue::resolve_frequency() const +{ + auto result = m_expression->resolve(nullptr, {}); + + if (result.value().has()) + return result.value().get(); + return {}; +} + +Optional CalculatedStyleValue::resolve_frequency_percentage(Frequency const& percentage_basis) const +{ + auto result = m_expression->resolve(nullptr, percentage_basis); + + return result.value().visit( + [&](Frequency const& frequency) -> Optional { + return frequency; + }, + [&](Percentage const& percentage) -> Optional { + return percentage; + }, + [&](auto const&) -> Optional { + return {}; + }); +} + Optional CalculatedStyleValue::resolve_length(Layout::Node const& layout_node) const { auto result = m_expression->resolve(&layout_node, {}); @@ -745,6 +800,7 @@ Optional CalculatedStyleValue::CalcValue::re return { number.is_integer ? ResolvedType::Integer : ResolvedType::Number }; }, [](Angle const&) -> Optional { return { ResolvedType::Angle }; }, + [](Frequency const&) -> Optional { return { ResolvedType::Frequency }; }, [](Length const&) -> Optional { return { ResolvedType::Length }; }, [](Percentage const&) -> Optional { return { ResolvedType::Percentage }; }, [](NonnullOwnPtr const& sum) { return sum->resolved_type(); }); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index a917626794..0d3fe3cff6 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -297,6 +298,7 @@ public: Flex, FlexFlow, Font, + Frequency, Identifier, Image, Inherit, @@ -330,6 +332,7 @@ public: 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; } @@ -361,6 +364,7 @@ public: 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; @@ -390,6 +394,7 @@ public: 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()); } @@ -730,11 +735,11 @@ public: float value; }; - using PercentageBasis = Variant; + using PercentageBasis = Variant; class CalculationResult { public: - CalculationResult(Variant value) + CalculationResult(Variant value) : m_value(move(value)) { } @@ -743,11 +748,11 @@ public: void multiply_by(CalculationResult const& other, Layout::Node const*); void divide_by(CalculationResult const& other, Layout::Node const*); - Variant const& value() const { return m_value; } + Variant const& value() const { return m_value; } private: void add_or_subtract_internal(SumOperation op, CalculationResult const& other, Layout::Node const*, PercentageBasis const& percentage_basis); - Variant m_value; + Variant m_value; }; struct CalcSum; @@ -767,7 +772,7 @@ public: }; struct CalcValue { - Variant> value; + Variant> value; String to_string() const; Optional resolved_type() const; CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; @@ -873,6 +878,8 @@ public: 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; @@ -1040,6 +1047,35 @@ private: // FIXME: Implement font-stretch and font-variant. }; +class FrequencyStyleValue : public StyleValue { +public: + static NonnullRefPtr create(Frequency frequency) + { + return adopt_ref(*new FrequencyStyleValue(move(frequency))); + } + virtual ~FrequencyStyleValue() override { } + + Frequency const& frequency() const { return m_frequency; } + + virtual String to_string() const override { return m_frequency.to_string(); } + + virtual bool equals(StyleValue const& other) const override + { + if (type() != other.type()) + return false; + return m_frequency == static_cast(other).m_frequency; + } + +private: + explicit FrequencyStyleValue(Frequency frequency) + : StyleValue(Type::Frequency) + , m_frequency(move(frequency)) + { + } + + Frequency m_frequency; +}; + class IdentifierStyleValue final : public StyleValue { public: static NonnullRefPtr create(CSS::ValueID id) diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 5e3dd94ca1..89d521db3f 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -43,6 +43,9 @@ class ElementInlineCSSStyleDeclaration; class FlexFlowStyleValue; class FlexStyleValue; class FontStyleValue; +class Frequency; +class FrequencyPercentage; +class FrequencyStyleValue; class IdentifierStyleValue; class ImageStyleValue; class InheritStyleValue;