1use crate::parser::{Parse, ParserContext};
8use crate::values::computed::time::Time as ComputedTime;
9use crate::values::computed::{Context, ToComputedValue};
10use crate::values::specified::calc::CalcNode;
11use crate::values::CSSFloat;
12use crate::Zero;
13use cssparser::{Parser, Token};
14use std::fmt::{self, Write};
15use style_traits::values::specified::AllowedNumericType;
16use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
17
18#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
20pub struct Time {
21 seconds: CSSFloat,
22 unit: TimeUnit,
23 calc_clamping_mode: Option<AllowedNumericType>,
24}
25
26#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
28pub enum TimeUnit {
29 Second,
31 Millisecond,
33}
34
35impl Time {
36 pub fn from_seconds_with_calc_clamping_mode(
38 seconds: CSSFloat,
39 calc_clamping_mode: Option<AllowedNumericType>,
40 ) -> Self {
41 Time {
42 seconds,
43 unit: TimeUnit::Second,
44 calc_clamping_mode,
45 }
46 }
47
48 pub fn from_seconds(seconds: CSSFloat) -> Self {
50 Self::from_seconds_with_calc_clamping_mode(seconds, None)
51 }
52
53 pub fn seconds(self) -> CSSFloat {
55 self.seconds
56 }
57
58 #[inline]
60 pub fn unit(&self) -> &'static str {
61 match self.unit {
62 TimeUnit::Second => "s",
63 TimeUnit::Millisecond => "ms",
64 }
65 }
66
67 #[inline]
68 fn unitless_value(&self) -> CSSFloat {
69 match self.unit {
70 TimeUnit::Second => self.seconds,
71 TimeUnit::Millisecond => self.seconds * 1000.,
72 }
73 }
74
75 pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> {
77 let (seconds, unit) = match_ignore_ascii_case! { unit,
78 "s" => (value, TimeUnit::Second),
79 "ms" => (value / 1000.0, TimeUnit::Millisecond),
80 _ => return Err(())
81 };
82
83 Ok(Time {
84 seconds,
85 unit,
86 calc_clamping_mode: None,
87 })
88 }
89
90 fn parse_with_clamping_mode<'i, 't>(
91 context: &ParserContext,
92 input: &mut Parser<'i, 't>,
93 clamping_mode: AllowedNumericType,
94 ) -> Result<Self, ParseError<'i>> {
95 use style_traits::ParsingMode;
96
97 let location = input.current_source_location();
98 match *input.next()? {
99 Token::Dimension {
105 value, ref unit, ..
106 } if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => {
107 Time::parse_dimension(value, unit)
108 .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
109 },
110 Token::Function(ref name) => {
111 let function = CalcNode::math_function(context, name, location)?;
112 CalcNode::parse_time(context, input, clamping_mode, function)
113 },
114 ref t => return Err(location.new_unexpected_token_error(t.clone())),
115 }
116 }
117
118 pub fn parse_non_negative<'i, 't>(
120 context: &ParserContext,
121 input: &mut Parser<'i, 't>,
122 ) -> Result<Self, ParseError<'i>> {
123 Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
124 }
125}
126
127impl Zero for Time {
128 #[inline]
129 fn zero() -> Self {
130 Self::from_seconds(0.0)
131 }
132
133 #[inline]
134 fn is_zero(&self) -> bool {
135 self.seconds == 0.0 && self.calc_clamping_mode.is_none()
137 }
138}
139
140impl ToComputedValue for Time {
141 type ComputedValue = ComputedTime;
142
143 fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
144 let seconds = self
145 .calc_clamping_mode
146 .map_or(self.seconds(), |mode| mode.clamp(self.seconds()));
147
148 ComputedTime::from_seconds(crate::values::normalize(seconds))
149 }
150
151 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
152 Time {
153 seconds: computed.seconds(),
154 unit: TimeUnit::Second,
155 calc_clamping_mode: None,
156 }
157 }
158}
159
160impl Parse for Time {
161 fn parse<'i, 't>(
162 context: &ParserContext,
163 input: &mut Parser<'i, 't>,
164 ) -> Result<Self, ParseError<'i>> {
165 Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
166 }
167}
168
169impl ToCss for Time {
170 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
171 where
172 W: Write,
173 {
174 crate::values::serialize_specified_dimension(
175 self.unitless_value(),
176 self.unit(),
177 self.calc_clamping_mode.is_some(),
178 dest,
179 )
180 }
181}
182
183impl SpecifiedValueInfo for Time {}