value.rs (23968B)
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 //! Parsing for registered custom properties. 6 7 use std::fmt::{self, Write}; 8 9 use super::{ 10 registry::PropertyRegistrationData, 11 syntax::{ 12 data_type::DataType, Component as SyntaxComponent, ComponentName, Descriptor, Multiplier, 13 }, 14 }; 15 use crate::custom_properties::ComputedValue as ComputedPropertyValue; 16 use crate::derives::*; 17 use crate::properties; 18 use crate::stylesheets::{CssRuleType, Origin, UrlExtraData}; 19 use crate::values::{ 20 animated::{self, Animate, Procedure}, 21 computed::{self, ToComputedValue}, 22 specified, CustomIdent, 23 }; 24 use crate::{ 25 dom::AttributeProvider, 26 parser::{Parse, ParserContext}, 27 }; 28 use cssparser::{BasicParseErrorKind, ParseErrorKind, Parser as CSSParser, TokenSerializationType}; 29 use selectors::matching::QuirksMode; 30 use servo_arc::Arc; 31 use smallvec::SmallVec; 32 use style_traits::{ 33 owned_str::OwnedStr, CssWriter, ParseError as StyleParseError, ParsingMode, 34 PropertySyntaxParseError, StyleParseErrorKind, ToCss, 35 }; 36 37 /// A single component of the computed value. 38 pub type ComputedValueComponent = GenericValueComponent< 39 computed::Length, 40 computed::Number, 41 computed::Percentage, 42 computed::LengthPercentage, 43 computed::Color, 44 computed::Image, 45 computed::url::ComputedUrl, 46 computed::Integer, 47 computed::Angle, 48 computed::Time, 49 computed::Resolution, 50 computed::Transform, 51 >; 52 53 /// A single component of the specified value. 54 pub type SpecifiedValueComponent = GenericValueComponent< 55 specified::Length, 56 specified::Number, 57 specified::Percentage, 58 specified::LengthPercentage, 59 specified::Color, 60 specified::Image, 61 specified::url::SpecifiedUrl, 62 specified::Integer, 63 specified::Angle, 64 specified::Time, 65 specified::Resolution, 66 specified::Transform, 67 >; 68 69 impl<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform> 70 GenericValueComponent<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform> 71 { 72 fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) { 73 let first_token_type = match self { 74 Self::Length(_) | Self::Angle(_) | Self::Time(_) | Self::Resolution(_) => { 75 TokenSerializationType::Dimension 76 }, 77 Self::Number(_) | Self::Integer(_) => TokenSerializationType::Number, 78 Self::Percentage(_) | Self::LengthPercentage(_) => TokenSerializationType::Percentage, 79 Self::Color(_) 80 | Self::Image(_) 81 | Self::Url(_) 82 | Self::TransformFunction(_) 83 | Self::TransformList(_) => TokenSerializationType::Function, 84 Self::CustomIdent(_) => TokenSerializationType::Ident, 85 Self::String(_) => TokenSerializationType::Other, 86 }; 87 let last_token_type = if first_token_type == TokenSerializationType::Function { 88 TokenSerializationType::Other 89 } else { 90 first_token_type 91 }; 92 (first_token_type, last_token_type) 93 } 94 } 95 96 /// A generic enum used for both specified value components and computed value components. 97 #[derive( 98 Animate, Clone, ToCss, ToComputedValue, ToResolvedValue, Debug, MallocSizeOf, PartialEq, ToShmem, 99 )] 100 #[animation(no_bound(Image, Url))] 101 pub enum GenericValueComponent< 102 Length, 103 Number, 104 Percentage, 105 LengthPercentage, 106 Color, 107 Image, 108 Url, 109 Integer, 110 Angle, 111 Time, 112 Resolution, 113 TransformFunction, 114 > { 115 /// A <length> value 116 Length(Length), 117 /// A <number> value 118 Number(Number), 119 /// A <percentage> value 120 Percentage(Percentage), 121 /// A <length-percentage> value 122 LengthPercentage(LengthPercentage), 123 /// A <color> value 124 Color(Color), 125 /// An <image> value 126 #[animation(error)] 127 Image(Image), 128 /// A <url> value 129 #[animation(error)] 130 Url(Url), 131 /// An <integer> value 132 Integer(Integer), 133 /// An <angle> value 134 Angle(Angle), 135 /// A <time> value 136 Time(Time), 137 /// A <resolution> value 138 Resolution(Resolution), 139 /// A <transform-function> value 140 /// TODO(bug 1884606): <transform-function> `none` should not interpolate. 141 TransformFunction(TransformFunction), 142 /// A <custom-ident> value 143 #[animation(error)] 144 CustomIdent(CustomIdent), 145 /// A <transform-list> value, equivalent to <transform-function>+ 146 /// TODO(bug 1884606): <transform-list> `none` should not interpolate. 147 TransformList(ComponentList<Self>), 148 /// A <string> value 149 #[animation(error)] 150 String(OwnedStr), 151 } 152 153 /// A list of component values, including the list's multiplier. 154 #[derive(Clone, ToComputedValue, ToResolvedValue, Debug, MallocSizeOf, PartialEq, ToShmem)] 155 pub struct ComponentList<Component> { 156 /// Multiplier 157 pub multiplier: Multiplier, 158 /// The list of components contained. 159 pub components: crate::OwnedSlice<Component>, 160 } 161 162 impl<Component: Animate> Animate for ComponentList<Component> { 163 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 164 if self.multiplier != other.multiplier { 165 return Err(()); 166 } 167 let components = animated::lists::by_computed_value::animate( 168 &self.components, 169 &other.components, 170 procedure, 171 )?; 172 Ok(Self { 173 multiplier: self.multiplier, 174 components, 175 }) 176 } 177 } 178 179 impl<Component: ToCss> ToCss for ComponentList<Component> { 180 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 181 where 182 W: Write, 183 { 184 let mut iter = self.components.iter(); 185 let Some(first) = iter.next() else { 186 return Ok(()); 187 }; 188 first.to_css(dest)?; 189 190 // The separator implied by the multiplier for this list. 191 let separator = match self.multiplier { 192 // <https://drafts.csswg.org/cssom-1/#serialize-a-whitespace-separated-list> 193 Multiplier::Space => " ", 194 // <https://drafts.csswg.org/cssom-1/#serialize-a-comma-separated-list> 195 Multiplier::Comma => ", ", 196 }; 197 for component in iter { 198 dest.write_str(separator)?; 199 component.to_css(dest)?; 200 } 201 Ok(()) 202 } 203 } 204 205 /// A struct for a single specified registered custom property value that includes its original URL 206 // data so the value can be uncomputed later. 207 #[derive( 208 Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem, 209 )] 210 pub struct Value<Component> { 211 /// The registered custom property value. 212 pub(crate) v: ValueInner<Component>, 213 /// The URL data of the registered custom property from before it was computed. This is 214 /// necessary to uncompute registered custom properties. 215 #[css(skip)] 216 url_data: UrlExtraData, 217 } 218 219 impl<Component: Animate> Animate for Value<Component> { 220 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 221 let v = self.v.animate(&other.v, procedure)?; 222 Ok(Value { 223 v, 224 url_data: self.url_data.clone(), 225 }) 226 } 227 } 228 229 impl<Component> Value<Component> { 230 /// Creates a new registered custom property value. 231 pub fn new(v: ValueInner<Component>, url_data: UrlExtraData) -> Self { 232 Self { v, url_data } 233 } 234 235 /// Creates a new registered custom property value presumed to have universal syntax. 236 pub fn universal(var: Arc<ComputedPropertyValue>) -> Self { 237 let url_data = var.url_data.clone(); 238 let v = ValueInner::Universal(var); 239 Self { v, url_data } 240 } 241 } 242 243 impl<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform> 244 Value<GenericValueComponent<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>> 245 where 246 Self: ToCss, 247 { 248 fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) { 249 match &self.v { 250 ValueInner::Component(component) => component.serialization_types(), 251 ValueInner::Universal(_) => unreachable!(), 252 ValueInner::List(list) => list 253 .components 254 .first() 255 .map_or(Default::default(), |f| f.serialization_types()), 256 } 257 } 258 259 /// Convert to an untyped variable value. 260 pub fn to_variable_value(&self) -> ComputedPropertyValue { 261 if let ValueInner::Universal(ref value) = self.v { 262 return (**value).clone(); 263 } 264 let serialization_types = self.serialization_types(); 265 ComputedPropertyValue::new( 266 self.to_css_string(), 267 &self.url_data, 268 serialization_types.0, 269 serialization_types.1, 270 ) 271 } 272 } 273 274 /// A specified registered custom property value. 275 #[derive( 276 Animate, ToComputedValue, ToResolvedValue, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToShmem, 277 )] 278 pub enum ValueInner<Component> { 279 /// A single specified component value whose syntax descriptor component did not have a 280 /// multiplier. 281 Component(Component), 282 /// A specified value whose syntax descriptor was the universal syntax definition. 283 #[animation(error)] 284 Universal(#[ignore_malloc_size_of = "Arc"] Arc<ComputedPropertyValue>), 285 /// A list of specified component values whose syntax descriptor component had a multiplier. 286 List(#[animation(field_bound)] ComponentList<Component>), 287 } 288 289 /// Specified custom property value. 290 pub type SpecifiedValue = Value<SpecifiedValueComponent>; 291 292 /// Computed custom property value. 293 pub type ComputedValue = Value<ComputedValueComponent>; 294 295 impl SpecifiedValue { 296 /// Convert a registered custom property to a Computed custom property value, given input and a 297 /// property registration. 298 pub fn compute<'i, 't>( 299 input: &mut CSSParser<'i, 't>, 300 registration: &PropertyRegistrationData, 301 url_data: &UrlExtraData, 302 context: &computed::Context, 303 allow_computationally_dependent: AllowComputationallyDependent, 304 ) -> Result<ComputedValue, ()> { 305 debug_assert!(!registration.syntax.is_universal(), "Shouldn't be needed"); 306 let Ok(value) = Self::parse( 307 input, 308 ®istration.syntax, 309 url_data, 310 allow_computationally_dependent, 311 ) else { 312 return Err(()); 313 }; 314 315 Ok(value.to_computed_value(context)) 316 } 317 318 /// Parse and validate a registered custom property value according to its syntax descriptor, 319 /// and check for computational independence. 320 pub fn parse<'i, 't>( 321 mut input: &mut CSSParser<'i, 't>, 322 syntax: &Descriptor, 323 url_data: &UrlExtraData, 324 allow_computationally_dependent: AllowComputationallyDependent, 325 ) -> Result<Self, StyleParseError<'i>> { 326 if syntax.is_universal() { 327 let parsed = ComputedPropertyValue::parse(&mut input, url_data)?; 328 return Ok(SpecifiedValue { 329 v: ValueInner::Universal(Arc::new(parsed)), 330 url_data: url_data.clone(), 331 }); 332 } 333 334 let mut values = SmallComponentVec::new(); 335 let mut multiplier = None; 336 { 337 let mut parser = Parser::new(syntax, &mut values, &mut multiplier); 338 parser.parse(&mut input, url_data, allow_computationally_dependent)?; 339 } 340 let v = if let Some(multiplier) = multiplier { 341 ValueInner::List(ComponentList { 342 multiplier, 343 components: values.to_vec().into(), 344 }) 345 } else { 346 ValueInner::Component(values[0].clone()) 347 }; 348 Ok(Self { 349 v, 350 url_data: url_data.clone(), 351 }) 352 } 353 } 354 355 impl ComputedValue { 356 fn to_declared_value(&self) -> properties::CustomDeclarationValue { 357 if let ValueInner::Universal(ref var) = self.v { 358 return properties::CustomDeclarationValue::Unparsed(Arc::clone(var)); 359 } 360 properties::CustomDeclarationValue::Parsed(Arc::new(ToComputedValue::from_computed_value( 361 self, 362 ))) 363 } 364 365 /// Returns the contained variable value if it exists, otherwise `None`. 366 pub fn as_universal(&self) -> Option<&Arc<ComputedPropertyValue>> { 367 if let ValueInner::Universal(ref var) = self.v { 368 Some(var) 369 } else { 370 None 371 } 372 } 373 374 } 375 376 /// Whether the computed value parsing should allow computationaly dependent values like 3em or 377 /// var(-foo). 378 /// 379 /// https://drafts.css-houdini.org/css-properties-values-api-1/#computationally-independent 380 pub enum AllowComputationallyDependent { 381 /// Only computationally independent values are allowed. 382 No, 383 /// Computationally independent and dependent values are allowed. 384 Yes, 385 } 386 387 type SmallComponentVec = SmallVec<[SpecifiedValueComponent; 1]>; 388 389 struct Parser<'a> { 390 syntax: &'a Descriptor, 391 output: &'a mut SmallComponentVec, 392 output_multiplier: &'a mut Option<Multiplier>, 393 } 394 395 impl<'a> Parser<'a> { 396 fn new( 397 syntax: &'a Descriptor, 398 output: &'a mut SmallComponentVec, 399 output_multiplier: &'a mut Option<Multiplier>, 400 ) -> Self { 401 Self { 402 syntax, 403 output, 404 output_multiplier, 405 } 406 } 407 408 fn parse<'i, 't>( 409 &mut self, 410 input: &mut CSSParser<'i, 't>, 411 url_data: &UrlExtraData, 412 allow_computationally_dependent: AllowComputationallyDependent, 413 ) -> Result<(), StyleParseError<'i>> { 414 use self::AllowComputationallyDependent::*; 415 let parsing_mode = match allow_computationally_dependent { 416 No => ParsingMode::DISALLOW_COMPUTATIONALLY_DEPENDENT, 417 Yes => ParsingMode::DEFAULT, 418 }; 419 let ref context = ParserContext::new( 420 Origin::Author, 421 url_data, 422 Some(CssRuleType::Style), 423 parsing_mode, 424 QuirksMode::NoQuirks, 425 /* namespaces = */ Default::default(), 426 None, 427 None, 428 ); 429 for component in self.syntax.components.iter() { 430 let result = input.try_parse(|input| { 431 input.parse_entirely(|input| { 432 Self::parse_value(context, input, &component.unpremultiplied()) 433 }) 434 }); 435 let Ok(values) = result else { continue }; 436 self.output.extend(values); 437 *self.output_multiplier = component.multiplier(); 438 break; 439 } 440 if self.output.is_empty() { 441 return Err(input.new_error(BasicParseErrorKind::EndOfInput)); 442 } 443 Ok(()) 444 } 445 446 fn parse_value<'i, 't>( 447 context: &ParserContext, 448 input: &mut CSSParser<'i, 't>, 449 component: &SyntaxComponent, 450 ) -> Result<SmallComponentVec, StyleParseError<'i>> { 451 let mut values = SmallComponentVec::new(); 452 values.push(Self::parse_component_without_multiplier( 453 context, input, component, 454 )?); 455 456 if let Some(multiplier) = component.multiplier() { 457 loop { 458 let result = Self::expect_multiplier(input, &multiplier); 459 if Self::expect_multiplier_yielded_eof_error(&result) { 460 break; 461 } 462 result?; 463 values.push(Self::parse_component_without_multiplier( 464 context, input, component, 465 )?); 466 } 467 } 468 Ok(values) 469 } 470 471 fn parse_component_without_multiplier<'i, 't>( 472 context: &ParserContext, 473 input: &mut CSSParser<'i, 't>, 474 component: &SyntaxComponent, 475 ) -> Result<SpecifiedValueComponent, StyleParseError<'i>> { 476 let data_type = match component.name() { 477 ComponentName::DataType(ty) => ty, 478 ComponentName::Ident(ref name) => { 479 let ident = CustomIdent::parse(input, &[])?; 480 if ident != *name { 481 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 482 } 483 return Ok(SpecifiedValueComponent::CustomIdent(ident)); 484 }, 485 }; 486 487 let value = match data_type { 488 DataType::Length => { 489 SpecifiedValueComponent::Length(specified::Length::parse(context, input)?) 490 }, 491 DataType::Number => { 492 SpecifiedValueComponent::Number(specified::Number::parse(context, input)?) 493 }, 494 DataType::Percentage => { 495 SpecifiedValueComponent::Percentage(specified::Percentage::parse(context, input)?) 496 }, 497 DataType::LengthPercentage => SpecifiedValueComponent::LengthPercentage( 498 specified::LengthPercentage::parse(context, input)?, 499 ), 500 DataType::Color => { 501 SpecifiedValueComponent::Color(specified::Color::parse(context, input)?) 502 }, 503 DataType::Image => { 504 SpecifiedValueComponent::Image(specified::Image::parse(context, input)?) 505 }, 506 DataType::Url => { 507 SpecifiedValueComponent::Url(specified::url::SpecifiedUrl::parse(context, input)?) 508 }, 509 DataType::Integer => { 510 SpecifiedValueComponent::Integer(specified::Integer::parse(context, input)?) 511 }, 512 DataType::Angle => { 513 SpecifiedValueComponent::Angle(specified::Angle::parse(context, input)?) 514 }, 515 DataType::Time => { 516 SpecifiedValueComponent::Time(specified::Time::parse(context, input)?) 517 }, 518 DataType::Resolution => { 519 SpecifiedValueComponent::Resolution(specified::Resolution::parse(context, input)?) 520 }, 521 DataType::TransformFunction => SpecifiedValueComponent::TransformFunction( 522 specified::Transform::parse(context, input)?, 523 ), 524 DataType::CustomIdent => { 525 let name = CustomIdent::parse(input, &[])?; 526 SpecifiedValueComponent::CustomIdent(name) 527 }, 528 DataType::TransformList => { 529 let mut values = vec![]; 530 let Some(multiplier) = component.unpremultiplied().multiplier() else { 531 debug_assert!(false, "Unpremultiplied <transform-list> had no multiplier?"); 532 return Err( 533 input.new_custom_error(StyleParseErrorKind::PropertySyntaxField( 534 PropertySyntaxParseError::UnexpectedEOF, 535 )), 536 ); 537 }; 538 debug_assert_eq!(multiplier, Multiplier::Space); 539 loop { 540 values.push(SpecifiedValueComponent::TransformFunction( 541 specified::Transform::parse(context, input)?, 542 )); 543 let result = Self::expect_multiplier(input, &multiplier); 544 if Self::expect_multiplier_yielded_eof_error(&result) { 545 break; 546 } 547 result?; 548 } 549 let list = ComponentList { 550 multiplier, 551 components: values.into(), 552 }; 553 SpecifiedValueComponent::TransformList(list) 554 }, 555 DataType::String => { 556 let string = input.expect_string()?; 557 SpecifiedValueComponent::String(string.as_ref().to_owned().into()) 558 }, 559 }; 560 Ok(value) 561 } 562 563 fn expect_multiplier_yielded_eof_error<'i>(result: &Result<(), StyleParseError<'i>>) -> bool { 564 matches!( 565 result, 566 Err(StyleParseError { 567 kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput), 568 .. 569 }) 570 ) 571 } 572 573 fn expect_multiplier<'i, 't>( 574 input: &mut CSSParser<'i, 't>, 575 multiplier: &Multiplier, 576 ) -> Result<(), StyleParseError<'i>> { 577 match multiplier { 578 Multiplier::Space => { 579 input.expect_whitespace()?; 580 if input.is_exhausted() { 581 // If there was trailing whitespace, do not interpret it as a multiplier 582 return Err(input.new_error(BasicParseErrorKind::EndOfInput)); 583 } 584 Ok(()) 585 }, 586 Multiplier::Comma => Ok(input.expect_comma()?), 587 } 588 } 589 } 590 591 /// An animated value for custom property. 592 #[derive(Clone, Debug, MallocSizeOf, PartialEq)] 593 pub struct CustomAnimatedValue { 594 /// The name of the custom property. 595 pub(crate) name: crate::custom_properties::Name, 596 /// The computed value of the custom property. 597 value: ComputedValue, 598 } 599 600 impl Animate for CustomAnimatedValue { 601 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 602 if self.name != other.name { 603 return Err(()); 604 } 605 let value = self.value.animate(&other.value, procedure)?; 606 Ok(Self { 607 name: self.name.clone(), 608 value, 609 }) 610 } 611 } 612 613 impl CustomAnimatedValue { 614 pub(crate) fn from_computed( 615 name: &crate::custom_properties::Name, 616 value: &ComputedValue, 617 ) -> Self { 618 Self { 619 name: name.clone(), 620 value: value.clone(), 621 } 622 } 623 624 pub(crate) fn from_declaration( 625 declaration: &properties::CustomDeclaration, 626 context: &mut computed::Context, 627 _initial: &properties::ComputedValues, 628 _attr_provider: &dyn AttributeProvider, 629 ) -> Option<Self> { 630 let computed_value = match declaration.value { 631 properties::CustomDeclarationValue::Unparsed(ref value) => { 632 debug_assert!( 633 context.builder.stylist.is_some(), 634 "Need a Stylist to get property registration!" 635 ); 636 let registration = context 637 .builder 638 .stylist 639 .unwrap() 640 .get_custom_property_registration(&declaration.name); 641 if registration.syntax.is_universal() { 642 // FIXME: Do we need to perform substitution here somehow? 643 ComputedValue { 644 v: ValueInner::Universal(Arc::clone(value)), 645 url_data: value.url_data.clone(), 646 } 647 } else { 648 let mut input = cssparser::ParserInput::new(&value.css); 649 let mut input = CSSParser::new(&mut input); 650 SpecifiedValue::compute( 651 &mut input, 652 registration, 653 &value.url_data, 654 context, 655 AllowComputationallyDependent::Yes, 656 ) 657 .unwrap_or_else(|_| ComputedValue { 658 v: ValueInner::Universal(Arc::clone(value)), 659 url_data: value.url_data.clone(), 660 }) 661 } 662 }, 663 properties::CustomDeclarationValue::Parsed(ref v) => v.to_computed_value(context), 664 // FIXME: This should be made to work to the extent possible like for non-custom 665 // properties (using `initial` at least to handle unset / inherit). 666 properties::CustomDeclarationValue::CSSWideKeyword(..) => return None, 667 }; 668 Some(Self { 669 name: declaration.name.clone(), 670 value: computed_value, 671 }) 672 } 673 674 pub(crate) fn to_declaration(&self) -> properties::PropertyDeclaration { 675 properties::PropertyDeclaration::Custom(properties::CustomDeclaration { 676 name: self.name.clone(), 677 value: self.value.to_declared_value(), 678 }) 679 } 680 }