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