1use crate::derives::*;
8use crate::error_reporting::ContextualParseError;
9use crate::parser::{Parse, ParserContext};
10#[cfg(feature = "servo")]
11use crate::properties::PropertyDeclarationIdSet;
12use crate::properties::{
13 longhands::{
14 animation_composition::single_value::SpecifiedValue as SpecifiedComposition,
15 transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction,
16 },
17 parse_property_declaration_list, LonghandId, PropertyDeclaration, PropertyDeclarationBlock,
18 PropertyDeclarationId,
19};
20use crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
21use crate::shared_lock::{Locked, ToCssWithGuard};
22use crate::stylesheets::rule_parser::VendorPrefix;
23use crate::stylesheets::{CssRuleType, StylesheetContents};
24use crate::values::specified::animation::TimelineRangeName;
25use crate::values::specified::{Number, Percentage};
26use crate::values::{serialize_percentage, KeyframesName};
27use cssparser::{
28 parse_one_rule, AtRuleParser, DeclarationParser, Parser, ParserState, QualifiedRuleParser,
29 RuleBodyItemParser, RuleBodyParser, SourceLocation, Token,
30};
31use servo_arc::Arc;
32use std::borrow::Cow;
33use std::fmt::{self, Write};
34use style_traits::{
35 CssStringWriter, CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss,
36};
37
38#[derive(Debug, ToShmem)]
42pub struct KeyframesRule {
43 pub name: KeyframesName,
45 pub keyframes: Vec<Arc<Locked<Keyframe>>>,
47 pub vendor_prefix: Option<VendorPrefix>,
49 pub source_location: SourceLocation,
51}
52
53impl ToCssWithGuard for KeyframesRule {
54 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
56 dest.write_str("@keyframes ")?;
57 self.name.to_css(&mut CssWriter::new(dest))?;
58 dest.write_str(" {")?;
59 let iter = self.keyframes.iter();
60 for lock in iter {
61 dest.write_str("\n")?;
62 let keyframe = lock.read_with(guard);
63 keyframe.to_css(guard, dest)?;
64 }
65 dest.write_str("\n}")
66 }
67}
68
69impl KeyframesRule {
70 pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
76 if let Ok(selector) = Parser::new(selector).parse_entirely(KeyframeSelectors::parse) {
77 for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
78 if keyframe.read_with(guard).selector == selector {
79 return Some(i);
80 }
81 }
82 }
83 None
84 }
85}
86
87impl DeepCloneWithLock for KeyframesRule {
88 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
89 KeyframesRule {
90 name: self.name.clone(),
91 keyframes: self
92 .keyframes
93 .iter()
94 .map(|x| Arc::new(lock.wrap(x.read_with(guard).deep_clone_with_lock(lock, guard))))
95 .collect(),
96 vendor_prefix: self.vendor_prefix.clone(),
97 source_location: self.source_location,
98 }
99 }
100}
101
102#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]
105pub struct KeyframePercentage(pub f32);
106
107impl ::std::cmp::Ord for KeyframePercentage {
108 #[inline]
109 fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
110 self.0.partial_cmp(&other.0).unwrap()
112 }
113}
114
115impl ::std::cmp::Eq for KeyframePercentage {}
116
117impl ToCss for KeyframePercentage {
118 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
119 where
120 W: Write,
121 {
122 serialize_percentage(self.0, dest)
123 }
124}
125
126impl KeyframePercentage {
127 #[inline]
129 pub fn new(value: f32) -> KeyframePercentage {
130 KeyframePercentage(value)
131 }
132
133 fn parse(input: &mut Parser) -> Result<KeyframePercentage, ParseError> {
134 let token = input.next()?.clone();
135 match token {
136 Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("from") => {
137 Ok(KeyframePercentage::new(0.))
138 },
139 Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("to") => {
140 Ok(KeyframePercentage::new(1.))
141 },
142 Token::Percentage {
143 unit_value: percentage,
144 ..
145 } if (0. ..=1.).contains(&percentage) => Ok(KeyframePercentage::new(percentage)),
146 _ => Err(ParseError::unexpected_token()),
147 }
148 }
149}
150
151#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
156pub struct KeyframeSelector {
157 pub range_name: TimelineRangeName,
161 pub percentage: KeyframePercentage,
164}
165
166impl KeyframeSelector {
167 fn from_percentage(percentage: KeyframePercentage) -> Self {
169 debug_assert!(percentage.0 >= 0. && percentage.0 <= 1.);
170 KeyframeSelector {
171 range_name: TimelineRangeName::None,
172 percentage,
173 }
174 }
175
176 pub fn parse_internal(input: &mut Parser) -> Result<Self, ParseError> {
178 if let Ok(percentage) = input.try_parse(KeyframePercentage::parse) {
180 return Ok(Self::from_percentage(percentage));
181 }
182
183 if !crate::pref!("layout.css.scroll-driven-animations.enabled") {
185 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
186 }
187
188 Ok(Self {
191 range_name: TimelineRangeName::parse(input)?,
192 percentage: KeyframePercentage::new(input.expect_percentage()?),
193 })
194 }
195}
196
197impl Parse for KeyframeSelector {
198 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
199 KeyframeSelector::parse_internal(input)
200 }
201}
202
203#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
205#[css(comma)]
206pub struct KeyframeSelectors(#[css(iterable)] Vec<KeyframeSelector>);
207
208impl KeyframeSelectors {
209 pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelectors {
211 KeyframeSelectors(
212 percentages
213 .into_iter()
214 .map(KeyframeSelector::from_percentage)
215 .collect(),
216 )
217 }
218
219 pub fn parse(input: &mut Parser) -> Result<Self, ParseError> {
221 input
222 .parse_comma_separated(KeyframeSelector::parse_internal)
223 .map(KeyframeSelectors)
224 }
225}
226
227#[derive(Debug, ToShmem)]
229pub struct Keyframe {
230 pub selector: KeyframeSelectors,
232
233 pub block: Arc<Locked<PropertyDeclarationBlock>>,
238
239 pub source_location: SourceLocation,
241}
242
243impl ToCssWithGuard for Keyframe {
244 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
245 self.selector.to_css(&mut CssWriter::new(dest))?;
246 dest.write_str(" { ")?;
247 self.block.read_with(guard).to_css(dest)?;
248 dest.write_str(" }")?;
249 Ok(())
250 }
251}
252
253impl Keyframe {
254 pub fn parse(
256 css: &str,
257 parent_stylesheet_contents: &StylesheetContents,
258 lock: &SharedRwLock,
259 ) -> Result<Arc<Locked<Self>>, ParseError> {
260 let url_data = &parent_stylesheet_contents.url_data;
261 let namespaces = &parent_stylesheet_contents.namespaces;
262 let mut context = ParserContext::new(
263 parent_stylesheet_contents.origin,
264 url_data,
265 Some(CssRuleType::Keyframe),
266 ParsingMode::DEFAULT,
267 parent_stylesheet_contents.quirks_mode,
268 Cow::Borrowed(namespaces),
269 None,
270 None,
271 Default::default(),
272 );
273 let mut input = Parser::new(css);
274
275 let mut rule_parser = KeyframeListParser {
276 context: &mut context,
277 shared_lock: lock,
278 };
279 parse_one_rule(&mut input, &mut rule_parser)
280 }
281}
282
283impl DeepCloneWithLock for Keyframe {
284 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Keyframe {
286 Keyframe {
287 selector: self.selector.clone(),
288 block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
289 source_location: self.source_location,
290 }
291 }
292}
293
294#[derive(Clone, Debug, MallocSizeOf)]
300pub enum KeyframesStepValue {
301 Declarations {
303 #[cfg_attr(
305 feature = "gecko",
306 ignore_malloc_size_of = "XXX: Primary ref, measure if DMD says it's worthwhile"
307 )]
308 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
309 block: Arc<Locked<PropertyDeclarationBlock>>,
310 },
311 ComputedValues,
314}
315
316#[derive(Clone, Debug, MallocSizeOf)]
318pub struct KeyframesStep {
319 pub start_offset: KeyframeSelector,
321 pub value: KeyframesStepValue,
324 pub declared_timing_function: bool,
329 pub declared_composition: bool,
334}
335
336impl KeyframesStep {
337 #[inline]
338 fn new(
339 start_offset: KeyframeSelector,
340 value: KeyframesStepValue,
341 guard: &SharedRwLockReadGuard,
342 ) -> Self {
343 let mut declared_timing_function = false;
344 let mut declared_composition = false;
345 if let KeyframesStepValue::Declarations { ref block } = value {
346 for prop_decl in block.read_with(guard).declarations().iter() {
347 match *prop_decl {
348 PropertyDeclaration::AnimationTimingFunction(..) => {
349 declared_timing_function = true;
350 },
351 PropertyDeclaration::AnimationComposition(..) => {
352 declared_composition = true;
353 },
354 _ => continue,
355 }
356 if declared_timing_function && declared_composition {
358 break;
359 }
360 }
361 }
362
363 KeyframesStep {
364 start_offset,
365 value,
366 declared_timing_function,
367 declared_composition,
368 }
369 }
370
371 #[inline]
373 fn get_declared_property<'a>(
374 &'a self,
375 guard: &'a SharedRwLockReadGuard,
376 property: LonghandId,
377 ) -> Option<&'a PropertyDeclaration> {
378 match self.value {
379 KeyframesStepValue::Declarations { ref block } => {
380 let guard = block.read_with(guard);
381 let (declaration, _) = guard
382 .get(PropertyDeclarationId::Longhand(property))
383 .unwrap();
384 match *declaration {
385 PropertyDeclaration::CSSWideKeyword(..) => None,
386 PropertyDeclaration::WithVariables(..) => None,
388 _ => Some(declaration),
389 }
390 },
391 KeyframesStepValue::ComputedValues => {
392 panic!("Shouldn't happen to set this property in missing keyframes")
393 },
394 }
395 }
396
397 pub fn get_animation_timing_function(
400 &self,
401 guard: &SharedRwLockReadGuard,
402 ) -> Option<SpecifiedTimingFunction> {
403 if !self.declared_timing_function {
404 return None;
405 }
406
407 self.get_declared_property(guard, LonghandId::AnimationTimingFunction)
408 .map(|decl| {
409 match *decl {
410 PropertyDeclaration::AnimationTimingFunction(ref value) => {
411 value.0[0].clone()
413 },
414 _ => unreachable!("Unexpected PropertyDeclaration"),
415 }
416 })
417 }
418
419 pub fn get_animation_composition(
421 &self,
422 guard: &SharedRwLockReadGuard,
423 ) -> Option<SpecifiedComposition> {
424 if !self.declared_composition {
425 return None;
426 }
427
428 self.get_declared_property(guard, LonghandId::AnimationComposition)
429 .map(|decl| {
430 match *decl {
431 PropertyDeclaration::AnimationComposition(ref value) => {
432 value.0[0]
434 },
435 _ => unreachable!("Unexpected PropertyDeclaration"),
436 }
437 })
438 }
439}
440
441#[derive(Clone, Debug, MallocSizeOf)]
446pub struct KeyframesAnimation {
447 pub steps: Vec<KeyframesStep>,
449 pub steps_with_range_name: Vec<KeyframesStep>,
457 #[cfg(feature = "servo")]
459 pub properties_changed: PropertyDeclarationIdSet,
460 pub vendor_prefix: Option<VendorPrefix>,
462}
463
464#[cfg(feature = "servo")]
465type AnimatedPropertiesInner = PropertyDeclarationIdSet;
466#[cfg(feature = "gecko")]
467type AnimatedPropertiesInner = bool;
468
469#[derive(Default)]
470struct AnimatedProperties(AnimatedPropertiesInner);
471
472#[cfg(feature = "servo")]
473impl AnimatedProperties {
474 const CAN_STOP_AFTER_FINDING_PROPERTY: bool = false;
475
476 #[inline]
477 fn insert_animated_property(&mut self, declaration_id: PropertyDeclarationId) {
478 self.0.insert(declaration_id);
479 }
480
481 #[inline]
482 fn is_empty(&self) -> bool {
483 self.0.is_empty()
484 }
485}
486
487#[cfg(feature = "gecko")]
488impl AnimatedProperties {
489 const CAN_STOP_AFTER_FINDING_PROPERTY: bool = true;
490
491 #[inline]
492 fn insert_animated_property(&mut self, _: PropertyDeclarationId) {
493 self.0 = true;
494 }
495
496 #[inline]
497 fn is_empty(&self) -> bool {
498 !self.0
499 }
500}
501
502fn get_animated_properties(
506 keyframes: &[Arc<Locked<Keyframe>>],
507 guard: &SharedRwLockReadGuard,
508) -> AnimatedProperties {
509 let mut animated_properties = AnimatedProperties::default();
510
511 for keyframe in keyframes {
514 let keyframe = keyframe.read_with(guard);
515 let block = keyframe.block.read_with(guard);
516 for declaration in block.normal_declaration_iter() {
523 let declaration_id = declaration.id();
524
525 if declaration_id == PropertyDeclarationId::Longhand(LonghandId::Display)
526 && !crate::pref!("layout.css.display-animations.enabled")
527 {
528 continue;
529 }
530
531 if !declaration_id.is_animatable() {
532 continue;
533 }
534
535 animated_properties.insert_animated_property(declaration_id);
536 if AnimatedProperties::CAN_STOP_AFTER_FINDING_PROPERTY {
537 return animated_properties;
538 }
539 }
540 }
541
542 animated_properties
543}
544
545impl KeyframesAnimation {
546 pub fn from_keyframes(
555 keyframes: &[Arc<Locked<Keyframe>>],
556 vendor_prefix: Option<VendorPrefix>,
557 guard: &SharedRwLockReadGuard,
558 ) -> Self {
559 let mut result = KeyframesAnimation {
560 steps: vec![],
561 steps_with_range_name: vec![],
562 #[cfg(feature = "servo")]
563 properties_changed: PropertyDeclarationIdSet::default(),
564 vendor_prefix,
565 };
566
567 if keyframes.is_empty() {
568 return result;
569 }
570
571 let animated_properties = get_animated_properties(keyframes, guard);
572 if animated_properties.is_empty() {
573 return result;
574 }
575 #[cfg(feature = "servo")] {
576 result.properties_changed = animated_properties.0;
577 }
578
579 let mut steps = vec![];
581
582 for keyframe in keyframes {
583 let keyframe = keyframe.read_with(guard);
584 for selector in keyframe.selector.0.iter() {
585 let step = KeyframesStep::new(
586 *selector,
587 KeyframesStepValue::Declarations {
588 block: keyframe.block.clone(),
589 },
590 guard,
591 );
592
593 if !selector.range_name.is_none() {
594 result.steps_with_range_name.push(step);
595 } else {
596 steps.push(step);
597 }
598 }
599 }
600
601 steps.sort_by_key(|step| step.start_offset.percentage);
605
606 #[cfg(feature = "servo")] {
608 if steps.is_empty() || steps[0].start_offset.percentage.0 != 0. {
609 steps.insert(
610 0,
611 KeyframesStep::new(
612 KeyframeSelector::from_percentage(KeyframePercentage::new(0.)),
613 KeyframesStepValue::ComputedValues,
614 guard,
615 ),
616 );
617 }
618 if steps.last().unwrap().start_offset.percentage.0 != 1. {
619 steps.push(KeyframesStep::new(
620 KeyframeSelector::from_percentage(KeyframePercentage::new(1.)),
621 KeyframesStepValue::ComputedValues,
622 guard,
623 ));
624 }
625 }
626
627 result.steps = steps;
628 result
629 }
630}
631
632struct KeyframeListParser<'a, 'b> {
641 context: &'a mut ParserContext<'b>,
642 shared_lock: &'a SharedRwLock,
643}
644
645pub fn parse_keyframe_list<'a>(
647 context: &mut ParserContext<'a>,
648 input: &mut Parser,
649 shared_lock: &SharedRwLock,
650) -> Vec<Arc<Locked<Keyframe>>> {
651 let mut parser = KeyframeListParser {
652 context,
653 shared_lock,
654 };
655 RuleBodyParser::new(input, &mut parser)
656 .filter_map(Result::ok)
657 .collect()
658}
659
660impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeListParser<'a, 'b> {
661 type Prelude = ();
662 type AtRule = Arc<Locked<Keyframe>>;
663 type Error = StyleParseErrorKind;
664}
665
666impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> {
667 type Declaration = Arc<Locked<Keyframe>>;
668 type Error = StyleParseErrorKind;
669}
670
671impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a, 'b> {
672 type Prelude = KeyframeSelectors;
673 type QualifiedRule = Arc<Locked<Keyframe>>;
674 type Error = StyleParseErrorKind;
675
676 fn parse_prelude(&mut self, input: &mut Parser<'i>) -> Result<Self::Prelude, ParseError> {
677 let start_position = input.position();
678 let start_location = input.current_source_location();
679 KeyframeSelectors::parse(input).inspect_err(|e| {
680 let error = ContextualParseError::InvalidKeyframeRule(
681 input.slice_from(start_position),
682 e.clone(),
683 );
684 self.context.log_css_error(start_location, error);
685 })
686 }
687
688 fn parse_block(
689 &mut self,
690 selector: Self::Prelude,
691 start: &ParserState,
692 input: &mut Parser<'i>,
693 ) -> Result<Self::QualifiedRule, ParseError> {
694 let block = self.context.nest_for_rule(CssRuleType::Keyframe, |p| {
695 parse_property_declaration_list(p, input, &[])
696 });
697 Ok(Arc::new(self.shared_lock.wrap(Keyframe {
698 selector,
699 block: Arc::new(self.shared_lock.wrap(block)),
700 source_location: start.source_location(),
701 })))
702 }
703}
704
705impl<'a, 'b, 'i> RuleBodyItemParser<'i, Arc<Locked<Keyframe>>, StyleParseErrorKind>
706 for KeyframeListParser<'a, 'b>
707{
708 fn parse_qualified(&self) -> bool {
709 true
710 }
711 fn parse_declarations(&self) -> bool {
712 false
713 }
714}
715
716#[derive(Debug, Parse)]
725pub enum KeyframeOffset {
726 Number(Number),
728 Percentage(Percentage),
731 KeyframeSelector(KeyframeSelector),
733}