1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 16:07:35 +00:00

LibWeb: Implement CSS Time class

This corresponds to `<time>` in the grammar.
This commit is contained in:
Sam Atkins 2022-02-21 19:29:43 +00:00 committed by Andreas Kling
parent 0465abcfec
commit 608bfac2a9
9 changed files with 268 additions and 5 deletions

View file

@ -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

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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());
}
};

View file

@ -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(); });
}

View file

@ -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)

View file

@ -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 {};
}
}

View file

@ -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;
};
}

View file

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