keyframes_rule.rs (20648B)
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 //! Keyframes: https://drafts.csswg.org/css-animations/#keyframes 6 7 use crate::derives::*; 8 use crate::error_reporting::ContextualParseError; 9 use crate::parser::ParserContext; 10 use crate::properties::{ 11 longhands::{ 12 animation_composition::single_value::SpecifiedValue as SpecifiedComposition, 13 transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction, 14 }, 15 parse_property_declaration_list, LonghandId, PropertyDeclaration, PropertyDeclarationBlock, 16 PropertyDeclarationId, PropertyDeclarationIdSet, 17 }; 18 use crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard}; 19 use crate::shared_lock::{Locked, ToCssWithGuard}; 20 use crate::stylesheets::rule_parser::VendorPrefix; 21 use crate::stylesheets::{CssRuleType, StylesheetContents}; 22 use crate::values::{serialize_percentage, KeyframesName}; 23 use cssparser::{ 24 parse_one_rule, AtRuleParser, DeclarationParser, Parser, ParserInput, ParserState, 25 QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, Token, 26 }; 27 use servo_arc::Arc; 28 use std::borrow::Cow; 29 use std::fmt::{self, Write}; 30 use style_traits::{ 31 CssStringWriter, CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss, 32 }; 33 34 /// A [`@keyframes`][keyframes] rule. 35 /// 36 /// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes 37 #[derive(Debug, ToShmem)] 38 pub struct KeyframesRule { 39 /// The name of the current animation. 40 pub name: KeyframesName, 41 /// The keyframes specified for this CSS rule. 42 pub keyframes: Vec<Arc<Locked<Keyframe>>>, 43 /// Vendor prefix type the @keyframes has. 44 pub vendor_prefix: Option<VendorPrefix>, 45 /// The line and column of the rule's source code. 46 pub source_location: SourceLocation, 47 } 48 49 impl ToCssWithGuard for KeyframesRule { 50 // Serialization of KeyframesRule is not specced. 51 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { 52 dest.write_str("@keyframes ")?; 53 self.name.to_css(&mut CssWriter::new(dest))?; 54 dest.write_str(" {")?; 55 let iter = self.keyframes.iter(); 56 for lock in iter { 57 dest.write_str("\n")?; 58 let keyframe = lock.read_with(&guard); 59 keyframe.to_css(guard, dest)?; 60 } 61 dest.write_str("\n}") 62 } 63 } 64 65 impl KeyframesRule { 66 /// Returns the index of the last keyframe that matches the given selector. 67 /// If the selector is not valid, or no keyframe is found, returns None. 68 /// 69 /// Related spec: 70 /// <https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule> 71 pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> { 72 let mut input = ParserInput::new(selector); 73 if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) { 74 for (i, keyframe) in self.keyframes.iter().enumerate().rev() { 75 if keyframe.read_with(guard).selector == selector { 76 return Some(i); 77 } 78 } 79 } 80 None 81 } 82 } 83 84 impl DeepCloneWithLock for KeyframesRule { 85 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self { 86 KeyframesRule { 87 name: self.name.clone(), 88 keyframes: self 89 .keyframes 90 .iter() 91 .map(|x| Arc::new(lock.wrap(x.read_with(guard).deep_clone_with_lock(lock, guard)))) 92 .collect(), 93 vendor_prefix: self.vendor_prefix.clone(), 94 source_location: self.source_location.clone(), 95 } 96 } 97 } 98 99 /// A number from 0 to 1, indicating the percentage of the animation when this 100 /// keyframe should run. 101 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)] 102 pub struct KeyframePercentage(pub f32); 103 104 impl ::std::cmp::Ord for KeyframePercentage { 105 #[inline] 106 fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { 107 // We know we have a number from 0 to 1, so unwrap() here is safe. 108 self.0.partial_cmp(&other.0).unwrap() 109 } 110 } 111 112 impl ::std::cmp::Eq for KeyframePercentage {} 113 114 impl ToCss for KeyframePercentage { 115 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 116 where 117 W: Write, 118 { 119 serialize_percentage(self.0, dest) 120 } 121 } 122 123 impl KeyframePercentage { 124 /// Trivially constructs a new `KeyframePercentage`. 125 #[inline] 126 pub fn new(value: f32) -> KeyframePercentage { 127 debug_assert!(value >= 0. && value <= 1.); 128 KeyframePercentage(value) 129 } 130 131 fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<KeyframePercentage, ParseError<'i>> { 132 let token = input.next()?.clone(); 133 match token { 134 Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("from") => { 135 Ok(KeyframePercentage::new(0.)) 136 }, 137 Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("to") => { 138 Ok(KeyframePercentage::new(1.)) 139 }, 140 Token::Percentage { 141 unit_value: percentage, 142 .. 143 } if percentage >= 0. && percentage <= 1. => Ok(KeyframePercentage::new(percentage)), 144 _ => Err(input.new_unexpected_token_error(token)), 145 } 146 } 147 } 148 149 /// A keyframes selector is a list of percentages or from/to symbols, which are 150 /// converted at parse time to percentages. 151 #[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)] 152 #[css(comma)] 153 pub struct KeyframeSelector(#[css(iterable)] Vec<KeyframePercentage>); 154 155 impl KeyframeSelector { 156 /// Return the list of percentages this selector contains. 157 #[inline] 158 pub fn percentages(&self) -> &[KeyframePercentage] { 159 &self.0 160 } 161 162 /// A dummy public function so we can write a unit test for this. 163 pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector { 164 KeyframeSelector(percentages) 165 } 166 167 /// Parse a keyframe selector from CSS input. 168 pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> { 169 input 170 .parse_comma_separated(KeyframePercentage::parse) 171 .map(KeyframeSelector) 172 } 173 } 174 175 /// A keyframe. 176 #[derive(Debug, ToShmem)] 177 pub struct Keyframe { 178 /// The selector this keyframe was specified from. 179 pub selector: KeyframeSelector, 180 181 /// The declaration block that was declared inside this keyframe. 182 /// 183 /// Note that `!important` rules in keyframes don't apply, but we keep this 184 /// `Arc` just for convenience. 185 pub block: Arc<Locked<PropertyDeclarationBlock>>, 186 187 /// The line and column of the rule's source code. 188 pub source_location: SourceLocation, 189 } 190 191 impl ToCssWithGuard for Keyframe { 192 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { 193 self.selector.to_css(&mut CssWriter::new(dest))?; 194 dest.write_str(" { ")?; 195 self.block.read_with(guard).to_css(dest)?; 196 dest.write_str(" }")?; 197 Ok(()) 198 } 199 } 200 201 impl Keyframe { 202 /// Parse a CSS keyframe. 203 pub fn parse<'i>( 204 css: &'i str, 205 parent_stylesheet_contents: &StylesheetContents, 206 lock: &SharedRwLock, 207 ) -> Result<Arc<Locked<Self>>, ParseError<'i>> { 208 let url_data = &parent_stylesheet_contents.url_data; 209 let namespaces = &parent_stylesheet_contents.namespaces; 210 let mut context = ParserContext::new( 211 parent_stylesheet_contents.origin, 212 &url_data, 213 Some(CssRuleType::Keyframe), 214 ParsingMode::DEFAULT, 215 parent_stylesheet_contents.quirks_mode, 216 Cow::Borrowed(&*namespaces), 217 None, 218 None, 219 ); 220 let mut input = ParserInput::new(css); 221 let mut input = Parser::new(&mut input); 222 223 let mut rule_parser = KeyframeListParser { 224 context: &mut context, 225 shared_lock: &lock, 226 }; 227 parse_one_rule(&mut input, &mut rule_parser) 228 } 229 } 230 231 impl DeepCloneWithLock for Keyframe { 232 /// Deep clones this Keyframe. 233 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Keyframe { 234 Keyframe { 235 selector: self.selector.clone(), 236 block: Arc::new(lock.wrap(self.block.read_with(guard).clone())), 237 source_location: self.source_location.clone(), 238 } 239 } 240 } 241 242 /// A keyframes step value. This can be a synthetised keyframes animation, that 243 /// is, one autogenerated from the current computed values, or a list of 244 /// declarations to apply. 245 /// 246 /// TODO: Find a better name for this? 247 #[derive(Clone, Debug, MallocSizeOf)] 248 pub enum KeyframesStepValue { 249 /// A step formed by a declaration block specified by the CSS. 250 Declarations { 251 /// The declaration block per se. 252 #[cfg_attr( 253 feature = "gecko", 254 ignore_malloc_size_of = "XXX: Primary ref, measure if DMD says it's worthwhile" 255 )] 256 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] 257 block: Arc<Locked<PropertyDeclarationBlock>>, 258 }, 259 /// A synthetic step computed from the current computed values at the time 260 /// of the animation. 261 ComputedValues, 262 } 263 264 /// A single step from a keyframe animation. 265 #[derive(Clone, Debug, MallocSizeOf)] 266 pub struct KeyframesStep { 267 /// The percentage of the animation duration when this step starts. 268 pub start_percentage: KeyframePercentage, 269 /// Declarations that will determine the final style during the step, or 270 /// `ComputedValues` if this is an autogenerated step. 271 pub value: KeyframesStepValue, 272 /// Whether an animation-timing-function declaration exists in the list of 273 /// declarations. 274 /// 275 /// This is used to know when to override the keyframe animation style. 276 pub declared_timing_function: bool, 277 /// Whether an animation-composition declaration exists in the list of 278 /// declarations. 279 /// 280 /// This is used to know when to override the keyframe animation style. 281 pub declared_composition: bool, 282 } 283 284 impl KeyframesStep { 285 #[inline] 286 fn new( 287 start_percentage: KeyframePercentage, 288 value: KeyframesStepValue, 289 guard: &SharedRwLockReadGuard, 290 ) -> Self { 291 let mut declared_timing_function = false; 292 let mut declared_composition = false; 293 if let KeyframesStepValue::Declarations { ref block } = value { 294 for prop_decl in block.read_with(guard).declarations().iter() { 295 match *prop_decl { 296 PropertyDeclaration::AnimationTimingFunction(..) => { 297 declared_timing_function = true; 298 }, 299 PropertyDeclaration::AnimationComposition(..) => { 300 declared_composition = true; 301 }, 302 _ => continue, 303 } 304 // Don't need to continue the loop if both are found. 305 if declared_timing_function && declared_composition { 306 break; 307 } 308 } 309 } 310 311 KeyframesStep { 312 start_percentage, 313 value, 314 declared_timing_function, 315 declared_composition, 316 } 317 } 318 319 /// Return specified PropertyDeclaration. 320 #[inline] 321 fn get_declared_property<'a>( 322 &'a self, 323 guard: &'a SharedRwLockReadGuard, 324 property: LonghandId, 325 ) -> Option<&'a PropertyDeclaration> { 326 match self.value { 327 KeyframesStepValue::Declarations { ref block } => { 328 let guard = block.read_with(guard); 329 let (declaration, _) = guard 330 .get(PropertyDeclarationId::Longhand(property)) 331 .unwrap(); 332 match *declaration { 333 PropertyDeclaration::CSSWideKeyword(..) => None, 334 // FIXME: Bug 1710735: Support css variable in @keyframes rule. 335 PropertyDeclaration::WithVariables(..) => None, 336 _ => Some(declaration), 337 } 338 }, 339 KeyframesStepValue::ComputedValues => { 340 panic!("Shouldn't happen to set this property in missing keyframes") 341 }, 342 } 343 } 344 345 /// Return specified TransitionTimingFunction if this KeyframesSteps has 346 /// 'animation-timing-function'. 347 pub fn get_animation_timing_function( 348 &self, 349 guard: &SharedRwLockReadGuard, 350 ) -> Option<SpecifiedTimingFunction> { 351 if !self.declared_timing_function { 352 return None; 353 } 354 355 self.get_declared_property(guard, LonghandId::AnimationTimingFunction) 356 .map(|decl| { 357 match *decl { 358 PropertyDeclaration::AnimationTimingFunction(ref value) => { 359 // Use the first value 360 value.0[0].clone() 361 }, 362 _ => unreachable!("Unexpected PropertyDeclaration"), 363 } 364 }) 365 } 366 367 /// Return CompositeOperation if this KeyframesSteps has 'animation-composition'. 368 pub fn get_animation_composition( 369 &self, 370 guard: &SharedRwLockReadGuard, 371 ) -> Option<SpecifiedComposition> { 372 if !self.declared_composition { 373 return None; 374 } 375 376 self.get_declared_property(guard, LonghandId::AnimationComposition) 377 .map(|decl| { 378 match *decl { 379 PropertyDeclaration::AnimationComposition(ref value) => { 380 // Use the first value 381 value.0[0].clone() 382 }, 383 _ => unreachable!("Unexpected PropertyDeclaration"), 384 } 385 }) 386 } 387 } 388 389 /// This structure represents a list of animation steps computed from the list 390 /// of keyframes, in order. 391 /// 392 /// It only takes into account animable properties. 393 #[derive(Clone, Debug, MallocSizeOf)] 394 pub struct KeyframesAnimation { 395 /// The difference steps of the animation. 396 pub steps: Vec<KeyframesStep>, 397 /// The properties that change in this animation. 398 pub properties_changed: PropertyDeclarationIdSet, 399 /// Vendor prefix type the @keyframes has. 400 pub vendor_prefix: Option<VendorPrefix>, 401 } 402 403 /// Get all the animated properties in a keyframes animation. 404 fn get_animated_properties( 405 keyframes: &[Arc<Locked<Keyframe>>], 406 guard: &SharedRwLockReadGuard, 407 ) -> PropertyDeclarationIdSet { 408 let mut ret = PropertyDeclarationIdSet::default(); 409 // NB: declarations are already deduplicated, so we don't have to check for 410 // it here. 411 for keyframe in keyframes { 412 let keyframe = keyframe.read_with(&guard); 413 let block = keyframe.block.read_with(guard); 414 // CSS Animations spec clearly defines that properties with !important 415 // in keyframe rules are invalid and ignored, but it's still ambiguous 416 // whether we should drop the !important properties or retain the 417 // properties when they are set via CSSOM. So we assume there might 418 // be properties with !important in keyframe rules here. 419 // See the spec issue https://github.com/w3c/csswg-drafts/issues/1824 420 for declaration in block.normal_declaration_iter() { 421 let declaration_id = declaration.id(); 422 423 if declaration_id == PropertyDeclarationId::Longhand(LonghandId::Display) { 424 continue; 425 } 426 427 if !declaration_id.is_animatable() { 428 continue; 429 } 430 431 ret.insert(declaration_id); 432 } 433 } 434 435 ret 436 } 437 438 impl KeyframesAnimation { 439 /// Create a keyframes animation from a given list of keyframes. 440 /// 441 /// This will return a keyframe animation with empty steps and 442 /// properties_changed if the list of keyframes is empty, or there are no 443 /// animated properties obtained from the keyframes. 444 /// 445 /// Otherwise, this will compute and sort the steps used for the animation, 446 /// and return the animation object. 447 pub fn from_keyframes( 448 keyframes: &[Arc<Locked<Keyframe>>], 449 vendor_prefix: Option<VendorPrefix>, 450 guard: &SharedRwLockReadGuard, 451 ) -> Self { 452 let mut result = KeyframesAnimation { 453 steps: vec![], 454 properties_changed: PropertyDeclarationIdSet::default(), 455 vendor_prefix, 456 }; 457 458 if keyframes.is_empty() { 459 return result; 460 } 461 462 result.properties_changed = get_animated_properties(keyframes, guard); 463 if result.properties_changed.is_empty() { 464 return result; 465 } 466 467 for keyframe in keyframes { 468 let keyframe = keyframe.read_with(&guard); 469 for percentage in keyframe.selector.0.iter() { 470 result.steps.push(KeyframesStep::new( 471 *percentage, 472 KeyframesStepValue::Declarations { 473 block: keyframe.block.clone(), 474 }, 475 guard, 476 )); 477 } 478 } 479 480 // Sort by the start percentage, so we can easily find a frame. 481 result.steps.sort_by_key(|step| step.start_percentage); 482 483 // Prepend autogenerated keyframes if appropriate. 484 if result.steps[0].start_percentage.0 != 0. { 485 result.steps.insert( 486 0, 487 KeyframesStep::new( 488 KeyframePercentage::new(0.), 489 KeyframesStepValue::ComputedValues, 490 guard, 491 ), 492 ); 493 } 494 495 if result.steps.last().unwrap().start_percentage.0 != 1. { 496 result.steps.push(KeyframesStep::new( 497 KeyframePercentage::new(1.), 498 KeyframesStepValue::ComputedValues, 499 guard, 500 )); 501 } 502 503 result 504 } 505 } 506 507 /// Parses a keyframes list, like: 508 /// 0%, 50% { 509 /// width: 50%; 510 /// } 511 /// 512 /// 40%, 60%, 100% { 513 /// width: 100%; 514 /// } 515 struct KeyframeListParser<'a, 'b> { 516 context: &'a mut ParserContext<'b>, 517 shared_lock: &'a SharedRwLock, 518 } 519 520 /// Parses a keyframe list from CSS input. 521 pub fn parse_keyframe_list<'a>( 522 context: &mut ParserContext<'a>, 523 input: &mut Parser, 524 shared_lock: &SharedRwLock, 525 ) -> Vec<Arc<Locked<Keyframe>>> { 526 let mut parser = KeyframeListParser { 527 context, 528 shared_lock, 529 }; 530 RuleBodyParser::new(input, &mut parser) 531 .filter_map(Result::ok) 532 .collect() 533 } 534 535 impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeListParser<'a, 'b> { 536 type Prelude = (); 537 type AtRule = Arc<Locked<Keyframe>>; 538 type Error = StyleParseErrorKind<'i>; 539 } 540 541 impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> { 542 type Declaration = Arc<Locked<Keyframe>>; 543 type Error = StyleParseErrorKind<'i>; 544 } 545 546 impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a, 'b> { 547 type Prelude = KeyframeSelector; 548 type QualifiedRule = Arc<Locked<Keyframe>>; 549 type Error = StyleParseErrorKind<'i>; 550 551 fn parse_prelude<'t>( 552 &mut self, 553 input: &mut Parser<'i, 't>, 554 ) -> Result<Self::Prelude, ParseError<'i>> { 555 let start_position = input.position(); 556 KeyframeSelector::parse(input).map_err(|e| { 557 let location = e.location; 558 let error = ContextualParseError::InvalidKeyframeRule( 559 input.slice_from(start_position), 560 e.clone(), 561 ); 562 self.context.log_css_error(location, error); 563 e 564 }) 565 } 566 567 fn parse_block<'t>( 568 &mut self, 569 selector: Self::Prelude, 570 start: &ParserState, 571 input: &mut Parser<'i, 't>, 572 ) -> Result<Self::QualifiedRule, ParseError<'i>> { 573 let block = self.context.nest_for_rule(CssRuleType::Keyframe, |p| { 574 parse_property_declaration_list(&p, input, &[]) 575 }); 576 Ok(Arc::new(self.shared_lock.wrap(Keyframe { 577 selector, 578 block: Arc::new(self.shared_lock.wrap(block)), 579 source_location: start.source_location(), 580 }))) 581 } 582 } 583 584 impl<'a, 'b, 'i> RuleBodyItemParser<'i, Arc<Locked<Keyframe>>, StyleParseErrorKind<'i>> 585 for KeyframeListParser<'a, 'b> 586 { 587 fn parse_qualified(&self) -> bool { 588 true 589 } 590 fn parse_declarations(&self) -> bool { 591 false 592 } 593 }