mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:52:45 +00:00 
			
		
		
		
	LibWeb: Implement CSS Frequency class
This corresponds to `<frequency>` in the grammar.
This commit is contained in:
		
							parent
							
								
									355d1936f2
								
							
						
					
					
						commit
						bd79c303f6
					
				
					 9 changed files with 267 additions and 5 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										86
									
								
								Userland/Libraries/LibWeb/CSS/Frequency.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								Userland/Libraries/LibWeb/CSS/Frequency.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #include "Frequency.h" | ||||
| #include <LibWeb/CSS/StyleValue.h> | ||||
| 
 | ||||
| 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<CalculatedStyleValue> 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::Type> 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 {}; | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										53
									
								
								Userland/Libraries/LibWeb/CSS/Frequency.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								Userland/Libraries/LibWeb/CSS/Frequency.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,53 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <AK/RefPtr.h> | ||||
| #include <LibWeb/Forward.h> | ||||
| 
 | ||||
| namespace Web::CSS { | ||||
| class Frequency { | ||||
| public: | ||||
|     enum class Type { | ||||
|         Calculated, | ||||
|         Hz, | ||||
|         kHz | ||||
|     }; | ||||
| 
 | ||||
|     static Optional<Type> unit_from_name(StringView); | ||||
| 
 | ||||
|     Frequency(int value, Type type); | ||||
|     Frequency(float value, Type type); | ||||
|     static Frequency make_calculated(NonnullRefPtr<CalculatedStyleValue>); | ||||
|     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<CalculatedStyleValue> m_calculated_style; | ||||
| }; | ||||
| } | ||||
|  | @ -4300,6 +4300,8 @@ Optional<CalculatedStyleValue::CalcValue> Parser::parse_calc_value(TokenStream<S | |||
| 
 | ||||
|         if (dimension.is_angle()) | ||||
|             return CalculatedStyleValue::CalcValue { dimension.angle() }; | ||||
|         if (dimension.is_frequency()) | ||||
|             return CalculatedStyleValue::CalcValue { dimension.frequency() }; | ||||
|         if (dimension.is_length()) | ||||
|             return CalculatedStyleValue::CalcValue { dimension.length() }; | ||||
|         if (dimension.is_percentage()) | ||||
|  |  | |||
|  | @ -14,6 +14,11 @@ Angle AnglePercentage::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> co | |||
|     return calculated->resolve_angle_percentage(reference_value)->resolved(layout_node, reference_value); | ||||
| } | ||||
| 
 | ||||
| Frequency FrequencyPercentage::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> 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<CalculatedStyleValue> 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); | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include <AK/String.h> | ||||
| #include <AK/Variant.h> | ||||
| #include <LibWeb/CSS/Angle.h> | ||||
| #include <LibWeb/CSS/Frequency.h> | ||||
| #include <LibWeb/CSS/Length.h> | ||||
| 
 | ||||
| namespace Web::CSS { | ||||
|  | @ -160,6 +161,16 @@ public: | |||
|     Angle const& angle() const { return get_t(); } | ||||
|     virtual Angle resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const&, Layout::Node const&, Angle const& reference_value) const override; | ||||
| }; | ||||
| 
 | ||||
| class FrequencyPercentage : public PercentageOr<Frequency> { | ||||
| public: | ||||
|     using PercentageOr<Frequency>::PercentageOr; | ||||
| 
 | ||||
|     bool is_frequency() const { return is_t(); } | ||||
|     Frequency const& frequency() const { return get_t(); } | ||||
|     virtual Frequency resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const&, Layout::Node const&, Frequency const& reference_value) const override; | ||||
| }; | ||||
| 
 | ||||
| class LengthPercentage : public PercentageOr<Length> { | ||||
| public: | ||||
|     using PercentageOr<Length>::PercentageOr; | ||||
|  | @ -187,6 +198,15 @@ struct AK::Formatter<Web::CSS::AnglePercentage> : Formatter<StringView> { | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct AK::Formatter<Web::CSS::FrequencyPercentage> : Formatter<StringView> { | ||||
|     ErrorOr<void> format(FormatBuilder& builder, Web::CSS::FrequencyPercentage const& frequency_percentage) | ||||
|     { | ||||
|         return Formatter<StringView>::format(builder, frequency_percentage.to_string()); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct AK::Formatter<Web::CSS::LengthPercentage> : Formatter<StringView> { | ||||
|     ErrorOr<void> format(FormatBuilder& builder, Web::CSS::LengthPercentage const& length_percentage) | ||||
|     { | ||||
|  |  | |||
|  | @ -99,6 +99,12 @@ FontStyleValue const& StyleValue::as_font() const | |||
|     return static_cast<FontStyleValue const&>(*this); | ||||
| } | ||||
| 
 | ||||
| FrequencyStyleValue const& StyleValue::as_frequency() const | ||||
| { | ||||
|     VERIFY(is_frequency()); | ||||
|     return static_cast<FrequencyStyleValue const&>(*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<Frequency>()) { | ||||
|                 auto other_hertz = other.m_value.get<Frequency>().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<Frequency>()); | ||||
| 
 | ||||
|                 auto other_hertz = percentage_basis.get<Frequency>().percentage_of(other.m_value.get<Percentage>()).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<Length>()) { | ||||
|  | @ -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<Number>().value); | ||||
|         }, | ||||
|         [&](Frequency const& frequency) { | ||||
|             m_value = Frequency::make_hertz(frequency.to_hertz() * other.m_value.get<Number>().value); | ||||
|         }, | ||||
|         [&](Length const& length) { | ||||
|             VERIFY(layout_node); | ||||
|             m_value = Length::make_px(length.to_px(*layout_node) * other.m_value.get<Number>().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<AnglePercentage> CalculatedStyleValue::resolve_angle_percentage(Angle c | |||
|         }); | ||||
| } | ||||
| 
 | ||||
| Optional<Frequency> CalculatedStyleValue::resolve_frequency() const | ||||
| { | ||||
|     auto result = m_expression->resolve(nullptr, {}); | ||||
| 
 | ||||
|     if (result.value().has<Frequency>()) | ||||
|         return result.value().get<Frequency>(); | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| Optional<FrequencyPercentage> 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<FrequencyPercentage> { | ||||
|             return frequency; | ||||
|         }, | ||||
|         [&](Percentage const& percentage) -> Optional<FrequencyPercentage> { | ||||
|             return percentage; | ||||
|         }, | ||||
|         [&](auto const&) -> Optional<FrequencyPercentage> { | ||||
|             return {}; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| Optional<Length> CalculatedStyleValue::resolve_length(Layout::Node const& layout_node) const | ||||
| { | ||||
|     auto result = m_expression->resolve(&layout_node, {}); | ||||
|  | @ -745,6 +800,7 @@ Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcValue::re | |||
|             return { number.is_integer ? ResolvedType::Integer : ResolvedType::Number }; | ||||
|         }, | ||||
|         [](Angle const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Angle }; }, | ||||
|         [](Frequency const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Frequency }; }, | ||||
|         [](Length const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Length }; }, | ||||
|         [](Percentage const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Percentage }; }, | ||||
|         [](NonnullOwnPtr<CalcSum> const& sum) { return sum->resolved_type(); }); | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ | |||
| #include <LibGfx/Color.h> | ||||
| #include <LibWeb/CSS/Angle.h> | ||||
| #include <LibWeb/CSS/Display.h> | ||||
| #include <LibWeb/CSS/Frequency.h> | ||||
| #include <LibWeb/CSS/Length.h> | ||||
| #include <LibWeb/CSS/Parser/StyleComponentValueRule.h> | ||||
| #include <LibWeb/CSS/Percentage.h> | ||||
|  | @ -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<FlexFlowStyleValue&>(const_cast<StyleValue const&>(*this).as_flex_flow()); } | ||||
|     FlexStyleValue& as_flex() { return const_cast<FlexStyleValue&>(const_cast<StyleValue const&>(*this).as_flex()); } | ||||
|     FontStyleValue& as_font() { return const_cast<FontStyleValue&>(const_cast<StyleValue const&>(*this).as_font()); } | ||||
|     FrequencyStyleValue& as_frequency() { return const_cast<FrequencyStyleValue&>(const_cast<StyleValue const&>(*this).as_frequency()); } | ||||
|     IdentifierStyleValue& as_identifier() { return const_cast<IdentifierStyleValue&>(const_cast<StyleValue const&>(*this).as_identifier()); } | ||||
|     ImageStyleValue& as_image() { return const_cast<ImageStyleValue&>(const_cast<StyleValue const&>(*this).as_image()); } | ||||
|     InheritStyleValue& as_inherit() { return const_cast<InheritStyleValue&>(const_cast<StyleValue const&>(*this).as_inherit()); } | ||||
|  | @ -730,11 +735,11 @@ public: | |||
|         float value; | ||||
|     }; | ||||
| 
 | ||||
|     using PercentageBasis = Variant<Empty, Angle, Length>; | ||||
|     using PercentageBasis = Variant<Empty, Angle, Frequency, Length>; | ||||
| 
 | ||||
|     class CalculationResult { | ||||
|     public: | ||||
|         CalculationResult(Variant<Number, Angle, Length, Percentage> value) | ||||
|         CalculationResult(Variant<Number, Angle, Frequency, Length, Percentage> 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<Number, Angle, Length, Percentage> const& value() const { return m_value; } | ||||
|         Variant<Number, Angle, Frequency, Length, Percentage> 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<Number, Angle, Length, Percentage> m_value; | ||||
|         Variant<Number, Angle, Frequency, Length, Percentage> m_value; | ||||
|     }; | ||||
| 
 | ||||
|     struct CalcSum; | ||||
|  | @ -767,7 +772,7 @@ public: | |||
|     }; | ||||
| 
 | ||||
|     struct CalcValue { | ||||
|         Variant<Number, Angle, Length, Percentage, NonnullOwnPtr<CalcSum>> value; | ||||
|         Variant<Number, Angle, Frequency, Length, Percentage, NonnullOwnPtr<CalcSum>> value; | ||||
|         String to_string() const; | ||||
|         Optional<ResolvedType> resolved_type() const; | ||||
|         CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const; | ||||
|  | @ -873,6 +878,8 @@ public: | |||
| 
 | ||||
|     Optional<Angle> resolve_angle() const; | ||||
|     Optional<AnglePercentage> resolve_angle_percentage(Angle const& percentage_basis) const; | ||||
|     Optional<Frequency> resolve_frequency() const; | ||||
|     Optional<FrequencyPercentage> resolve_frequency_percentage(Frequency const& percentage_basis) const; | ||||
|     Optional<Length> resolve_length(Layout::Node const& layout_node) const; | ||||
|     Optional<LengthPercentage> resolve_length_percentage(Layout::Node const&, Length const& percentage_basis) const; | ||||
|     Optional<Percentage> resolve_percentage() const; | ||||
|  | @ -1040,6 +1047,35 @@ private: | |||
|     // FIXME: Implement font-stretch and font-variant.
 | ||||
| }; | ||||
| 
 | ||||
| class FrequencyStyleValue : public StyleValue { | ||||
| public: | ||||
|     static NonnullRefPtr<FrequencyStyleValue> 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<FrequencyStyleValue const&>(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<IdentifierStyleValue> create(CSS::ValueID id) | ||||
|  |  | |||
|  | @ -43,6 +43,9 @@ class ElementInlineCSSStyleDeclaration; | |||
| class FlexFlowStyleValue; | ||||
| class FlexStyleValue; | ||||
| class FontStyleValue; | ||||
| class Frequency; | ||||
| class FrequencyPercentage; | ||||
| class FrequencyStyleValue; | ||||
| class IdentifierStyleValue; | ||||
| class ImageStyleValue; | ||||
| class InheritStyleValue; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sam Atkins
						Sam Atkins