time.rs (5713B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! Specified time values. 6 7 use crate::derives::*; 8 use crate::parser::{Parse, ParserContext}; 9 use crate::values::computed::time::Time as ComputedTime; 10 use crate::values::computed::{Context, ToComputedValue}; 11 use crate::values::specified::calc::CalcNode; 12 use crate::values::CSSFloat; 13 use crate::Zero; 14 use cssparser::{match_ignore_ascii_case, Parser, Token}; 15 use std::fmt::{self, Write}; 16 use style_traits::values::specified::AllowedNumericType; 17 use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; 18 19 /// A time value according to CSS-VALUES § 6.2. 20 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)] 21 pub struct Time { 22 seconds: CSSFloat, 23 unit: TimeUnit, 24 calc_clamping_mode: Option<AllowedNumericType>, 25 } 26 27 /// A time unit. 28 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)] 29 pub enum TimeUnit { 30 /// `s` 31 Second, 32 /// `ms` 33 Millisecond, 34 } 35 36 impl Time { 37 /// Returns a time value that represents `seconds` seconds. 38 pub fn from_seconds_with_calc_clamping_mode( 39 seconds: CSSFloat, 40 calc_clamping_mode: Option<AllowedNumericType>, 41 ) -> Self { 42 Time { 43 seconds, 44 unit: TimeUnit::Second, 45 calc_clamping_mode, 46 } 47 } 48 49 /// Returns a time value that represents `seconds` seconds. 50 pub fn from_seconds(seconds: CSSFloat) -> Self { 51 Self::from_seconds_with_calc_clamping_mode(seconds, None) 52 } 53 54 /// Returns the time in fractional seconds. 55 pub fn seconds(self) -> CSSFloat { 56 self.seconds 57 } 58 59 /// Returns the unit of the time. 60 #[inline] 61 pub fn unit(&self) -> &'static str { 62 match self.unit { 63 TimeUnit::Second => "s", 64 TimeUnit::Millisecond => "ms", 65 } 66 } 67 68 #[inline] 69 fn unitless_value(&self) -> CSSFloat { 70 match self.unit { 71 TimeUnit::Second => self.seconds, 72 TimeUnit::Millisecond => self.seconds * 1000., 73 } 74 } 75 76 /// Parses a time according to CSS-VALUES § 6.2. 77 pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> { 78 let (seconds, unit) = match_ignore_ascii_case! { unit, 79 "s" => (value, TimeUnit::Second), 80 "ms" => (value / 1000.0, TimeUnit::Millisecond), 81 _ => return Err(()) 82 }; 83 84 Ok(Time { 85 seconds, 86 unit, 87 calc_clamping_mode: None, 88 }) 89 } 90 91 fn parse_with_clamping_mode<'i, 't>( 92 context: &ParserContext, 93 input: &mut Parser<'i, 't>, 94 clamping_mode: AllowedNumericType, 95 ) -> Result<Self, ParseError<'i>> { 96 use style_traits::ParsingMode; 97 98 let location = input.current_source_location(); 99 match *input.next()? { 100 // Note that we generally pass ParserContext to is_ok() to check 101 // that the ParserMode of the ParserContext allows all numeric 102 // values for SMIL regardless of clamping_mode, but in this Time 103 // value case, the value does not animate for SMIL at all, so we use 104 // ParsingMode::DEFAULT directly. 105 Token::Dimension { 106 value, ref unit, .. 107 } if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => { 108 Time::parse_dimension(value, unit) 109 .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 110 }, 111 Token::Function(ref name) => { 112 let function = CalcNode::math_function(context, name, location)?; 113 CalcNode::parse_time(context, input, clamping_mode, function) 114 }, 115 ref t => return Err(location.new_unexpected_token_error(t.clone())), 116 } 117 } 118 119 /// Parses a non-negative time value. 120 pub fn parse_non_negative<'i, 't>( 121 context: &ParserContext, 122 input: &mut Parser<'i, 't>, 123 ) -> Result<Self, ParseError<'i>> { 124 Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative) 125 } 126 } 127 128 impl Zero for Time { 129 #[inline] 130 fn zero() -> Self { 131 Self::from_seconds(0.0) 132 } 133 134 #[inline] 135 fn is_zero(&self) -> bool { 136 // The unit doesn't matter, i.e. `s` and `ms` are the same for zero. 137 self.seconds == 0.0 && self.calc_clamping_mode.is_none() 138 } 139 } 140 141 impl ToComputedValue for Time { 142 type ComputedValue = ComputedTime; 143 144 fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue { 145 let seconds = self 146 .calc_clamping_mode 147 .map_or(self.seconds(), |mode| mode.clamp(self.seconds())); 148 149 ComputedTime::from_seconds(crate::values::normalize(seconds)) 150 } 151 152 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 153 Time { 154 seconds: computed.seconds(), 155 unit: TimeUnit::Second, 156 calc_clamping_mode: None, 157 } 158 } 159 } 160 161 impl Parse for Time { 162 fn parse<'i, 't>( 163 context: &ParserContext, 164 input: &mut Parser<'i, 't>, 165 ) -> Result<Self, ParseError<'i>> { 166 Self::parse_with_clamping_mode(context, input, AllowedNumericType::All) 167 } 168 } 169 170 impl ToCss for Time { 171 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 172 where 173 W: Write, 174 { 175 crate::values::serialize_specified_dimension( 176 self.unitless_value(), 177 self.unit(), 178 self.calc_clamping_mode.is_some(), 179 dest, 180 ) 181 } 182 } 183 184 impl SpecifiedValueInfo for Time {}