Skip to main content

style_traits/
values.rs

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//! Helper types and traits for the handling of CSS values.
6
7use app_units::Au;
8use cssparser::ToCss as CssparserToCss;
9use cssparser::{serialize_string, ParseError, Parser, Token, UnicodeRange};
10use servo_arc::Arc;
11use std::fmt::{self, Write};
12use thin_vec::ThinVec;
13
14/// Serialises a value according to its CSS representation.
15///
16/// This trait is implemented for `str` and its friends, serialising the string
17/// contents as a CSS quoted string.
18///
19/// This trait is derivable with `#[derive(ToCss)]`, with the following behaviour:
20/// * unit variants get serialised as the `snake-case` representation
21///   of their name;
22/// * unit variants whose name starts with "Moz" or "Webkit" are prepended
23///   with a "-";
24/// * if `#[css(comma)]` is found on a variant, its fields are separated by
25///   commas, otherwise, by spaces;
26/// * if `#[css(function)]` is found on a variant, the variant name gets
27///   serialised like unit variants and its fields are surrounded by parentheses;
28/// * if `#[css(iterable)]` is found on a function variant, that variant needs
29///   to have a single member, and that member needs to be iterable. The
30///   iterable will be serialized as the arguments for the function;
31/// * an iterable field can also be annotated with `#[css(if_empty = "foo")]`
32///   to print `"foo"` if the iterator is empty;
33/// * if `#[css(dimension)]` is found on a variant, that variant needs
34///   to have a single member. The variant would be serialized as a CSS
35///   dimension token, like: <member><identifier>;
36/// * if `#[css(skip)]` is found on a field, the `ToCss` call for that field
37///   is skipped;
38/// * if `#[css(skip_if = "function")]` is found on a field, the `ToCss` call
39///   for that field is skipped if `function` returns true. This function is
40///   provided the field as an argument;
41/// * if `#[css(contextual_skip_if = "function")]` is found on a field, the
42///   `ToCss` call for that field is skipped if `function` returns true. This
43///   function is given all the fields in the current struct or variant as an
44///   argument;
45/// * `#[css(represents_keyword)]` can be used on bool fields in order to
46///   serialize the field name if the field is true, or nothing otherwise.  It
47///   also collects those keywords for `SpecifiedValueInfo`.
48/// * `#[css(bitflags(single="", mixed="", validate_mixed="", overlapping_bits)]` can
49///   be used to derive parse / serialize / etc on bitflags. The rules for parsing
50///   bitflags are the following:
51///
52///     * `single` flags can only appear on their own. It's common that bitflags
53///       properties at least have one such value like `none` or `auto`.
54///     * `mixed` properties can appear mixed together, but not along any other
55///       flag that shares a bit with itself. For example, if you have three
56///       bitflags like:
57///
58///         FOO = 1 << 0;
59///         BAR = 1 << 1;
60///         BAZ = 1 << 2;
61///         BAZZ = BAR | BAZ;
62///
63///       Then the following combinations won't be valid:
64///
65///         * foo foo: (every flag shares a bit with itself)
66///         * bar bazz: (bazz shares a bit with bar)
67///
68///       But `bar baz` will be valid, as they don't share bits, and so would
69///       `foo` with any other flag, or `bazz` on its own.
70///    * `validate_mixed` can be used to reject invalid mixed combinations, and also to simplify
71///      the type or add default ones if needed.
72///    * `overlapping_bits` enables some tracking during serialization of mixed flags to avoid
73///       serializing variants that can subsume other variants.
74///       In the example above, you could do:
75///         mixed="foo,bazz,bar,baz", overlapping_bits
76///       to ensure that if bazz is serialized, bar and baz aren't, even though
77///       their bits are set. Note that the serialization order is canonical,
78///       and thus depends on the order you specify the flags in.
79///
80/// * finally, one can put `#[css(derive_debug)]` on the whole type, to
81///   implement `Debug` by a single call to `ToCss::to_css`.
82pub trait ToCss {
83    /// Serialize `self` in CSS syntax, writing to `dest`.
84    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
85    where
86        W: Write;
87
88    /// Serialize `self` in CSS syntax and return a string.
89    ///
90    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
91    #[inline]
92    fn to_css_string(&self) -> String {
93        let mut s = String::new();
94        self.to_css(&mut CssWriter::new(&mut s)).unwrap();
95        s
96    }
97
98    /// Serialize `self` in CSS syntax and return a CssString.
99    ///
100    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
101    #[inline]
102    fn to_css_cssstring(&self) -> CssString {
103        let mut s = CssString::new();
104        self.to_css(&mut CssWriter::new(&mut s)).unwrap();
105        s
106    }
107}
108
109impl<'a, T> ToCss for &'a T
110where
111    T: ToCss + ?Sized,
112{
113    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
114    where
115        W: Write,
116    {
117        (*self).to_css(dest)
118    }
119}
120
121impl ToCss for crate::owned_str::OwnedStr {
122    #[inline]
123    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
124    where
125        W: Write,
126    {
127        serialize_string(self, dest)
128    }
129}
130
131impl ToCss for str {
132    #[inline]
133    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
134    where
135        W: Write,
136    {
137        serialize_string(self, dest)
138    }
139}
140
141impl ToCss for String {
142    #[inline]
143    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
144    where
145        W: Write,
146    {
147        serialize_string(self, dest)
148    }
149}
150
151impl<T> ToCss for Option<T>
152where
153    T: ToCss,
154{
155    #[inline]
156    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
157    where
158        W: Write,
159    {
160        self.as_ref().map_or(Ok(()), |value| value.to_css(dest))
161    }
162}
163
164impl ToCss for () {
165    #[inline]
166    fn to_css<W>(&self, _: &mut CssWriter<W>) -> fmt::Result
167    where
168        W: Write,
169    {
170        Ok(())
171    }
172}
173
174/// A writer tailored for serialising CSS.
175///
176/// Coupled with SequenceWriter, this allows callers to transparently handle
177/// things like comma-separated values etc.
178pub struct CssWriter<'w, W: 'w> {
179    inner: &'w mut W,
180    prefix: Option<&'static str>,
181}
182
183impl<'w, W> CssWriter<'w, W>
184where
185    W: Write,
186{
187    /// Creates a new `CssWriter`.
188    #[inline]
189    pub fn new(inner: &'w mut W) -> Self {
190        Self {
191            inner,
192            prefix: Some(""),
193        }
194    }
195}
196
197impl<'w, W> Write for CssWriter<'w, W>
198where
199    W: Write,
200{
201    #[inline]
202    fn write_str(&mut self, s: &str) -> fmt::Result {
203        if s.is_empty() {
204            return Ok(());
205        }
206        if let Some(prefix) = self.prefix.take() {
207            // We are going to write things, but first we need to write
208            // the prefix that was set by `SequenceWriter::item`.
209            if !prefix.is_empty() {
210                self.inner.write_str(prefix)?;
211            }
212        }
213        self.inner.write_str(s)
214    }
215
216    #[inline]
217    fn write_char(&mut self, c: char) -> fmt::Result {
218        if let Some(prefix) = self.prefix.take() {
219            // See comment in `write_str`.
220            if !prefix.is_empty() {
221                self.inner.write_str(prefix)?;
222            }
223        }
224        self.inner.write_char(c)
225    }
226}
227
228/// To avoid accidentally instantiating multiple monomorphizations of large
229/// serialization routines, we define explicit concrete types and require
230/// them in those routines. This avoids accidental mixing of String and
231/// nsACString arguments in Gecko, which would cause code size to blow up.
232#[cfg(feature = "gecko")]
233pub type CssStringWriter = ::nsstring::nsACString;
234
235/// String type that coerces to CssStringWriter, used when serialization code
236/// needs to allocate a temporary string. In Gecko, this is backed by
237/// nsCString, which allows the result to be passed directly to C++ without
238/// conversion or copying. This makes it suitable not only for temporary
239/// serialization but also for values that need to cross the Rust/C++ boundary.
240#[cfg(feature = "gecko")]
241pub type CssString = ::nsstring::nsCString;
242
243/// String. The comments for the Gecko types explain the need for this abstraction.
244#[cfg(feature = "servo")]
245pub type CssStringWriter = String;
246
247/// String. The comments for the Gecko types explain the need for this abstraction.
248#[cfg(feature = "servo")]
249pub type CssString = String;
250
251/// Convenience wrapper to serialise CSS values separated by a given string.
252pub struct SequenceWriter<'a, 'b: 'a, W: 'b> {
253    inner: &'a mut CssWriter<'b, W>,
254    separator: &'static str,
255}
256
257impl<'a, 'b, W> SequenceWriter<'a, 'b, W>
258where
259    W: Write + 'b,
260{
261    /// Returns whether this writer has written any item.
262    pub fn has_written(&self) -> bool {
263        // See comment in item()
264        self.inner.prefix.is_none()
265    }
266
267    /// Create a new sequence writer.
268    #[inline]
269    pub fn new(inner: &'a mut CssWriter<'b, W>, separator: &'static str) -> Self {
270        if inner.prefix.is_none() {
271            // See comment in `item`.
272            inner.prefix = Some("");
273        }
274        Self { inner, separator }
275    }
276
277    /// Serialize the CSS Value with the specific serialization function.
278    #[inline]
279    pub fn write_item<F>(&mut self, f: F) -> fmt::Result
280    where
281        F: FnOnce(&mut CssWriter<'b, W>) -> fmt::Result,
282    {
283        // Separate non-generic functions so that this code is not repeated
284        // in every monomorphization with a different type `F` or `W`.
285        // https://github.com/servo/servo/issues/26713
286        fn before(
287            prefix: &mut Option<&'static str>,
288            separator: &'static str,
289        ) -> Option<&'static str> {
290            let old_prefix = *prefix;
291            if old_prefix.is_none() {
292                // If there is no prefix in the inner writer, a previous
293                // call to this method produced output, which means we need
294                // to write the separator next time we produce output again.
295                *prefix = Some(separator);
296            }
297            old_prefix
298        }
299        fn after(
300            old_prefix: Option<&'static str>,
301            prefix: &mut Option<&'static str>,
302            separator: &'static str,
303        ) {
304            match (old_prefix, *prefix) {
305                (_, None) => {
306                    // This call produced output and cleaned up after itself.
307                },
308                (None, Some(p)) => {
309                    // Some previous call to `item` produced output,
310                    // but this one did not, prefix should be the same as
311                    // the one we set.
312                    debug_assert_eq!(separator, p);
313                    // We clean up here even though it's not necessary just
314                    // to be able to do all these assertion checks.
315                    *prefix = None;
316                },
317                (Some(old), Some(new)) => {
318                    // No previous call to `item` produced output, and this one
319                    // either.
320                    debug_assert_eq!(old, new);
321                },
322            }
323        }
324
325        let old_prefix = before(&mut self.inner.prefix, self.separator);
326        f(self.inner)?;
327        after(old_prefix, &mut self.inner.prefix, self.separator);
328        Ok(())
329    }
330
331    /// Serialises a CSS value, writing any separator as necessary.
332    ///
333    /// The separator is never written before any `item` produces any output,
334    /// and is written in subsequent calls only if the `item` produces some
335    /// output on its own again. This lets us handle `Option<T>` fields by
336    /// just not printing anything on `None`.
337    #[inline]
338    pub fn item<T>(&mut self, item: &T) -> fmt::Result
339    where
340        T: ToCss,
341    {
342        self.write_item(|inner| item.to_css(inner))
343    }
344
345    /// Writes a string as-is (i.e. not escaped or wrapped in quotes)
346    /// with any separator as necessary.
347    ///
348    /// See SequenceWriter::item.
349    #[inline]
350    pub fn raw_item(&mut self, item: &str) -> fmt::Result {
351        self.write_item(|inner| inner.write_str(item))
352    }
353}
354
355/// Type used as the associated type in the `OneOrMoreSeparated` trait on a
356/// type to indicate that a serialized list of elements of this type is
357/// separated by commas.
358pub struct Comma;
359
360/// Type used as the associated type in the `OneOrMoreSeparated` trait on a
361/// type to indicate that a serialized list of elements of this type is
362/// separated by spaces.
363pub struct Space;
364
365/// Type used as the associated type in the `OneOrMoreSeparated` trait on a
366/// type to indicate that a serialized list of elements of this type is
367/// separated by commas, but spaces without commas are also allowed when
368/// parsing.
369pub struct CommaWithSpace;
370
371/// A trait satisfied by the types corresponding to separators.
372pub trait Separator {
373    /// The separator string that the satisfying separator type corresponds to.
374    fn separator() -> &'static str;
375
376    /// Parses a sequence of values separated by this separator.
377    ///
378    /// The given closure is called repeatedly for each item in the sequence.
379    ///
380    /// Successful results are accumulated in a vector.
381    ///
382    /// This method returns `Err(_)` the first time a closure does or if
383    /// the separators aren't correct.
384    fn parse<'i, 't, F, T, E>(
385        parser: &mut Parser<'i, 't>,
386        parse_one: F,
387    ) -> Result<Vec<T>, ParseError<'i, E>>
388    where
389        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>;
390}
391
392impl Separator for Comma {
393    fn separator() -> &'static str {
394        ", "
395    }
396
397    fn parse<'i, 't, F, T, E>(
398        input: &mut Parser<'i, 't>,
399        parse_one: F,
400    ) -> Result<Vec<T>, ParseError<'i, E>>
401    where
402        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
403    {
404        input.parse_comma_separated(parse_one)
405    }
406}
407
408impl Separator for Space {
409    fn separator() -> &'static str {
410        " "
411    }
412
413    fn parse<'i, 't, F, T, E>(
414        input: &mut Parser<'i, 't>,
415        mut parse_one: F,
416    ) -> Result<Vec<T>, ParseError<'i, E>>
417    where
418        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
419    {
420        input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.
421        let mut results = vec![parse_one(input)?];
422        loop {
423            input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.
424            if let Ok(item) = input.try_parse(&mut parse_one) {
425                results.push(item);
426            } else {
427                return Ok(results);
428            }
429        }
430    }
431}
432
433impl Separator for CommaWithSpace {
434    fn separator() -> &'static str {
435        ", "
436    }
437
438    fn parse<'i, 't, F, T, E>(
439        input: &mut Parser<'i, 't>,
440        mut parse_one: F,
441    ) -> Result<Vec<T>, ParseError<'i, E>>
442    where
443        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
444    {
445        input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.
446        let mut results = vec![parse_one(input)?];
447        loop {
448            input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.
449            let comma_location = input.current_source_location();
450            let comma = input.try_parse(|i| i.expect_comma()).is_ok();
451            input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.
452            if let Ok(item) = input.try_parse(&mut parse_one) {
453                results.push(item);
454            } else if comma {
455                return Err(comma_location.new_unexpected_token_error(Token::Comma));
456            } else {
457                break;
458            }
459        }
460        Ok(results)
461    }
462}
463
464/// Marker trait on T to automatically implement ToCss for Vec<T> when T's are
465/// separated by some delimiter `delim`.
466pub trait OneOrMoreSeparated {
467    /// Associated type indicating which separator is used.
468    type S: Separator;
469}
470
471impl OneOrMoreSeparated for UnicodeRange {
472    type S = Comma;
473}
474
475impl<T> ToCss for Vec<T>
476where
477    T: ToCss + OneOrMoreSeparated,
478{
479    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
480    where
481        W: Write,
482    {
483        let mut iter = self.iter();
484        iter.next().unwrap().to_css(dest)?;
485        for item in iter {
486            dest.write_str(<T as OneOrMoreSeparated>::S::separator())?;
487            item.to_css(dest)?;
488        }
489        Ok(())
490    }
491}
492
493impl<T> ToCss for Box<T>
494where
495    T: ?Sized + ToCss,
496{
497    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
498    where
499        W: Write,
500    {
501        (**self).to_css(dest)
502    }
503}
504
505impl<T> ToCss for Arc<T>
506where
507    T: ?Sized + ToCss,
508{
509    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
510    where
511        W: Write,
512    {
513        (**self).to_css(dest)
514    }
515}
516
517impl ToCss for Au {
518    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
519    where
520        W: Write,
521    {
522        self.to_f64_px().to_css(dest)?;
523        dest.write_str("px")
524    }
525}
526
527macro_rules! impl_to_css_for_predefined_type {
528    ($name: ty) => {
529        impl<'a> ToCss for $name {
530            fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
531            where
532                W: Write,
533            {
534                ::cssparser::ToCss::to_css(self, dest)
535            }
536        }
537    };
538}
539
540impl_to_css_for_predefined_type!(f32);
541impl_to_css_for_predefined_type!(i8);
542impl_to_css_for_predefined_type!(i32);
543impl_to_css_for_predefined_type!(u8);
544impl_to_css_for_predefined_type!(u16);
545impl_to_css_for_predefined_type!(u32);
546impl_to_css_for_predefined_type!(::cssparser::Token<'a>);
547impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);
548
549/// Helper types for the handling of specified values.
550pub mod specified {
551    use crate::ParsingMode;
552
553    /// Whether to allow negative lengths or not.
554    #[repr(u8)]
555    #[derive(
556        Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, PartialOrd, Serialize, ToShmem,
557    )]
558    pub enum AllowedNumericType {
559        /// Allow all kind of numeric values.
560        All,
561        /// Allow only non-negative numeric values.
562        NonNegative,
563        /// Allow only numeric values greater or equal to 1.0.
564        AtLeastOne,
565        /// Allow only numeric values from 0 to 1.0.
566        ZeroToOne,
567    }
568
569    impl Default for AllowedNumericType {
570        #[inline]
571        fn default() -> Self {
572            AllowedNumericType::All
573        }
574    }
575
576    impl AllowedNumericType {
577        /// Whether the value fits the rules of this numeric type.
578        #[inline]
579        pub fn is_ok(&self, parsing_mode: ParsingMode, val: f32) -> bool {
580            if parsing_mode.allows_all_numeric_values() {
581                return true;
582            }
583            match *self {
584                AllowedNumericType::All => true,
585                AllowedNumericType::NonNegative => val >= 0.0,
586                AllowedNumericType::AtLeastOne => val >= 1.0,
587                AllowedNumericType::ZeroToOne => val >= 0.0 && val <= 1.0,
588            }
589        }
590
591        /// Clamp the value following the rules of this numeric type.
592        #[inline]
593        pub fn clamp(&self, val: f32) -> f32 {
594            match *self {
595                AllowedNumericType::All => val,
596                AllowedNumericType::NonNegative => val.max(0.),
597                AllowedNumericType::AtLeastOne => val.max(1.),
598                AllowedNumericType::ZeroToOne => val.max(0.).min(1.),
599            }
600        }
601    }
602}
603
604/// A numeric value used by the Typed OM.
605///
606/// This corresponds to `CSSNumericValue` and its subclasses in the Typed OM
607/// specification. It represents numbers that can appear in CSS values,
608/// including both simple unit quantities and sums of numeric terms.
609///
610/// Unlike the parser-level representation, `NumericValue` is property-agnostic
611/// and suitable for conversion to or from the `CSSNumericValue` family of DOM
612/// objects.
613#[derive(Clone, Debug)]
614#[repr(C)]
615pub enum NumericValue {
616    /// A single numeric value with a concrete unit.
617    ///
618    /// This corresponds to `CSSUnitValue`. The `value` field stores the raw
619    /// numeric component, and the `unit` field stores the textual unit
620    /// identifier (e.g. `"px"`, `"em"`, `"%"`, `"deg"`).
621    Unit {
622        /// The numeric component of the value.
623        value: f32,
624        /// The textual unit string (e.g. `"px"`, `"em"`, `"deg"`).
625        unit: CssString,
626    },
627
628    /// A sum of multiple numeric values.
629    ///
630    /// This corresponds to `CSSMathSum`, representing an expression such as
631    /// `10px + 2em`. Each entry in `values` is another `NumericValue`, which
632    /// may itself be a unit value or a nested sum.
633    Sum {
634        /// The list of numeric terms that make up the sum.
635        values: ThinVec<NumericValue>,
636    },
637}
638
639/// A property-agnostic representation of a value, used by Typed OM.
640///
641/// `TypedValue` is the internal counterpart of the various `CSSStyleValue`
642/// subclasses defined by the Typed OM specification. It captures values that
643/// can be represented independently of any particular property.
644#[derive(Clone, Debug)]
645#[repr(C)]
646pub enum TypedValue {
647    /// A keyword value (e.g. `"block"`, `"none"`, `"thin"`).
648    ///
649    /// Keywords are stored as a `CssString` so they can be represented and
650    /// transferred independently of any specific property. This corresponds
651    /// to `CSSKeywordValue` in the Typed OM specification.
652    Keyword(CssString),
653
654    /// A numeric value such as a length, angle, time, or a sum thereof.
655    ///
656    /// This corresponds to the `CSSNumericValue` hierarchy in the Typed OM
657    /// specification, including `CSSUnitValue` and `CSSMathSum`.
658    Numeric(NumericValue),
659}
660
661/// Reifies a value into its Typed OM representation.
662///
663/// This trait is the Typed OM analogue of [`ToCss`]. Instead of serializing
664/// values into CSS syntax, it converts them into [`TypedValue`]s that can be
665/// exposed to the DOM as `CSSStyleValue` subclasses.
666///
667/// This trait is derivable with `#[derive(ToTyped)]`. The derived
668/// implementation currently supports:
669///
670/// * Keyword enums: Enums whose variants are all unit variants are
671///   automatically reified as [`TypedValue::Keyword`], using the same
672///   serialization logic as [`ToCss`].
673///
674/// * Structs and data-carrying variants: When the
675///   `#[typed_value(derive_fields)]` attribute is present, the derive attempts
676///   to call `.to_typed()` recursively on inner fields or variant payloads,
677///   producing a nested [`TypedValue`] representation when possible.
678///
679/// * Other cases: If no automatic mapping is defined or recursion is not
680///   enabled, the derived implementation falls back to the default method,
681///   returning `None`.
682///
683/// The `derive_fields` attribute is intentionally opt-in for now to avoid
684/// forcing types that do not participate in reification to implement
685/// [`ToTyped`]. Once Typed OM coverage stabilizes, this behavior is expected
686/// to become the default (see the corresponding follow-up bug).
687///
688/// Over time, the derive may be extended to handle additional CSS value
689/// categories such as numeric, color, and transform types.
690pub trait ToTyped {
691    /// Attempt to convert `self` into a [`TypedValue`].
692    ///
693    /// Returns `Some(TypedValue)` if the value can be reified into a
694    /// property-agnostic CSSStyleValue subclass. Returns `None` if the value
695    /// is unrepresentable, in which case reification produces a property-tied
696    /// CSSStyleValue instead.
697    fn to_typed(&self) -> Option<TypedValue> {
698        None
699    }
700}
701
702impl<T> ToTyped for Box<T>
703where
704    T: ?Sized + ToTyped,
705{
706    fn to_typed(&self) -> Option<TypedValue> {
707        (**self).to_typed()
708    }
709}
710
711impl ToTyped for Au {
712    fn to_typed(&self) -> Option<TypedValue> {
713        let value = self.to_f32_px();
714        let unit = CssString::from("px");
715        Some(TypedValue::Numeric(NumericValue::Unit { value, unit }))
716    }
717}
718
719macro_rules! impl_to_typed_for_predefined_type {
720    ($name: ty) => {
721        impl<'a> ToTyped for $name {
722            fn to_typed(&self) -> Option<TypedValue> {
723                // XXX Should return TypedValue::Numeric with unit "number"
724                // once that variant is available. Tracked in bug 1990419.
725                None
726            }
727        }
728    };
729}
730
731impl_to_typed_for_predefined_type!(f32);
732impl_to_typed_for_predefined_type!(i8);
733impl_to_typed_for_predefined_type!(i32);