1use crate::derives::*;
8use crate::error_reporting::ContextualParseError;
9use crate::parser::{Parse, ParserContext};
10use 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};
18use crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
19use crate::shared_lock::{Locked, ToCssWithGuard};
20use crate::stylesheets::rule_parser::VendorPrefix;
21use crate::stylesheets::{CssRuleType, StylesheetContents};
22use crate::values::specified::animation::TimelineRangeName;
23use crate::values::specified::{Number, Percentage};
24use crate::values::{serialize_percentage, KeyframesName};
25use cssparser::{
26 parse_one_rule, AtRuleParser, DeclarationParser, Parser, ParserState, QualifiedRuleParser,
27 RuleBodyItemParser, RuleBodyParser, SourceLocation, Token,
28};
29use servo_arc::Arc;
30use std::borrow::Cow;
31use std::fmt::{self, Write};
32use style_traits::{
33 CssStringWriter, CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss,
34};
35
36#[derive(Debug, ToShmem)]
40pub struct KeyframesRule {
41 pub name: KeyframesName,
43 pub keyframes: Vec<Arc<Locked<Keyframe>>>,
45 pub vendor_prefix: Option<VendorPrefix>,
47 pub source_location: SourceLocation,
49}
50
51impl ToCssWithGuard for KeyframesRule {
52 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
54 dest.write_str("@keyframes ")?;
55 self.name.to_css(&mut CssWriter::new(dest))?;
56 dest.write_str(" {")?;
57 let iter = self.keyframes.iter();
58 for lock in iter {
59 dest.write_str("\n")?;
60 let keyframe = lock.read_with(guard);
61 keyframe.to_css(guard, dest)?;
62 }
63 dest.write_str("\n}")
64 }
65}
66
67impl KeyframesRule {
68 pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
74 if let Ok(selector) = Parser::new(selector).parse_entirely(KeyframeSelectors::parse) {
75 for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
76 if keyframe.read_with(guard).selector == selector {
77 return Some(i);
78 }
79 }
80 }
81 None
82 }
83}
84
85impl DeepCloneWithLock for KeyframesRule {
86 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
87 KeyframesRule {
88 name: self.name.clone(),
89 keyframes: self
90 .keyframes
91 .iter()
92 .map(|x| Arc::new(lock.wrap(x.read_with(guard).deep_clone_with_lock(lock, guard))))
93 .collect(),
94 vendor_prefix: self.vendor_prefix.clone(),
95 source_location: self.source_location,
96 }
97 }
98}
99
100#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]
103pub struct KeyframePercentage(pub f32);
104
105impl ::std::cmp::Ord for KeyframePercentage {
106 #[inline]
107 fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
108 self.0.partial_cmp(&other.0).unwrap()
110 }
111}
112
113impl ::std::cmp::Eq for KeyframePercentage {}
114
115impl ToCss for KeyframePercentage {
116 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
117 where
118 W: Write,
119 {
120 serialize_percentage(self.0, dest)
121 }
122}
123
124impl KeyframePercentage {
125 #[inline]
127 pub fn new(value: f32) -> KeyframePercentage {
128 KeyframePercentage(value)
129 }
130
131 fn parse(input: &mut Parser) -> Result<KeyframePercentage, ParseError> {
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 (0. ..=1.).contains(&percentage) => Ok(KeyframePercentage::new(percentage)),
144 _ => Err(ParseError::unexpected_token()),
145 }
146 }
147}
148
149#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
154pub struct KeyframeSelector {
155 pub range_name: TimelineRangeName,
159 pub percentage: KeyframePercentage,
162}
163
164impl KeyframeSelector {
165 fn from_percentage(percentage: KeyframePercentage) -> Self {
167 debug_assert!(percentage.0 >= 0. && percentage.0 <= 1.);
168 KeyframeSelector {
169 range_name: TimelineRangeName::None,
170 percentage,
171 }
172 }
173
174 pub fn parse_internal(input: &mut Parser) -> Result<Self, ParseError> {
176 if let Ok(percentage) = input.try_parse(KeyframePercentage::parse) {
178 return Ok(Self::from_percentage(percentage));
179 }
180
181 if !crate::pref!("layout.css.scroll-driven-animations.enabled") {
183 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
184 }
185
186 Ok(Self {
189 range_name: TimelineRangeName::parse(input)?,
190 percentage: KeyframePercentage::new(input.expect_percentage()?),
191 })
192 }
193}
194
195impl Parse for KeyframeSelector {
196 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
197 KeyframeSelector::parse_internal(input)
198 }
199}
200
201#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
203#[css(comma)]
204pub struct KeyframeSelectors(#[css(iterable)] Vec<KeyframeSelector>);
205
206impl KeyframeSelectors {
207 pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelectors {
209 KeyframeSelectors(
210 percentages
211 .into_iter()
212 .map(KeyframeSelector::from_percentage)
213 .collect(),
214 )
215 }
216
217 pub fn parse(input: &mut Parser) -> Result<Self, ParseError> {
219 input
220 .parse_comma_separated(KeyframeSelector::parse_internal)
221 .map(KeyframeSelectors)
222 }
223}
224
225#[derive(Debug, ToShmem)]
227pub struct Keyframe {
228 pub selector: KeyframeSelectors,
230
231 pub block: Arc<Locked<PropertyDeclarationBlock>>,
236
237 pub source_location: SourceLocation,
239}
240
241impl ToCssWithGuard for Keyframe {
242 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
243 self.selector.to_css(&mut CssWriter::new(dest))?;
244 dest.write_str(" { ")?;
245 self.block.read_with(guard).to_css(dest)?;
246 dest.write_str(" }")?;
247 Ok(())
248 }
249}
250
251impl Keyframe {
252 pub fn parse(
254 css: &str,
255 parent_stylesheet_contents: &StylesheetContents,
256 lock: &SharedRwLock,
257 ) -> Result<Arc<Locked<Self>>, ParseError> {
258 let url_data = &parent_stylesheet_contents.url_data;
259 let namespaces = &parent_stylesheet_contents.namespaces;
260 let mut context = ParserContext::new(
261 parent_stylesheet_contents.origin,
262 url_data,
263 Some(CssRuleType::Keyframe),
264 ParsingMode::DEFAULT,
265 parent_stylesheet_contents.quirks_mode,
266 Cow::Borrowed(namespaces),
267 None,
268 None,
269 Default::default(),
270 );
271 let mut input = Parser::new(css);
272
273 let mut rule_parser = KeyframeListParser {
274 context: &mut context,
275 shared_lock: lock,
276 };
277 parse_one_rule(&mut input, &mut rule_parser)
278 }
279}
280
281impl DeepCloneWithLock for Keyframe {
282 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Keyframe {
284 Keyframe {
285 selector: self.selector.clone(),
286 block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
287 source_location: self.source_location,
288 }
289 }
290}
291
292#[derive(Clone, Debug, MallocSizeOf)]
298pub enum KeyframesStepValue {
299 Declarations {
301 #[cfg_attr(
303 feature = "gecko",
304 ignore_malloc_size_of = "XXX: Primary ref, measure if DMD says it's worthwhile"
305 )]
306 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
307 block: Arc<Locked<PropertyDeclarationBlock>>,
308 },
309 ComputedValues,
312}
313
314#[derive(Clone, Debug, MallocSizeOf)]
316pub struct KeyframesStep {
317 pub start_offset: KeyframeSelector,
319 pub value: KeyframesStepValue,
322 pub declared_timing_function: bool,
327 pub declared_composition: bool,
332}
333
334impl KeyframesStep {
335 #[inline]
336 fn new(
337 start_offset: KeyframeSelector,
338 value: KeyframesStepValue,
339 guard: &SharedRwLockReadGuard,
340 ) -> Self {
341 let mut declared_timing_function = false;
342 let mut declared_composition = false;
343 if let KeyframesStepValue::Declarations { ref block } = value {
344 for prop_decl in block.read_with(guard).declarations().iter() {
345 match *prop_decl {
346 PropertyDeclaration::AnimationTimingFunction(..) => {
347 declared_timing_function = true;
348 },
349 PropertyDeclaration::AnimationComposition(..) => {
350 declared_composition = true;
351 },
352 _ => continue,
353 }
354 if declared_timing_function && declared_composition {
356 break;
357 }
358 }
359 }
360
361 KeyframesStep {
362 start_offset,
363 value,
364 declared_timing_function,
365 declared_composition,
366 }
367 }
368
369 #[inline]
371 fn get_declared_property<'a>(
372 &'a self,
373 guard: &'a SharedRwLockReadGuard,
374 property: LonghandId,
375 ) -> Option<&'a PropertyDeclaration> {
376 match self.value {
377 KeyframesStepValue::Declarations { ref block } => {
378 let guard = block.read_with(guard);
379 let (declaration, _) = guard
380 .get(PropertyDeclarationId::Longhand(property))
381 .unwrap();
382 match *declaration {
383 PropertyDeclaration::CSSWideKeyword(..) => None,
384 PropertyDeclaration::WithVariables(..) => None,
386 _ => Some(declaration),
387 }
388 },
389 KeyframesStepValue::ComputedValues => {
390 panic!("Shouldn't happen to set this property in missing keyframes")
391 },
392 }
393 }
394
395 pub fn get_animation_timing_function(
398 &self,
399 guard: &SharedRwLockReadGuard,
400 ) -> Option<SpecifiedTimingFunction> {
401 if !self.declared_timing_function {
402 return None;
403 }
404
405 self.get_declared_property(guard, LonghandId::AnimationTimingFunction)
406 .map(|decl| {
407 match *decl {
408 PropertyDeclaration::AnimationTimingFunction(ref value) => {
409 value.0[0].clone()
411 },
412 _ => unreachable!("Unexpected PropertyDeclaration"),
413 }
414 })
415 }
416
417 pub fn get_animation_composition(
419 &self,
420 guard: &SharedRwLockReadGuard,
421 ) -> Option<SpecifiedComposition> {
422 if !self.declared_composition {
423 return None;
424 }
425
426 self.get_declared_property(guard, LonghandId::AnimationComposition)
427 .map(|decl| {
428 match *decl {
429 PropertyDeclaration::AnimationComposition(ref value) => {
430 value.0[0]
432 },
433 _ => unreachable!("Unexpected PropertyDeclaration"),
434 }
435 })
436 }
437}
438
439#[derive(Clone, Debug, MallocSizeOf)]
444pub struct KeyframesAnimation {
445 pub steps: Vec<KeyframesStep>,
447 pub steps_with_range_name: Vec<KeyframesStep>,
455 #[cfg(feature = "servo")]
457 pub properties_changed: PropertyDeclarationIdSet,
458 pub vendor_prefix: Option<VendorPrefix>,
460}
461
462fn has_animated_properties(
465 keyframes: &[Arc<Locked<Keyframe>>],
466 guard: &SharedRwLockReadGuard,
467 mut properties_changed: Option<&mut PropertyDeclarationIdSet>,
468) -> bool {
469 for keyframe in keyframes {
472 let keyframe = keyframe.read_with(guard);
473 let block = keyframe.block.read_with(guard);
474 for declaration in block.normal_declaration_iter() {
481 let declaration_id = declaration.id();
482
483 if declaration_id == PropertyDeclarationId::Longhand(LonghandId::Display)
484 && !crate::pref!("layout.css.display-animations.enabled")
485 {
486 continue;
487 }
488
489 if !declaration_id.is_animatable() {
490 continue;
491 }
492
493 if let Some(ref mut properties_changed) = properties_changed {
494 properties_changed.insert(declaration_id);
495 } else {
496 return true;
497 }
498 }
499 }
500
501 if let Some(properties_changed) = properties_changed {
502 !properties_changed.is_empty()
503 } else {
504 false
505 }
506}
507
508impl KeyframesAnimation {
509 pub fn from_keyframes(
518 keyframes: &[Arc<Locked<Keyframe>>],
519 vendor_prefix: Option<VendorPrefix>,
520 guard: &SharedRwLockReadGuard,
521 ) -> Self {
522 let mut result = KeyframesAnimation {
523 steps: vec![],
524 steps_with_range_name: vec![],
525 #[cfg(feature = "servo")]
526 properties_changed: PropertyDeclarationIdSet::default(),
527 vendor_prefix,
528 };
529
530 #[cfg(feature = "servo")]
531 let properties_changed = Some(&mut result.properties_changed);
532 #[cfg(feature = "gecko")]
533 let properties_changed = None;
534
535 if keyframes.is_empty() || !has_animated_properties(keyframes, guard, properties_changed) {
536 return result;
537 }
538
539 let mut steps = vec![];
541
542 for keyframe in keyframes {
543 let keyframe = keyframe.read_with(guard);
544 for selector in keyframe.selector.0.iter() {
545 let step = KeyframesStep::new(
546 *selector,
547 KeyframesStepValue::Declarations {
548 block: keyframe.block.clone(),
549 },
550 guard,
551 );
552
553 if !selector.range_name.is_none() {
554 result.steps_with_range_name.push(step);
555 } else {
556 steps.push(step);
557 }
558 }
559 }
560
561 steps.sort_by_key(|step| step.start_offset.percentage);
565
566 #[cfg(feature = "servo")]
576 if steps.is_empty() || steps[0].start_offset.percentage.0 != 0. {
577 steps.insert(
578 0,
579 KeyframesStep::new(
580 KeyframeSelector::from_percentage(KeyframePercentage::new(0.)),
581 KeyframesStepValue::ComputedValues,
582 guard,
583 ),
584 );
585 }
586 #[cfg(feature = "servo")]
587 if steps.last().unwrap().start_offset.percentage.0 != 1. {
588 steps.push(KeyframesStep::new(
589 KeyframeSelector::from_percentage(KeyframePercentage::new(1.)),
590 KeyframesStepValue::ComputedValues,
591 guard,
592 ));
593 }
594
595 result.steps = steps;
596 result
597 }
598}
599
600struct KeyframeListParser<'a, 'b> {
609 context: &'a mut ParserContext<'b>,
610 shared_lock: &'a SharedRwLock,
611}
612
613pub fn parse_keyframe_list<'a>(
615 context: &mut ParserContext<'a>,
616 input: &mut Parser,
617 shared_lock: &SharedRwLock,
618) -> Vec<Arc<Locked<Keyframe>>> {
619 let mut parser = KeyframeListParser {
620 context,
621 shared_lock,
622 };
623 RuleBodyParser::new(input, &mut parser)
624 .filter_map(Result::ok)
625 .collect()
626}
627
628impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeListParser<'a, 'b> {
629 type Prelude = ();
630 type AtRule = Arc<Locked<Keyframe>>;
631 type Error = StyleParseErrorKind;
632}
633
634impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> {
635 type Declaration = Arc<Locked<Keyframe>>;
636 type Error = StyleParseErrorKind;
637}
638
639impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a, 'b> {
640 type Prelude = KeyframeSelectors;
641 type QualifiedRule = Arc<Locked<Keyframe>>;
642 type Error = StyleParseErrorKind;
643
644 fn parse_prelude(&mut self, input: &mut Parser<'i>) -> Result<Self::Prelude, ParseError> {
645 let start_position = input.position();
646 let start_location = input.current_source_location();
647 KeyframeSelectors::parse(input).inspect_err(|e| {
648 let error = ContextualParseError::InvalidKeyframeRule(
649 input.slice_from(start_position),
650 e.clone(),
651 );
652 self.context.log_css_error(start_location, error);
653 })
654 }
655
656 fn parse_block(
657 &mut self,
658 selector: Self::Prelude,
659 start: &ParserState,
660 input: &mut Parser<'i>,
661 ) -> Result<Self::QualifiedRule, ParseError> {
662 let block = self.context.nest_for_rule(CssRuleType::Keyframe, |p| {
663 parse_property_declaration_list(p, input, &[])
664 });
665 Ok(Arc::new(self.shared_lock.wrap(Keyframe {
666 selector,
667 block: Arc::new(self.shared_lock.wrap(block)),
668 source_location: start.source_location(),
669 })))
670 }
671}
672
673impl<'a, 'b, 'i> RuleBodyItemParser<'i, Arc<Locked<Keyframe>>, StyleParseErrorKind>
674 for KeyframeListParser<'a, 'b>
675{
676 fn parse_qualified(&self) -> bool {
677 true
678 }
679 fn parse_declarations(&self) -> bool {
680 false
681 }
682}
683
684#[derive(Debug, Parse)]
693pub enum KeyframeOffset {
694 Number(Number),
696 Percentage(Percentage),
699 KeyframeSelector(KeyframeSelector),
701}