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 <%namespace name="helpers" file="/helpers.mako.rs" />
6 
7 <%
8     from data import to_idl_name, SYSTEM_FONT_LONGHANDS, to_camel_case
9     from itertools import groupby
10 %>
11 
12 #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::nsCSSPropertyID;
13 use itertools::{EitherOrBoth, Itertools};
14 use crate::properties::{CSSWideKeyword, PropertyDeclaration, NonCustomPropertyIterator};
15 use crate::properties::longhands;
16 use crate::properties::longhands::visibility::computed_value::T as Visibility;
17 use crate::properties::LonghandId;
18 use servo_arc::Arc;
19 use smallvec::SmallVec;
20 use std::ptr;
21 use std::mem;
22 use fxhash::FxHashMap;
23 use super::ComputedValues;
24 use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
25 use crate::values::animated::effects::AnimatedFilter;
26 #[cfg(feature = "gecko")] use crate::values::computed::TransitionProperty;
27 use crate::values::computed::{ClipRect, Context};
28 use crate::values::computed::ToComputedValue;
29 use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
30 use crate::values::generics::effects::Filter;
31 use void::{self, Void};
32 
33 /// Convert nsCSSPropertyID to TransitionProperty
34 #[cfg(feature = "gecko")]
35 #[allow(non_upper_case_globals)]
36 impl From<nsCSSPropertyID> for TransitionProperty {
from(property: nsCSSPropertyID) -> TransitionProperty37     fn from(property: nsCSSPropertyID) -> TransitionProperty {
38         use crate::properties::ShorthandId;
39         match property {
40             % for prop in data.longhands:
41             ${prop.nscsspropertyid()} => {
42                 TransitionProperty::Longhand(LonghandId::${prop.camel_case})
43             }
44             % endfor
45             % for prop in data.shorthands_except_all():
46             ${prop.nscsspropertyid()} => {
47                 TransitionProperty::Shorthand(ShorthandId::${prop.camel_case})
48             }
49             % endfor
50             nsCSSPropertyID::eCSSPropertyExtra_all_properties => {
51                 TransitionProperty::Shorthand(ShorthandId::All)
52             }
53             _ => {
54                 panic!("non-convertible nsCSSPropertyID")
55             }
56         }
57     }
58 }
59 
60 /// A collection of AnimationValue that were composed on an element.
61 /// This HashMap stores the values that are the last AnimationValue to be
62 /// composed for each TransitionProperty.
63 pub type AnimationValueMap = FxHashMap<LonghandId, AnimationValue>;
64 
65 /// An enum to represent a single computed value belonging to an animated
66 /// property in order to be interpolated with another one. When interpolating,
67 /// both values need to belong to the same property.
68 ///
69 /// FIXME: We need to add a path for custom properties, but that's trivial after
70 /// this (is a similar path to that of PropertyDeclaration).
71 #[derive(Debug, MallocSizeOf)]
72 #[repr(u16)]
73 pub enum AnimationValue {
74     % for prop in data.longhands:
75     /// `${prop.name}`
76     % if prop.animatable and not prop.logical:
77     ${prop.camel_case}(${prop.animated_type()}),
78     % else:
79     ${prop.camel_case}(Void),
80     % endif
81     % endfor
82 }
83 
84 <%
85     animated = []
86     unanimated = []
87     animated_with_logical = []
88     for prop in data.longhands:
89         if prop.animatable:
90             animated_with_logical.append(prop)
91         if prop.animatable and not prop.logical:
92             animated.append(prop)
93         else:
94             unanimated.append(prop)
95 %>
96 
97 #[repr(C)]
98 struct AnimationValueVariantRepr<T> {
99     tag: u16,
100     value: T
101 }
102 
103 impl Clone for AnimationValue {
104     #[inline]
clone(&self) -> Self105     fn clone(&self) -> Self {
106         use self::AnimationValue::*;
107 
108         <%
109             [copy, others] = [list(g) for _, g in groupby(animated, key=lambda x: not x.specified_is_copy())]
110         %>
111 
112         let self_tag = unsafe { *(self as *const _ as *const u16) };
113         if self_tag <= LonghandId::${copy[-1].camel_case} as u16 {
114             #[derive(Clone, Copy)]
115             #[repr(u16)]
116             enum CopyVariants {
117                 % for prop in copy:
118                 _${prop.camel_case}(${prop.animated_type()}),
119                 % endfor
120             }
121 
122             unsafe {
123                 let mut out = mem::MaybeUninit::uninit();
124                 ptr::write(
125                     out.as_mut_ptr() as *mut CopyVariants,
126                     *(self as *const _ as *const CopyVariants),
127                 );
128                 return out.assume_init();
129             }
130         }
131 
132         match *self {
133             % for ty, props in groupby(others, key=lambda x: x.animated_type()):
134             <% props = list(props) %>
135             ${" |\n".join("{}(ref value)".format(prop.camel_case) for prop in props)} => {
136                 % if len(props) == 1:
137                 ${props[0].camel_case}(value.clone())
138                 % else:
139                 unsafe {
140                     let mut out = mem::MaybeUninit::uninit();
141                     ptr::write(
142                         out.as_mut_ptr() as *mut AnimationValueVariantRepr<${ty}>,
143                         AnimationValueVariantRepr {
144                             tag: *(self as *const _ as *const u16),
145                             value: value.clone(),
146                         },
147                     );
148                     out.assume_init()
149                 }
150                 % endif
151             }
152             % endfor
153             _ => unsafe { debug_unreachable!() }
154         }
155     }
156 }
157 
158 impl PartialEq for AnimationValue {
159     #[inline]
eq(&self, other: &Self) -> bool160     fn eq(&self, other: &Self) -> bool {
161         use self::AnimationValue::*;
162 
163         unsafe {
164             let this_tag = *(self as *const _ as *const u16);
165             let other_tag = *(other as *const _ as *const u16);
166             if this_tag != other_tag {
167                 return false;
168             }
169 
170             match *self {
171                 % for ty, props in groupby(animated, key=lambda x: x.animated_type()):
172                 ${" |\n".join("{}(ref this)".format(prop.camel_case) for prop in props)} => {
173                     let other_repr =
174                         &*(other as *const _ as *const AnimationValueVariantRepr<${ty}>);
175                     *this == other_repr.value
176                 }
177                 % endfor
178                 ${" |\n".join("{}(void)".format(prop.camel_case) for prop in unanimated)} => {
179                     void::unreachable(void)
180                 }
181             }
182         }
183     }
184 }
185 
186 impl AnimationValue {
187     /// Returns the longhand id this animated value corresponds to.
188     #[inline]
id(&self) -> LonghandId189     pub fn id(&self) -> LonghandId {
190         let id = unsafe { *(self as *const _ as *const LonghandId) };
191         debug_assert_eq!(id, match *self {
192             % for prop in data.longhands:
193             % if prop.animatable and not prop.logical:
194             AnimationValue::${prop.camel_case}(..) => LonghandId::${prop.camel_case},
195             % else:
196             AnimationValue::${prop.camel_case}(void) => void::unreachable(void),
197             % endif
198             % endfor
199         });
200         id
201     }
202 
203     /// "Uncompute" this animation value in order to be used inside the CSS
204     /// cascade.
uncompute(&self) -> PropertyDeclaration205     pub fn uncompute(&self) -> PropertyDeclaration {
206         use crate::properties::longhands;
207         use self::AnimationValue::*;
208 
209         use super::PropertyDeclarationVariantRepr;
210 
211         match *self {
212             <% keyfunc = lambda x: (x.base_type(), x.specified_type(), x.boxed, x.is_animatable_with_computed_value) %>
213             % for (ty, specified, boxed, computed), props in groupby(animated, key=keyfunc):
214             <% props = list(props) %>
215             ${" |\n".join("{}(ref value)".format(prop.camel_case) for prop in props)} => {
216                 % if not computed:
217                 let ref value = ToAnimatedValue::from_animated_value(value.clone());
218                 % endif
219                 let value = ${ty}::from_computed_value(&value);
220                 % if boxed:
221                 let value = Box::new(value);
222                 % endif
223                 % if len(props) == 1:
224                 PropertyDeclaration::${props[0].camel_case}(value)
225                 % else:
226                 unsafe {
227                     let mut out = mem::MaybeUninit::uninit();
228                     ptr::write(
229                         out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${specified}>,
230                         PropertyDeclarationVariantRepr {
231                             tag: *(self as *const _ as *const u16),
232                             value,
233                         },
234                     );
235                     out.assume_init()
236                 }
237                 % endif
238             }
239             % endfor
240             ${" |\n".join("{}(void)".format(prop.camel_case) for prop in unanimated)} => {
241                 void::unreachable(void)
242             }
243         }
244     }
245 
246     /// Construct an AnimationValue from a property declaration.
from_declaration( decl: &PropertyDeclaration, context: &mut Context, extra_custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>, initial: &ComputedValues, ) -> Option<Self>247     pub fn from_declaration(
248         decl: &PropertyDeclaration,
249         context: &mut Context,
250         extra_custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
251         initial: &ComputedValues,
252     ) -> Option<Self> {
253         use super::PropertyDeclarationVariantRepr;
254 
255         <%
256             keyfunc = lambda x: (
257                 x.specified_type(),
258                 x.animated_type(),
259                 x.boxed,
260                 not x.is_animatable_with_computed_value,
261                 x.style_struct.inherited,
262                 x.ident in SYSTEM_FONT_LONGHANDS and engine == "gecko",
263             )
264         %>
265 
266         let animatable = match *decl {
267             % for (specified_ty, ty, boxed, to_animated, inherit, system), props in groupby(animated_with_logical, key=keyfunc):
268             ${" |\n".join("PropertyDeclaration::{}(ref value)".format(prop.camel_case) for prop in props)} => {
269                 let decl_repr = unsafe {
270                     &*(decl as *const _ as *const PropertyDeclarationVariantRepr<${specified_ty}>)
271                 };
272                 let longhand_id = unsafe {
273                     *(&decl_repr.tag as *const u16 as *const LonghandId)
274                 };
275                 % if inherit:
276                 context.for_non_inherited_property = None;
277                 % else:
278                 context.for_non_inherited_property = Some(longhand_id);
279                 % endif
280                 % if system:
281                 if let Some(sf) = value.get_system() {
282                     longhands::system_font::resolve_system_font(sf, context)
283                 }
284                 % endif
285                 % if boxed:
286                 let value = (**value).to_computed_value(context);
287                 % else:
288                 let value = value.to_computed_value(context);
289                 % endif
290                 % if to_animated:
291                 let value = value.to_animated_value();
292                 % endif
293 
294                 unsafe {
295                     let mut out = mem::MaybeUninit::uninit();
296                     ptr::write(
297                         out.as_mut_ptr() as *mut AnimationValueVariantRepr<${ty}>,
298                         AnimationValueVariantRepr {
299                             tag: longhand_id.to_physical(context.builder.writing_mode) as u16,
300                             value,
301                         },
302                     );
303                     out.assume_init()
304                 }
305             }
306             % endfor
307             PropertyDeclaration::CSSWideKeyword(ref declaration) => {
308                 match declaration.id {
309                     // We put all the animatable properties first in the hopes
310                     // that it might increase match locality.
311                     % for prop in data.longhands:
312                     % if prop.animatable:
313                     LonghandId::${prop.camel_case} => {
314                         // FIXME(emilio, bug 1533327): I think revert (and
315                         // revert-layer) handling is not fine here, but what to
316                         // do instead?
317                         //
318                         // Seems we'd need the computed value as if it was
319                         // revert, somehow. Treating it as `unset` seems fine
320                         // for now...
321                         let style_struct = match declaration.keyword {
322                             % if not prop.style_struct.inherited:
323                             CSSWideKeyword::Revert |
324                             CSSWideKeyword::RevertLayer |
325                             CSSWideKeyword::Unset |
326                             % endif
327                             CSSWideKeyword::Initial => {
328                                 initial.get_${prop.style_struct.name_lower}()
329                             },
330                             % if prop.style_struct.inherited:
331                             CSSWideKeyword::Revert |
332                             CSSWideKeyword::RevertLayer |
333                             CSSWideKeyword::Unset |
334                             % endif
335                             CSSWideKeyword::Inherit => {
336                                 context.builder
337                                        .get_parent_${prop.style_struct.name_lower}()
338                             },
339                         };
340                         let computed = style_struct
341                         % if prop.logical:
342                             .clone_${prop.ident}(context.builder.writing_mode);
343                         % else:
344                             .clone_${prop.ident}();
345                         % endif
346 
347                         % if not prop.is_animatable_with_computed_value:
348                         let computed = computed.to_animated_value();
349                         % endif
350 
351                         % if prop.logical:
352                         let wm = context.builder.writing_mode;
353                         <%helpers:logical_setter_helper name="${prop.name}">
354                         <%def name="inner(physical_ident)">
355                             AnimationValue::${to_camel_case(physical_ident)}(computed)
356                         </%def>
357                         </%helpers:logical_setter_helper>
358                         % else:
359                             AnimationValue::${prop.camel_case}(computed)
360                         % endif
361                     },
362                     % endif
363                     % endfor
364                     % for prop in data.longhands:
365                     % if not prop.animatable:
366                     LonghandId::${prop.camel_case} => return None,
367                     % endif
368                     % endfor
369                 }
370             },
371             PropertyDeclaration::WithVariables(ref declaration) => {
372                 let mut cache = Default::default();
373                 let substituted = {
374                     let custom_properties =
375                         extra_custom_properties.or_else(|| context.style().custom_properties());
376 
377                     declaration.value.substitute_variables(
378                         declaration.id,
379                         context.builder.writing_mode,
380                         custom_properties,
381                         context.quirks_mode,
382                         context.device(),
383                         &mut cache,
384                     )
385                 };
386                 return AnimationValue::from_declaration(
387                     &substituted,
388                     context,
389                     extra_custom_properties,
390                     initial,
391                 )
392             },
393             _ => return None // non animatable properties will get included because of shorthands. ignore.
394         };
395         Some(animatable)
396     }
397 
398     /// Get an AnimationValue for an AnimatableLonghand from a given computed values.
from_computed_values( property: LonghandId, style: &ComputedValues, ) -> Option<Self>399     pub fn from_computed_values(
400         property: LonghandId,
401         style: &ComputedValues,
402     ) -> Option<Self> {
403         let property = property.to_physical(style.writing_mode);
404         Some(match property {
405             % for prop in data.longhands:
406             % if prop.animatable and not prop.logical:
407             LonghandId::${prop.camel_case} => {
408                 let computed = style.clone_${prop.ident}();
409                 AnimationValue::${prop.camel_case}(
410                 % if prop.is_animatable_with_computed_value:
411                     computed
412                 % else:
413                     computed.to_animated_value()
414                 % endif
415                 )
416             }
417             % endif
418             % endfor
419             _ => return None,
420         })
421     }
422 
423     /// Update `style` with the value of this `AnimationValue`.
424     ///
425     /// SERVO ONLY: This doesn't properly handle things like updating 'em' units
426     /// when animated font-size.
427     #[cfg(feature = "servo")]
set_in_style_for_servo(&self, style: &mut ComputedValues)428     pub fn set_in_style_for_servo(&self, style: &mut ComputedValues) {
429         match self {
430             % for prop in data.longhands:
431             % if prop.animatable and not prop.logical:
432             AnimationValue::${prop.camel_case}(ref value) => {
433                 % if not prop.is_animatable_with_computed_value:
434                 let value: longhands::${prop.ident}::computed_value::T =
435                     ToAnimatedValue::from_animated_value(value.clone());
436                     style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value);
437                 % else:
438                     style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value.clone());
439                 % endif
440             }
441             % else:
442             AnimationValue::${prop.camel_case}(..) => unreachable!(),
443             % endif
444             % endfor
445         }
446     }
447 
448     /// As above, but a stub for Gecko.
449     #[cfg(feature = "gecko")]
set_in_style_for_servo(&self, _: &mut ComputedValues)450     pub fn set_in_style_for_servo(&self, _: &mut ComputedValues) {
451     }
452 }
453 
animate_discrete<T: Clone>(this: &T, other: &T, procedure: Procedure) -> Result<T, ()>454 fn animate_discrete<T: Clone>(this: &T, other: &T, procedure: Procedure) -> Result<T, ()> {
455     if let Procedure::Interpolate { progress } = procedure {
456         Ok(if progress < 0.5 { this.clone() } else { other.clone() })
457     } else {
458         Err(())
459     }
460 }
461 
462 impl Animate for AnimationValue {
animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>463     fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
464         Ok(unsafe {
465             use self::AnimationValue::*;
466 
467             let this_tag = *(self as *const _ as *const u16);
468             let other_tag = *(other as *const _ as *const u16);
469             if this_tag != other_tag {
470                 panic!("Unexpected AnimationValue::animate call");
471             }
472 
473             match *self {
474                 <% keyfunc = lambda x: (x.animated_type(), x.animation_value_type == "discrete") %>
475                 % for (ty, discrete), props in groupby(animated, key=keyfunc):
476                 ${" |\n".join("{}(ref this)".format(prop.camel_case) for prop in props)} => {
477                     let other_repr =
478                         &*(other as *const _ as *const AnimationValueVariantRepr<${ty}>);
479                     % if discrete:
480                     let value = animate_discrete(this, &other_repr.value, procedure)?;
481                     % else:
482                     let value = this.animate(&other_repr.value, procedure)?;
483                     % endif
484 
485                     let mut out = mem::MaybeUninit::uninit();
486                     ptr::write(
487                         out.as_mut_ptr() as *mut AnimationValueVariantRepr<${ty}>,
488                         AnimationValueVariantRepr {
489                             tag: this_tag,
490                             value,
491                         },
492                     );
493                     out.assume_init()
494                 }
495                 % endfor
496                 ${" |\n".join("{}(void)".format(prop.camel_case) for prop in unanimated)} => {
497                     void::unreachable(void)
498                 }
499             }
500         })
501     }
502 }
503 
504 <%
505     nondiscrete = []
506     for prop in animated:
507         if prop.animation_value_type != "discrete":
508             nondiscrete.append(prop)
509 %>
510 
511 impl ComputeSquaredDistance for AnimationValue {
compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()>512     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
513         unsafe {
514             use self::AnimationValue::*;
515 
516             let this_tag = *(self as *const _ as *const u16);
517             let other_tag = *(other as *const _ as *const u16);
518             if this_tag != other_tag {
519                 panic!("Unexpected AnimationValue::compute_squared_distance call");
520             }
521 
522             match *self {
523                 % for ty, props in groupby(nondiscrete, key=lambda x: x.animated_type()):
524                 ${" |\n".join("{}(ref this)".format(prop.camel_case) for prop in props)} => {
525                     let other_repr =
526                         &*(other as *const _ as *const AnimationValueVariantRepr<${ty}>);
527 
528                     this.compute_squared_distance(&other_repr.value)
529                 }
530                 % endfor
531                 _ => Err(()),
532             }
533         }
534     }
535 }
536 
537 impl ToAnimatedZero for AnimationValue {
538     #[inline]
to_animated_zero(&self) -> Result<Self, ()>539     fn to_animated_zero(&self) -> Result<Self, ()> {
540         match *self {
541             % for prop in data.longhands:
542             % if prop.animatable and not prop.logical and prop.animation_value_type != "discrete":
543             AnimationValue::${prop.camel_case}(ref base) => {
544                 Ok(AnimationValue::${prop.camel_case}(base.to_animated_zero()?))
545             },
546             % endif
547             % endfor
548             _ => Err(()),
549         }
550     }
551 }
552 
553 /// A trait to abstract away the different kind of animations over a list that
554 /// there may be.
555 pub trait ListAnimation<T> : Sized {
556     /// <https://drafts.csswg.org/css-transitions/#animtype-repeatable-list>
animate_repeatable_list(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> where T: Animate557     fn animate_repeatable_list(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>
558     where
559         T: Animate;
560 
561     /// <https://drafts.csswg.org/css-transitions/#animtype-repeatable-list>
squared_distance_repeatable_list(&self, other: &Self) -> Result<SquaredDistance, ()> where T: ComputeSquaredDistance562     fn squared_distance_repeatable_list(&self, other: &Self) -> Result<SquaredDistance, ()>
563     where
564         T: ComputeSquaredDistance;
565 
566     /// This is the animation used for some of the types like shadows and
567     /// filters, where the interpolation happens with the zero value if one of
568     /// the sides is not present.
animate_with_zero(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> where T: Animate + Clone + ToAnimatedZero569     fn animate_with_zero(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>
570     where
571         T: Animate + Clone + ToAnimatedZero;
572 
573     /// This is the animation used for some of the types like shadows and
574     /// filters, where the interpolation happens with the zero value if one of
575     /// the sides is not present.
squared_distance_with_zero(&self, other: &Self) -> Result<SquaredDistance, ()> where T: ToAnimatedZero + ComputeSquaredDistance576     fn squared_distance_with_zero(&self, other: &Self) -> Result<SquaredDistance, ()>
577     where
578         T: ToAnimatedZero + ComputeSquaredDistance;
579 }
580 
581 macro_rules! animated_list_impl {
582     (<$t:ident> for $ty:ty) => {
583         impl<$t> ListAnimation<$t> for $ty {
584             fn animate_repeatable_list(
585                 &self,
586                 other: &Self,
587                 procedure: Procedure,
588             ) -> Result<Self, ()>
589             where
590                 T: Animate,
591             {
592                 // If the length of either list is zero, the least common multiple is undefined.
593                 if self.is_empty() || other.is_empty() {
594                     return Err(());
595                 }
596                 use num_integer::lcm;
597                 let len = lcm(self.len(), other.len());
598                 self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(this, other)| {
599                     this.animate(other, procedure)
600                 }).collect()
601             }
602 
603             fn squared_distance_repeatable_list(
604                 &self,
605                 other: &Self,
606             ) -> Result<SquaredDistance, ()>
607             where
608                 T: ComputeSquaredDistance,
609             {
610                 if self.is_empty() || other.is_empty() {
611                     return Err(());
612                 }
613                 use num_integer::lcm;
614                 let len = lcm(self.len(), other.len());
615                 self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(this, other)| {
616                     this.compute_squared_distance(other)
617                 }).sum()
618             }
619 
620             fn animate_with_zero(
621                 &self,
622                 other: &Self,
623                 procedure: Procedure,
624             ) -> Result<Self, ()>
625             where
626                 T: Animate + Clone + ToAnimatedZero
627             {
628                 if procedure == Procedure::Add {
629                     return Ok(
630                         self.iter().chain(other.iter()).cloned().collect()
631                     );
632                 }
633                 self.iter().zip_longest(other.iter()).map(|it| {
634                     match it {
635                         EitherOrBoth::Both(this, other) => {
636                             this.animate(other, procedure)
637                         },
638                         EitherOrBoth::Left(this) => {
639                             this.animate(&this.to_animated_zero()?, procedure)
640                         },
641                         EitherOrBoth::Right(other) => {
642                             other.to_animated_zero()?.animate(other, procedure)
643                         }
644                     }
645                 }).collect()
646             }
647 
648             fn squared_distance_with_zero(
649                 &self,
650                 other: &Self,
651             ) -> Result<SquaredDistance, ()>
652             where
653                 T: ToAnimatedZero + ComputeSquaredDistance
654             {
655                 self.iter().zip_longest(other.iter()).map(|it| {
656                     match it {
657                         EitherOrBoth::Both(this, other) => {
658                             this.compute_squared_distance(other)
659                         },
660                         EitherOrBoth::Left(list) | EitherOrBoth::Right(list) => {
661                             list.to_animated_zero()?.compute_squared_distance(list)
662                         },
663                     }
664                 }).sum()
665             }
666         }
667     }
668 }
669 
670 animated_list_impl!(<T> for crate::OwnedSlice<T>);
671 animated_list_impl!(<T> for SmallVec<[T; 1]>);
672 animated_list_impl!(<T> for Vec<T>);
673 
674 /// <https://drafts.csswg.org/web-animations-1/#animating-visibility>
675 impl Animate for Visibility {
676     #[inline]
animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>677     fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
678         match procedure {
679             Procedure::Interpolate { .. } => {
680                 let (this_weight, other_weight) = procedure.weights();
681                 match (*self, *other) {
682                     (Visibility::Visible, _) => {
683                         Ok(if this_weight > 0.0 { *self } else { *other })
684                     },
685                     (_, Visibility::Visible) => {
686                         Ok(if other_weight > 0.0 { *other } else { *self })
687                     },
688                     _ => Err(()),
689                 }
690             },
691             _ => Err(()),
692         }
693     }
694 }
695 
696 impl ComputeSquaredDistance for Visibility {
697     #[inline]
compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()>698     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
699         Ok(SquaredDistance::from_sqrt(if *self == *other { 0. } else { 1. }))
700     }
701 }
702 
703 impl ToAnimatedZero for Visibility {
704     #[inline]
to_animated_zero(&self) -> Result<Self, ()>705     fn to_animated_zero(&self) -> Result<Self, ()> {
706         Err(())
707     }
708 }
709 
710 /// <https://drafts.csswg.org/css-transitions/#animtype-rect>
711 impl Animate for ClipRect {
712     #[inline]
animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>713     fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
714         use crate::values::computed::LengthOrAuto;
715         let animate_component = |this: &LengthOrAuto, other: &LengthOrAuto| {
716             let result = this.animate(other, procedure)?;
717             if let Procedure::Interpolate { .. } = procedure {
718                 return Ok(result);
719             }
720             if result.is_auto() {
721                 // FIXME(emilio): Why? A couple SMIL tests fail without this,
722                 // but it seems extremely fishy.
723                 return Err(());
724             }
725             Ok(result)
726         };
727 
728         Ok(ClipRect {
729             top: animate_component(&self.top, &other.top)?,
730             right: animate_component(&self.right, &other.right)?,
731             bottom: animate_component(&self.bottom, &other.bottom)?,
732             left: animate_component(&self.left, &other.left)?,
733         })
734     }
735 }
736 
737 <%
738     FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
739                          'HueRotate', 'Invert', 'Opacity', 'Saturate',
740                          'Sepia' ]
741 %>
742 
743 /// <https://drafts.fxtf.org/filters/#animation-of-filters>
744 impl Animate for AnimatedFilter {
animate( &self, other: &Self, procedure: Procedure, ) -> Result<Self, ()>745     fn animate(
746         &self,
747         other: &Self,
748         procedure: Procedure,
749     ) -> Result<Self, ()> {
750         use crate::values::animated::animate_multiplicative_factor;
751         match (self, other) {
752             % for func in ['Blur', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']:
753             (&Filter::${func}(ref this), &Filter::${func}(ref other)) => {
754                 Ok(Filter::${func}(this.animate(other, procedure)?))
755             },
756             % endfor
757             % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:
758             (&Filter::${func}(this), &Filter::${func}(other)) => {
759                 Ok(Filter::${func}(animate_multiplicative_factor(this, other, procedure)?))
760             },
761             % endfor
762             % if engine == "gecko":
763             (&Filter::DropShadow(ref this), &Filter::DropShadow(ref other)) => {
764                 Ok(Filter::DropShadow(this.animate(other, procedure)?))
765             },
766             % endif
767             _ => Err(()),
768         }
769     }
770 }
771 
772 /// <http://dev.w3.org/csswg/css-transforms/#none-transform-animation>
773 impl ToAnimatedZero for AnimatedFilter {
to_animated_zero(&self) -> Result<Self, ()>774     fn to_animated_zero(&self) -> Result<Self, ()> {
775         match *self {
776             % for func in ['Blur', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']:
777             Filter::${func}(ref this) => Ok(Filter::${func}(this.to_animated_zero()?)),
778             % endfor
779             % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:
780             Filter::${func}(_) => Ok(Filter::${func}(1.)),
781             % endfor
782             % if engine == "gecko":
783             Filter::DropShadow(ref this) => Ok(Filter::DropShadow(this.to_animated_zero()?)),
784             % endif
785             _ => Err(()),
786         }
787     }
788 }
789 
790 /// An iterator over all the properties that transition on a given style.
791 pub struct TransitionPropertyIterator<'a> {
792     style: &'a ComputedValues,
793     index_range: core::ops::Range<usize>,
794     longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,
795 }
796 
797 impl<'a> TransitionPropertyIterator<'a> {
798     /// Create a `TransitionPropertyIterator` for the given style.
from_style(style: &'a ComputedValues) -> Self799     pub fn from_style(style: &'a ComputedValues) -> Self {
800         Self {
801             style,
802             index_range: 0..style.get_box().transition_property_count(),
803             longhand_iterator: None,
804         }
805     }
806 }
807 
808 /// A single iteration of the TransitionPropertyIterator.
809 pub struct TransitionPropertyIteration {
810     /// The id of the longhand for this property.
811     pub longhand_id: LonghandId,
812 
813     /// The index of this property in the list of transition properties for this
814     /// iterator's style.
815     pub index: usize,
816 }
817 
818 impl<'a> Iterator for TransitionPropertyIterator<'a> {
819     type Item = TransitionPropertyIteration;
820 
next(&mut self) -> Option<Self::Item>821     fn next(&mut self) -> Option<Self::Item> {
822         use crate::values::computed::TransitionProperty;
823         loop {
824             if let Some(ref mut longhand_iterator) = self.longhand_iterator {
825                 if let Some(longhand_id) = longhand_iterator.next() {
826                     return Some(TransitionPropertyIteration {
827                         longhand_id,
828                         index: self.index_range.start - 1,
829                     });
830                 }
831                 self.longhand_iterator = None;
832             }
833 
834             let index = self.index_range.next()?;
835             match self.style.get_box().transition_property_at(index) {
836                 TransitionProperty::Longhand(longhand_id) => {
837                     return Some(TransitionPropertyIteration {
838                         longhand_id,
839                         index,
840                     })
841                 }
842                 // In the other cases, we set up our state so that we are ready to
843                 // compute the next value of the iterator and then loop (equivalent
844                 // to calling self.next()).
845                 TransitionProperty::Shorthand(ref shorthand_id) =>
846                     self.longhand_iterator = Some(shorthand_id.longhands()),
847                 TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => {}
848             }
849         }
850     }
851 }
852