Kaynağa Gözat

LibWeb: Implement CSS Time class

This corresponds to `<time>` in the grammar.
Sam Atkins 3 yıl önce
ebeveyn
işleme
608bfac2a9

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -55,6 +55,7 @@ set(SOURCES
     CSS/StyleValue.cpp
     CSS/Supports.cpp
     CSS/SyntaxHighlighter/SyntaxHighlighter.cpp
+    CSS/Time.cpp
     CSS/ValueID.cpp
     CSS/ValueID.h
     Cookie/ParsedCookie.cpp

+ 2 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -4310,6 +4310,8 @@ Optional<CalculatedStyleValue::CalcValue> Parser::parse_calc_value(TokenStream<S
             // Resolution is not allowed in calc()
             return {};
         }
+        if (dimension.is_time())
+            return CalculatedStyleValue::CalcValue { dimension.time() };
         VERIFY_NOT_REACHED();
     }
 

+ 5 - 0
Userland/Libraries/LibWeb/CSS/Percentage.cpp

@@ -24,4 +24,9 @@ Length LengthPercentage::resolve_calculated(NonnullRefPtr<CalculatedStyleValue>
     return calculated->resolve_length_percentage(layout_node, reference_value)->resolved(layout_node, reference_value);
 }
 
+Time TimePercentage::resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const& calculated, Layout::Node const& layout_node, Time const& reference_value) const
+{
+    return calculated->resolve_time_percentage(reference_value)->resolved(layout_node, reference_value);
+}
+
 }

+ 18 - 0
Userland/Libraries/LibWeb/CSS/Percentage.h

@@ -11,6 +11,7 @@
 #include <LibWeb/CSS/Angle.h>
 #include <LibWeb/CSS/Frequency.h>
 #include <LibWeb/CSS/Length.h>
+#include <LibWeb/CSS/Time.h>
 
 namespace Web::CSS {
 
@@ -180,6 +181,15 @@ public:
     virtual Length resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const&, Layout::Node const&, Length const& reference_value) const override;
 };
 
+class TimePercentage : public PercentageOr<Time> {
+public:
+    using PercentageOr<Time>::PercentageOr;
+
+    bool is_time() const { return is_t(); }
+    Time const& time() const { return get_t(); }
+    virtual Time resolve_calculated(NonnullRefPtr<CalculatedStyleValue> const&, Layout::Node const&, Time const& reference_value) const override;
+};
+
 }
 
 template<>
@@ -213,3 +223,11 @@ struct AK::Formatter<Web::CSS::LengthPercentage> : Formatter<StringView> {
         return Formatter<StringView>::format(builder, length_percentage.to_string());
     }
 };
+
+template<>
+struct AK::Formatter<Web::CSS::TimePercentage> : Formatter<StringView> {
+    ErrorOr<void> format(FormatBuilder& builder, Web::CSS::TimePercentage const& time_percentage)
+    {
+        return Formatter<StringView>::format(builder, time_percentage.to_string());
+    }
+};

+ 56 - 0
Userland/Libraries/LibWeb/CSS/StyleValue.cpp

@@ -183,6 +183,12 @@ TextDecorationStyleValue const& StyleValue::as_text_decoration() const
     return static_cast<TextDecorationStyleValue const&>(*this);
 }
 
+TimeStyleValue const& StyleValue::as_time() const
+{
+    VERIFY(is_time());
+    return static_cast<TimeStyleValue const&>(*this);
+}
+
 TransformationStyleValue const& StyleValue::as_transformation() const
 {
     VERIFY(is_transformation());
@@ -381,6 +387,24 @@ void CalculatedStyleValue::CalculationResult::add_or_subtract_internal(SumOperat
                     m_value = Length::make_px(this_px - other_px);
             }
         },
+        [&](Time const& time) {
+            auto this_seconds = time.to_seconds();
+            if (other.m_value.has<Time>()) {
+                auto other_seconds = other.m_value.get<Time>().to_seconds();
+                if (op == SumOperation::Add)
+                    m_value = Time::make_seconds(this_seconds + other_seconds);
+                else
+                    m_value = Time::make_seconds(this_seconds - other_seconds);
+            } else {
+                VERIFY(percentage_basis.has<Time>());
+
+                auto other_seconds = percentage_basis.get<Time>().percentage_of(other.m_value.get<Percentage>()).to_seconds();
+                if (op == SumOperation::Add)
+                    m_value = Time::make_seconds(this_seconds + other_seconds);
+                else
+                    m_value = Time::make_seconds(this_seconds - other_seconds);
+            }
+        },
         [&](Percentage const& percentage) {
             if (other.m_value.has<Percentage>()) {
                 if (op == SumOperation::Add)
@@ -433,6 +457,9 @@ void CalculatedStyleValue::CalculationResult::multiply_by(CalculationResult cons
             VERIFY(layout_node);
             m_value = Length::make_px(length.to_px(*layout_node) * other.m_value.get<Number>().value);
         },
+        [&](Time const& time) {
+            m_value = Time::make_seconds(time.to_seconds() * other.m_value.get<Number>().value);
+        },
         [&](Percentage const& percentage) {
             m_value = Percentage { percentage.value() * other.m_value.get<Number>().value };
         });
@@ -463,6 +490,9 @@ void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const&
             VERIFY(layout_node);
             m_value = Length::make_px(length.to_px(*layout_node) / denominator);
         },
+        [&](Time const& time) {
+            m_value = Time::make_seconds(time.to_seconds() / denominator);
+        },
         [&](Percentage const& percentage) {
             m_value = Percentage { percentage.value() / denominator };
         });
@@ -630,6 +660,31 @@ Optional<Percentage> CalculatedStyleValue::resolve_percentage() const
     return {};
 }
 
+Optional<Time> CalculatedStyleValue::resolve_time() const
+{
+    auto result = m_expression->resolve(nullptr, {});
+
+    if (result.value().has<Time>())
+        return result.value().get<Time>();
+    return {};
+}
+
+Optional<TimePercentage> CalculatedStyleValue::resolve_time_percentage(Time const& percentage_basis) const
+{
+    auto result = m_expression->resolve(nullptr, percentage_basis);
+
+    return result.value().visit(
+        [&](Time const& time) -> Optional<TimePercentage> {
+            return time;
+        },
+        [&](Percentage const& percentage) -> Optional<TimePercentage> {
+            return percentage;
+        },
+        [&](auto const&) -> Optional<TimePercentage> {
+            return {};
+        });
+}
+
 Optional<float> CalculatedStyleValue::resolve_number()
 {
     auto result = m_expression->resolve(nullptr, {});
@@ -809,6 +864,7 @@ Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcValue::re
         [](Frequency const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Frequency }; },
         [](Length const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Length }; },
         [](Percentage const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Percentage }; },
+        [](Time const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Time }; },
         [](NonnullOwnPtr<CalcSum> const& sum) { return sum->resolved_type(); });
 }
 

+ 42 - 5
Userland/Libraries/LibWeb/CSS/StyleValue.h

@@ -30,6 +30,7 @@
 #include <LibWeb/CSS/Percentage.h>
 #include <LibWeb/CSS/PropertyID.h>
 #include <LibWeb/CSS/Resolution.h>
+#include <LibWeb/CSS/Time.h>
 #include <LibWeb/CSS/ValueID.h>
 #include <LibWeb/Forward.h>
 #include <LibWeb/Loader/ImageResource.h>
@@ -314,6 +315,7 @@ public:
         Resolution,
         String,
         TextDecoration,
+        Time,
         Transformation,
         Unresolved,
         Unset,
@@ -348,6 +350,7 @@ public:
     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; }
@@ -381,6 +384,7 @@ public:
     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;
@@ -412,6 +416,7 @@ public:
     ResolutionStyleValue& as_resolution() { return const_cast<ResolutionStyleValue&>(const_cast<StyleValue const&>(*this).as_resolution()); }
     StringStyleValue& as_string() { return const_cast<StringStyleValue&>(const_cast<StyleValue const&>(*this).as_string()); }
     TextDecorationStyleValue& as_text_decoration() { return const_cast<TextDecorationStyleValue&>(const_cast<StyleValue const&>(*this).as_text_decoration()); }
+    TimeStyleValue& as_time() { return const_cast<TimeStyleValue&>(const_cast<StyleValue const&>(*this).as_time()); }
     TransformationStyleValue& as_transformation() { return const_cast<TransformationStyleValue&>(const_cast<StyleValue const&>(*this).as_transformation()); }
     UnresolvedStyleValue& as_unresolved() { return const_cast<UnresolvedStyleValue&>(const_cast<StyleValue const&>(*this).as_unresolved()); }
     UnsetStyleValue& as_unset() { return const_cast<UnsetStyleValue&>(const_cast<StyleValue const&>(*this).as_unset()); }
@@ -740,11 +745,12 @@ public:
         float value;
     };
 
-    using PercentageBasis = Variant<Empty, Angle, Frequency, Length>;
+    using PercentageBasis = Variant<Empty, Angle, Frequency, Length, Time>;
 
     class CalculationResult {
     public:
-        CalculationResult(Variant<Number, Angle, Frequency, Length, Percentage> value)
+        using Value = Variant<Number, Angle, Frequency, Length, Percentage, Time>;
+        CalculationResult(Value value)
             : m_value(move(value))
         {
         }
@@ -753,11 +759,11 @@ public:
         void multiply_by(CalculationResult const& other, Layout::Node const*);
         void divide_by(CalculationResult const& other, Layout::Node const*);
 
-        Variant<Number, Angle, Frequency, Length, Percentage> const& value() const { return m_value; }
+        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);
-        Variant<Number, Angle, Frequency, Length, Percentage> m_value;
+        Value m_value;
     };
 
     struct CalcSum;
@@ -777,7 +783,7 @@ public:
     };
 
     struct CalcValue {
-        Variant<Number, Angle, Frequency, Length, Percentage, NonnullOwnPtr<CalcSum>> value;
+        Variant<Number, Angle, Frequency, Length, Percentage, Time, NonnullOwnPtr<CalcSum>> value;
         String to_string() const;
         Optional<ResolvedType> resolved_type() const;
         CalculationResult resolve(Layout::Node const*, PercentageBasis const& percentage_basis) const;
@@ -888,6 +894,8 @@ public:
     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;
+    Optional<Time> resolve_time() const;
+    Optional<TimePercentage> resolve_time_percentage(Time const& percentage_basis) const;
     Optional<float> resolve_number();
     Optional<i64> resolve_integer();
 
@@ -1455,6 +1463,35 @@ private:
     NonnullRefPtr<StyleValue> m_color;
 };
 
+class TimeStyleValue : public StyleValue {
+public:
+    static NonnullRefPtr<TimeStyleValue> create(Time time)
+    {
+        return adopt_ref(*new TimeStyleValue(move(time)));
+    }
+    virtual ~TimeStyleValue() override { }
+
+    Time const& time() const { return m_time; }
+
+    virtual String to_string() const override { return m_time.to_string(); }
+
+    virtual bool equals(StyleValue const& other) const override
+    {
+        if (type() != other.type())
+            return false;
+        return m_time == static_cast<TimeStyleValue const&>(other).m_time;
+    }
+
+private:
+    explicit TimeStyleValue(Time time)
+        : StyleValue(Type::Time)
+        , m_time(move(time))
+    {
+    }
+
+    Time m_time;
+};
+
 class TransformationStyleValue final : public StyleValue {
 public:
     static NonnullRefPtr<TransformationStyleValue> create(CSS::TransformFunction transform_function, NonnullRefPtrVector<StyleValue>&& values)

+ 86 - 0
Userland/Libraries/LibWeb/CSS/Time.cpp

@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "Time.h"
+#include <LibWeb/CSS/StyleValue.h>
+
+namespace Web::CSS {
+
+Time::Time(int value, Type type)
+    : m_type(type)
+    , m_value(value)
+{
+}
+
+Time::Time(float value, Type type)
+    : m_type(type)
+    , m_value(value)
+{
+}
+
+Time Time::make_calculated(NonnullRefPtr<CalculatedStyleValue> calculated_style_value)
+{
+    Time frequency { 0, Type::Calculated };
+    frequency.m_calculated_style = move(calculated_style_value);
+    return frequency;
+}
+
+Time Time::make_seconds(float value)
+{
+    return { value, Type::S };
+}
+
+Time Time::percentage_of(Percentage const& percentage) const
+{
+    VERIFY(!is_calculated());
+
+    return Time { percentage.as_fraction() * m_value, m_type };
+}
+
+String Time::to_string() const
+{
+    if (is_calculated())
+        return m_calculated_style->to_string();
+    return String::formatted("{}{}", m_value, unit_name());
+}
+
+float Time::to_seconds() const
+{
+    switch (m_type) {
+    case Type::Calculated:
+        return m_calculated_style->resolve_time()->to_seconds();
+    case Type::S:
+        return m_value;
+    case Type::Ms:
+        return m_value / 1000.0f;
+    }
+    VERIFY_NOT_REACHED();
+}
+
+StringView Time::unit_name() const
+{
+    switch (m_type) {
+    case Type::Calculated:
+        return "calculated"sv;
+    case Type::S:
+        return "s"sv;
+    case Type::Ms:
+        return "ms"sv;
+    }
+    VERIFY_NOT_REACHED();
+}
+
+Optional<Time::Type> Time::unit_from_name(StringView name)
+{
+    if (name.equals_ignoring_case("s"sv)) {
+        return Type::S;
+    } else if (name.equals_ignoring_case("ms"sv)) {
+        return Type::Ms;
+    }
+    return {};
+}
+
+}

+ 55 - 0
Userland/Libraries/LibWeb/CSS/Time.h

@@ -0,0 +1,55 @@
+/*
+ * 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 Time {
+public:
+    enum class Type {
+        Calculated,
+        S,
+        Ms,
+    };
+
+    static Optional<Type> unit_from_name(StringView);
+
+    Time(int value, Type type);
+    Time(float value, Type type);
+    static Time make_calculated(NonnullRefPtr<CalculatedStyleValue>);
+    static Time make_seconds(float);
+    Time percentage_of(Percentage const&) const;
+
+    bool is_calculated() const { return m_type == Type::Calculated; }
+
+    String to_string() const;
+    float to_seconds() const;
+
+    bool operator==(Time 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!=(Time const& other) const
+    {
+        return !(*this == other);
+    }
+
+private:
+    StringView unit_name() const;
+
+    Type m_type;
+    float m_value { 0 };
+    RefPtr<CalculatedStyleValue> m_calculated_style;
+};
+
+}

+ 3 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -76,6 +76,9 @@ class StyleValue;
 class StyleValueList;
 class Supports;
 class TextDecorationStyleValue;
+class Time;
+class TimePercentage;
+class TimeStyleValue;
 class TransformationStyleValue;
 class UnresolvedStyleValue;
 class UnsetStyleValue;