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