values.rs (24637B)
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 7 use app_units::Au; 8 use cssparser::ToCss as CssparserToCss; 9 use cssparser::{serialize_string, ParseError, Parser, Token, UnicodeRange}; 10 use servo_arc::Arc; 11 use std::fmt::{self, Write}; 12 use 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`. 82 pub 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 109 impl<'a, T> ToCss for &'a T 110 where 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 121 impl 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 131 impl 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 141 impl 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 151 impl<T> ToCss for Option<T> 152 where 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 164 impl 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. 178 pub struct CssWriter<'w, W: 'w> { 179 inner: &'w mut W, 180 prefix: Option<&'static str>, 181 } 182 183 impl<'w, W> CssWriter<'w, W> 184 where 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 197 impl<'w, W> Write for CssWriter<'w, W> 198 where 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")] 233 pub 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")] 241 pub type CssString = ::nsstring::nsCString; 242 243 /// String. The comments for the Gecko types explain the need for this abstraction. 244 #[cfg(feature = "servo")] 245 pub type CssStringWriter = String; 246 247 /// String. The comments for the Gecko types explain the need for this abstraction. 248 #[cfg(feature = "servo")] 249 pub type CssString = String; 250 251 /// Convenience wrapper to serialise CSS values separated by a given string. 252 pub struct SequenceWriter<'a, 'b: 'a, W: 'b> { 253 inner: &'a mut CssWriter<'b, W>, 254 separator: &'static str, 255 } 256 257 impl<'a, 'b, W> SequenceWriter<'a, 'b, W> 258 where 259 W: Write + 'b, 260 { 261 /// Create a new sequence writer. 262 #[inline] 263 pub fn new(inner: &'a mut CssWriter<'b, W>, separator: &'static str) -> Self { 264 if inner.prefix.is_none() { 265 // See comment in `item`. 266 inner.prefix = Some(""); 267 } 268 Self { inner, separator } 269 } 270 271 /// Serialize the CSS Value with the specific serialization function. 272 #[inline] 273 pub fn write_item<F>(&mut self, f: F) -> fmt::Result 274 where 275 F: FnOnce(&mut CssWriter<'b, W>) -> fmt::Result, 276 { 277 // Separate non-generic functions so that this code is not repeated 278 // in every monomorphization with a different type `F` or `W`. 279 // https://github.com/servo/servo/issues/26713 280 fn before( 281 prefix: &mut Option<&'static str>, 282 separator: &'static str, 283 ) -> Option<&'static str> { 284 let old_prefix = *prefix; 285 if old_prefix.is_none() { 286 // If there is no prefix in the inner writer, a previous 287 // call to this method produced output, which means we need 288 // to write the separator next time we produce output again. 289 *prefix = Some(separator); 290 } 291 old_prefix 292 } 293 fn after( 294 old_prefix: Option<&'static str>, 295 prefix: &mut Option<&'static str>, 296 separator: &'static str, 297 ) { 298 match (old_prefix, *prefix) { 299 (_, None) => { 300 // This call produced output and cleaned up after itself. 301 }, 302 (None, Some(p)) => { 303 // Some previous call to `item` produced output, 304 // but this one did not, prefix should be the same as 305 // the one we set. 306 debug_assert_eq!(separator, p); 307 // We clean up here even though it's not necessary just 308 // to be able to do all these assertion checks. 309 *prefix = None; 310 }, 311 (Some(old), Some(new)) => { 312 // No previous call to `item` produced output, and this one 313 // either. 314 debug_assert_eq!(old, new); 315 }, 316 } 317 } 318 319 let old_prefix = before(&mut self.inner.prefix, self.separator); 320 f(self.inner)?; 321 after(old_prefix, &mut self.inner.prefix, self.separator); 322 Ok(()) 323 } 324 325 /// Serialises a CSS value, writing any separator as necessary. 326 /// 327 /// The separator is never written before any `item` produces any output, 328 /// and is written in subsequent calls only if the `item` produces some 329 /// output on its own again. This lets us handle `Option<T>` fields by 330 /// just not printing anything on `None`. 331 #[inline] 332 pub fn item<T>(&mut self, item: &T) -> fmt::Result 333 where 334 T: ToCss, 335 { 336 self.write_item(|inner| item.to_css(inner)) 337 } 338 339 /// Writes a string as-is (i.e. not escaped or wrapped in quotes) 340 /// with any separator as necessary. 341 /// 342 /// See SequenceWriter::item. 343 #[inline] 344 pub fn raw_item(&mut self, item: &str) -> fmt::Result { 345 self.write_item(|inner| inner.write_str(item)) 346 } 347 } 348 349 /// Type used as the associated type in the `OneOrMoreSeparated` trait on a 350 /// type to indicate that a serialized list of elements of this type is 351 /// separated by commas. 352 pub struct Comma; 353 354 /// Type used as the associated type in the `OneOrMoreSeparated` trait on a 355 /// type to indicate that a serialized list of elements of this type is 356 /// separated by spaces. 357 pub struct Space; 358 359 /// Type used as the associated type in the `OneOrMoreSeparated` trait on a 360 /// type to indicate that a serialized list of elements of this type is 361 /// separated by commas, but spaces without commas are also allowed when 362 /// parsing. 363 pub struct CommaWithSpace; 364 365 /// A trait satisfied by the types corresponding to separators. 366 pub trait Separator { 367 /// The separator string that the satisfying separator type corresponds to. 368 fn separator() -> &'static str; 369 370 /// Parses a sequence of values separated by this separator. 371 /// 372 /// The given closure is called repeatedly for each item in the sequence. 373 /// 374 /// Successful results are accumulated in a vector. 375 /// 376 /// This method returns `Err(_)` the first time a closure does or if 377 /// the separators aren't correct. 378 fn parse<'i, 't, F, T, E>( 379 parser: &mut Parser<'i, 't>, 380 parse_one: F, 381 ) -> Result<Vec<T>, ParseError<'i, E>> 382 where 383 F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>; 384 } 385 386 impl Separator for Comma { 387 fn separator() -> &'static str { 388 ", " 389 } 390 391 fn parse<'i, 't, F, T, E>( 392 input: &mut Parser<'i, 't>, 393 parse_one: F, 394 ) -> Result<Vec<T>, ParseError<'i, E>> 395 where 396 F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>, 397 { 398 input.parse_comma_separated(parse_one) 399 } 400 } 401 402 impl Separator for Space { 403 fn separator() -> &'static str { 404 " " 405 } 406 407 fn parse<'i, 't, F, T, E>( 408 input: &mut Parser<'i, 't>, 409 mut parse_one: F, 410 ) -> Result<Vec<T>, ParseError<'i, E>> 411 where 412 F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>, 413 { 414 input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less. 415 let mut results = vec![parse_one(input)?]; 416 loop { 417 input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less. 418 if let Ok(item) = input.try_parse(&mut parse_one) { 419 results.push(item); 420 } else { 421 return Ok(results); 422 } 423 } 424 } 425 } 426 427 impl Separator for CommaWithSpace { 428 fn separator() -> &'static str { 429 ", " 430 } 431 432 fn parse<'i, 't, F, T, E>( 433 input: &mut Parser<'i, 't>, 434 mut parse_one: F, 435 ) -> Result<Vec<T>, ParseError<'i, E>> 436 where 437 F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>, 438 { 439 input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less. 440 let mut results = vec![parse_one(input)?]; 441 loop { 442 input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less. 443 let comma_location = input.current_source_location(); 444 let comma = input.try_parse(|i| i.expect_comma()).is_ok(); 445 input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less. 446 if let Ok(item) = input.try_parse(&mut parse_one) { 447 results.push(item); 448 } else if comma { 449 return Err(comma_location.new_unexpected_token_error(Token::Comma)); 450 } else { 451 break; 452 } 453 } 454 Ok(results) 455 } 456 } 457 458 /// Marker trait on T to automatically implement ToCss for Vec<T> when T's are 459 /// separated by some delimiter `delim`. 460 pub trait OneOrMoreSeparated { 461 /// Associated type indicating which separator is used. 462 type S: Separator; 463 } 464 465 impl OneOrMoreSeparated for UnicodeRange { 466 type S = Comma; 467 } 468 469 impl<T> ToCss for Vec<T> 470 where 471 T: ToCss + OneOrMoreSeparated, 472 { 473 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 474 where 475 W: Write, 476 { 477 let mut iter = self.iter(); 478 iter.next().unwrap().to_css(dest)?; 479 for item in iter { 480 dest.write_str(<T as OneOrMoreSeparated>::S::separator())?; 481 item.to_css(dest)?; 482 } 483 Ok(()) 484 } 485 } 486 487 impl<T> ToCss for Box<T> 488 where 489 T: ?Sized + ToCss, 490 { 491 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 492 where 493 W: Write, 494 { 495 (**self).to_css(dest) 496 } 497 } 498 499 impl<T> ToCss for Arc<T> 500 where 501 T: ?Sized + ToCss, 502 { 503 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 504 where 505 W: Write, 506 { 507 (**self).to_css(dest) 508 } 509 } 510 511 impl ToCss for Au { 512 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 513 where 514 W: Write, 515 { 516 self.to_f64_px().to_css(dest)?; 517 dest.write_str("px") 518 } 519 } 520 521 macro_rules! impl_to_css_for_predefined_type { 522 ($name: ty) => { 523 impl<'a> ToCss for $name { 524 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 525 where 526 W: Write, 527 { 528 ::cssparser::ToCss::to_css(self, dest) 529 } 530 } 531 }; 532 } 533 534 impl_to_css_for_predefined_type!(f32); 535 impl_to_css_for_predefined_type!(i8); 536 impl_to_css_for_predefined_type!(i32); 537 impl_to_css_for_predefined_type!(u8); 538 impl_to_css_for_predefined_type!(u16); 539 impl_to_css_for_predefined_type!(u32); 540 impl_to_css_for_predefined_type!(::cssparser::Token<'a>); 541 impl_to_css_for_predefined_type!(::cssparser::UnicodeRange); 542 543 /// Helper types for the handling of specified values. 544 pub mod specified { 545 use crate::ParsingMode; 546 547 /// Whether to allow negative lengths or not. 548 #[repr(u8)] 549 #[derive( 550 Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, PartialOrd, Serialize, ToShmem, 551 )] 552 pub enum AllowedNumericType { 553 /// Allow all kind of numeric values. 554 All, 555 /// Allow only non-negative numeric values. 556 NonNegative, 557 /// Allow only numeric values greater or equal to 1.0. 558 AtLeastOne, 559 /// Allow only numeric values from 0 to 1.0. 560 ZeroToOne, 561 } 562 563 impl Default for AllowedNumericType { 564 #[inline] 565 fn default() -> Self { 566 AllowedNumericType::All 567 } 568 } 569 570 impl AllowedNumericType { 571 /// Whether the value fits the rules of this numeric type. 572 #[inline] 573 pub fn is_ok(&self, parsing_mode: ParsingMode, val: f32) -> bool { 574 if parsing_mode.allows_all_numeric_values() { 575 return true; 576 } 577 match *self { 578 AllowedNumericType::All => true, 579 AllowedNumericType::NonNegative => val >= 0.0, 580 AllowedNumericType::AtLeastOne => val >= 1.0, 581 AllowedNumericType::ZeroToOne => val >= 0.0 && val <= 1.0, 582 } 583 } 584 585 /// Clamp the value following the rules of this numeric type. 586 #[inline] 587 pub fn clamp(&self, val: f32) -> f32 { 588 match *self { 589 AllowedNumericType::All => val, 590 AllowedNumericType::NonNegative => val.max(0.), 591 AllowedNumericType::AtLeastOne => val.max(1.), 592 AllowedNumericType::ZeroToOne => val.max(0.).min(1.), 593 } 594 } 595 } 596 } 597 598 /// A numeric value used by the Typed OM. 599 /// 600 /// This corresponds to `CSSNumericValue` and its subclasses in the Typed OM 601 /// specification. It represents numbers that can appear in CSS values, 602 /// including both simple unit quantities and sums of numeric terms. 603 /// 604 /// Unlike the parser-level representation, `NumericValue` is property-agnostic 605 /// and suitable for conversion to or from the `CSSNumericValue` family of DOM 606 /// objects. 607 #[derive(Clone, Debug)] 608 #[repr(C)] 609 pub enum NumericValue { 610 /// A single numeric value with a concrete unit. 611 /// 612 /// This corresponds to `CSSUnitValue`. The `value` field stores the raw 613 /// numeric component, and the `unit` field stores the textual unit 614 /// identifier (e.g. `"px"`, `"em"`, `"%"`, `"deg"`). 615 Unit { 616 /// The numeric component of the value. 617 value: f32, 618 /// The textual unit string (e.g. `"px"`, `"em"`, `"deg"`). 619 unit: CssString, 620 }, 621 622 /// A sum of multiple numeric values. 623 /// 624 /// This corresponds to `CSSMathSum`, representing an expression such as 625 /// `10px + 2em`. Each entry in `values` is another `NumericValue`, which 626 /// may itself be a unit value or a nested sum. 627 Sum { 628 /// The list of numeric terms that make up the sum. 629 values: ThinVec<NumericValue>, 630 }, 631 } 632 633 /// A property-agnostic representation of a value, used by Typed OM. 634 /// 635 /// `TypedValue` is the internal counterpart of the various `CSSStyleValue` 636 /// subclasses defined by the Typed OM specification. It captures values that 637 /// can be represented independently of any particular property. 638 #[derive(Clone, Debug)] 639 #[repr(C)] 640 pub enum TypedValue { 641 /// A keyword value (e.g. `"block"`, `"none"`, `"thin"`). 642 /// 643 /// Keywords are stored as a `CssString` so they can be represented and 644 /// transferred independently of any specific property. This corresponds 645 /// to `CSSKeywordValue` in the Typed OM specification. 646 Keyword(CssString), 647 648 /// A numeric value such as a length, angle, time, or a sum thereof. 649 /// 650 /// This corresponds to the `CSSNumericValue` hierarchy in the Typed OM 651 /// specification, including `CSSUnitValue` and `CSSMathSum`. 652 Numeric(NumericValue), 653 } 654 655 /// Reifies a value into its Typed OM representation. 656 /// 657 /// This trait is the Typed OM analogue of [`ToCss`]. Instead of serializing 658 /// values into CSS syntax, it converts them into [`TypedValue`]s that can be 659 /// exposed to the DOM as `CSSStyleValue` subclasses. 660 /// 661 /// This trait is derivable with `#[derive(ToTyped)]`. The derived 662 /// implementation currently supports: 663 /// 664 /// * Keyword enums: Enums whose variants are all unit variants are 665 /// automatically reified as [`TypedValue::Keyword`], using the same 666 /// serialization logic as [`ToCss`]. 667 /// 668 /// * Structs and data-carrying variants: When the 669 /// `#[typed_value(derive_fields)]` attribute is present, the derive attempts 670 /// to call `.to_typed()` recursively on inner fields or variant payloads, 671 /// producing a nested [`TypedValue`] representation when possible. 672 /// 673 /// * Other cases: If no automatic mapping is defined or recursion is not 674 /// enabled, the derived implementation falls back to the default method, 675 /// returning `None`. 676 /// 677 /// The `derive_fields` attribute is intentionally opt-in for now to avoid 678 /// forcing types that do not participate in reification to implement 679 /// [`ToTyped`]. Once Typed OM coverage stabilizes, this behavior is expected 680 /// to become the default (see the corresponding follow-up bug). 681 /// 682 /// Over time, the derive may be extended to handle additional CSS value 683 /// categories such as numeric, color, and transform types. 684 pub trait ToTyped { 685 /// Attempt to convert `self` into a [`TypedValue`]. 686 /// 687 /// Returns `Some(TypedValue)` if the value can be reified into a 688 /// property-agnostic CSSStyleValue subclass. Returns `None` if the value 689 /// is unrepresentable, in which case reification produces a property-tied 690 /// CSSStyleValue instead. 691 fn to_typed(&self) -> Option<TypedValue> { 692 None 693 } 694 } 695 696 impl<T> ToTyped for Box<T> 697 where 698 T: ?Sized + ToTyped, 699 { 700 fn to_typed(&self) -> Option<TypedValue> { 701 (**self).to_typed() 702 } 703 } 704 705 impl ToTyped for Au { 706 fn to_typed(&self) -> Option<TypedValue> { 707 let value = self.to_f32_px(); 708 let unit = CssString::from("px"); 709 Some(TypedValue::Numeric(NumericValue::Unit { value, unit })) 710 } 711 } 712 713 macro_rules! impl_to_typed_for_predefined_type { 714 ($name: ty) => { 715 impl<'a> ToTyped for $name { 716 fn to_typed(&self) -> Option<TypedValue> { 717 // XXX Should return TypedValue::Numeric with unit "number" 718 // once that variant is available. Tracked in bug 1990419. 719 None 720 } 721 } 722 }; 723 } 724 725 impl_to_typed_for_predefined_type!(f32); 726 impl_to_typed_for_predefined_type!(i8); 727 impl_to_typed_for_predefined_type!(i32);