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