From 9b504cb7496e39c4bdcd106ff1f9107f7c04980e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 6 Jul 2018 05:19:10 +0200 Subject: [PATCH] Bug 1309752: Animate logical properties. r=birtles The setup is that AnimationValue only contains physical properties, and we physicalize when building keyframes and transitions. MozReview-Commit-ID: 9dI20N0LFrk --- layout/style/ServoBindingList.h | 1 + layout/style/ServoBindings.cpp | 4 +- layout/style/ServoStyleSet.cpp | 2 + layout/style/ServoStyleSet.h | 3 +- layout/style/nsAnimationManager.cpp | 26 +-- layout/style/nsTransitionManager.cpp | 17 +- servo/components/style/gecko/wrapper.rs | 4 + servo/components/style/properties/data.py | 6 - .../style/properties/declaration_block.rs | 21 ++- .../helpers/animated_properties.mako.rs | 149 ++++++++++++------ servo/ports/geckolib/glue.rs | 29 +++- 11 files changed, 177 insertions(+), 85 deletions(-) diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index bf46a663dc2d..0d65bab3f431 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -136,6 +136,7 @@ SERVO_BINDING_FUNC(Servo_StyleSet_NoteStyleSheetsChanged, void, SERVO_BINDING_FUNC(Servo_StyleSet_GetKeyframesForName, bool, RawServoStyleSetBorrowed set, RawGeckoElementBorrowed element, + ComputedStyleBorrowed style, nsAtom* name, nsTimingFunctionBorrowed timing_function, RawGeckoKeyframeListBorrowedMut keyframe_list) diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index 5e8c12956824..b81bdc65a685 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -1976,7 +1976,9 @@ Gecko_AppendPropertyValuePair(nsTArray* aProperties, nsCSSPropertyID aProperty) { MOZ_ASSERT(aProperties); - return aProperties->AppendElement(PropertyValuePair {aProperty}); + MOZ_ASSERT(aProperty == eCSSPropertyExtra_variable || + !nsCSSProps::PropHasFlags(aProperty, CSSPropFlags::IsLogical)); + return aProperties->AppendElement(PropertyValuePair { aProperty }); } void diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index c547e2607fe6..8b1396b8688f 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -1159,6 +1159,7 @@ ServoStyleSet::AssertTreeIsClean() bool ServoStyleSet::GetKeyframesForName(const Element& aElement, + const ComputedStyle& aStyle, nsAtom* aName, const nsTimingFunction& aTimingFunction, nsTArray& aKeyframes) @@ -1166,6 +1167,7 @@ ServoStyleSet::GetKeyframesForName(const Element& aElement, MOZ_ASSERT(!StylistNeedsUpdate()); return Servo_StyleSet_GetKeyframesForName(mRawSet.get(), &aElement, + &aStyle, aName, &aTimingFunction, &aKeyframes); diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h index ba021607049f..690cf50e5b4f 100644 --- a/layout/style/ServoStyleSet.h +++ b/layout/style/ServoStyleSet.h @@ -336,7 +336,8 @@ public: inline already_AddRefed ResolveServoStyle(dom::Element* aElement); - bool GetKeyframesForName(const dom::Element& aElement, + bool GetKeyframesForName(const dom::Element&, + const ComputedStyle&, nsAtom* aName, const nsTimingFunction& aTimingFunction, nsTArray& aKeyframes); diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp index 968878163d8a..36477314faf0 100755 --- a/layout/style/nsAnimationManager.cpp +++ b/layout/style/nsAnimationManager.cpp @@ -360,12 +360,12 @@ public: const nsTimingFunction& aTimingFunction, nsTArray& aKeyframes) { - ServoStyleSet* styleSet = aPresContext->StyleSet(); - MOZ_ASSERT(styleSet); - return styleSet->GetKeyframesForName(aElement, - aName, - aTimingFunction, - aKeyframes); + return aPresContext->StyleSet()->GetKeyframesForName( + aElement, + *mComputedStyle, + aName, + aTimingFunction, + aKeyframes); } void SetKeyframes(KeyframeEffect& aEffect, nsTArray&& aKeyframes) { @@ -647,13 +647,13 @@ nsAnimationManager::DoUpdateAnimations( // Build the updated animations list, extracting matching animations from // the existing collection as we go. - OwningCSSAnimationPtrArray newAnimations; - newAnimations = BuildAnimations(mPresContext, - aTarget, - aStyleDisplay, - aBuilder, - collection, - mMaybeReferencedAnimations); + OwningCSSAnimationPtrArray newAnimations = + BuildAnimations(mPresContext, + aTarget, + aStyleDisplay, + aBuilder, + collection, + mMaybeReferencedAnimations); if (newAnimations.IsEmpty()) { if (collection) { diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index 3af2b81eb45a..1d06ddd4073c 100755 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -544,15 +544,18 @@ nsTransitionManager::DoUpdateTransitions( for (nsCSSPropertyID p = nsCSSPropertyID(0); p < eCSSProperty_COUNT_no_shorthands; p = nsCSSPropertyID(p + 1)) { + p = nsCSSProps::Physicalize(p, aNewStyle); allTransitionProperties.AddProperty(p); } } else if (nsCSSProps::IsShorthand(property)) { CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES( subprop, property, CSSEnabledState::eForAllContent) { - allTransitionProperties.AddProperty(*subprop); + auto p = nsCSSProps::Physicalize(*subprop, aNewStyle); + allTransitionProperties.AddProperty(p); } } else { - allTransitionProperties.AddProperty(property); + allTransitionProperties.AddProperty( + nsCSSProps::Physicalize(property, aNewStyle)); } } } @@ -656,13 +659,15 @@ nsTransitionManager::ConsiderInitiatingTransition( nsCSSPropertyIDSet& aPropertiesChecked) { // IsShorthand itself will assert if aProperty is not a property. - MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), - "property out of range"); + MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), "property out of range"); NS_ASSERTION(!aElementTransitions || aElementTransitions->mElement == aElement, "Element mismatch"); - // A later item in transition-property already specified a transition for this - // property, so we ignore this one. + aProperty = nsCSSProps::Physicalize(aProperty, aNewStyle); + + // A later item in transition-property already specified a transition for + // this property, so we ignore this one. + // // See http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html . if (aPropertiesChecked.HasProperty(aProperty)) { return false; diff --git a/servo/components/style/gecko/wrapper.rs b/servo/components/style/gecko/wrapper.rs index ee0c134b269c..00a7dcdb2c4c 100644 --- a/servo/components/style/gecko/wrapper.rs +++ b/servo/components/style/gecko/wrapper.rs @@ -882,6 +882,7 @@ impl<'le> GeckoElement<'le> { .expect("AnimationValue not found in ElementTransitions"); let property = end_value.id(); + debug_assert!(!property.is_logical()); map.insert(property, end_value.clone_arc()); } map @@ -896,6 +897,7 @@ impl<'le> GeckoElement<'le> { existing_transitions: &FnvHashMap>, ) -> bool { use values::animated::{Animate, Procedure}; + debug_assert!(!longhand_id.is_logical()); // If there is an existing transition, update only if the end value // differs. @@ -1657,6 +1659,8 @@ impl<'le> TElement for GeckoElement<'le> { let transition_property: TransitionProperty = property.into(); let mut property_check_helper = |property: LonghandId| -> bool { + let property = + property.to_physical(after_change_style.writing_mode); transitions_to_keep.insert(property); self.needs_transitions_update_per_property( property, diff --git a/servo/components/style/properties/data.py b/servo/components/style/properties/data.py index c33eeb7b39ca..ea0516d803ef 100644 --- a/servo/components/style/properties/data.py +++ b/servo/components/style/properties/data.py @@ -223,12 +223,6 @@ class Longhand(object): and animation_value_type != "discrete" self.is_animatable_with_computed_value = animation_value_type == "ComputedValue" \ or animation_value_type == "discrete" - if self.logical: - # Logical properties will be animatable (i.e. the animation type is - # discrete). For now, it is still non-animatable. - self.animatable = False - self.transitionable = False - self.animation_value_type = None # See compute_damage for the various values this can take self.servo_restyle_damage = servo_restyle_damage diff --git a/servo/components/style/properties/declaration_block.rs b/servo/components/style/properties/declaration_block.rs index 29277eef1d81..7f6fc15bde96 100644 --- a/servo/components/style/properties/declaration_block.rs +++ b/servo/components/style/properties/declaration_block.rs @@ -131,12 +131,15 @@ impl<'a> DoubleEndedIterator for DeclarationImportanceIterator<'a> { } /// Iterator over `PropertyDeclaration` for Importance::Normal. +/// +/// TODO(emilio): This should be replaced by `impl Trait`, returning a +/// filter()ed iterator when available instead, and all the boilerplate below +/// should go. pub struct NormalDeclarationIterator<'a>(DeclarationImportanceIterator<'a>); impl<'a> NormalDeclarationIterator<'a> { - /// Constructor. #[inline] - pub fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self { + fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self { NormalDeclarationIterator( DeclarationImportanceIterator::new(declarations, important) ) @@ -146,6 +149,7 @@ impl<'a> NormalDeclarationIterator<'a> { impl<'a> Iterator for NormalDeclarationIterator<'a> { type Item = &'a PropertyDeclaration; + #[inline] fn next(&mut self) -> Option { loop { let (decl, importance) = self.0.iter.next()?; @@ -155,11 +159,24 @@ impl<'a> Iterator for NormalDeclarationIterator<'a> { } } + #[inline] fn size_hint(&self) -> (usize, Option) { self.0.iter.size_hint() } } +impl<'a> DoubleEndedIterator for NormalDeclarationIterator<'a> { + #[inline] + fn next_back(&mut self) -> Option { + loop { + let (decl, importance) = self.0.iter.next_back()?; + if !importance { + return Some(decl); + } + } + } +} + /// Iterator for AnimationValue to be generated from PropertyDeclarationBlock. pub struct AnimationValueIterator<'a, 'cx, 'cx_a:'cx> { iter: NormalDeclarationIterator<'a>, diff --git a/servo/components/style/properties/helpers/animated_properties.mako.rs b/servo/components/style/properties/helpers/animated_properties.mako.rs index a51860f9a199..d00f1947d865 100644 --- a/servo/components/style/properties/helpers/animated_properties.mako.rs +++ b/servo/components/style/properties/helpers/animated_properties.mako.rs @@ -5,7 +5,7 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> <% - from data import to_idl_name, SYSTEM_FONT_LONGHANDS + from data import to_idl_name, SYSTEM_FONT_LONGHANDS, to_camel_case from itertools import groupby %> @@ -114,7 +114,7 @@ pub fn nscsspropertyid_is_transitionable(property: nsCSSPropertyID) -> bool { #[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub enum AnimatedProperty { % for prop in data.longhands: - % if prop.animatable: + % if prop.animatable and not prop.logical: <% value_type = "longhands::{}::computed_value::T".format(prop.ident) if not prop.is_animatable_with_computed_value: @@ -131,7 +131,7 @@ impl AnimatedProperty { pub fn name(&self) -> &'static str { match *self { % for prop in data.longhands: - % if prop.animatable: + % if prop.animatable and not prop.logical: AnimatedProperty::${prop.camel_case}(..) => "${prop.name}", % endif % endfor @@ -143,7 +143,7 @@ impl AnimatedProperty { pub fn does_animate(&self) -> bool { match *self { % for prop in data.longhands: - % if prop.animatable: + % if prop.animatable and not prop.logical: AnimatedProperty::${prop.camel_case}(ref from, ref to) => from != to, % endif % endfor @@ -154,7 +154,7 @@ impl AnimatedProperty { pub fn has_the_same_end_value_as(&self, other: &Self) -> bool { match (self, other) { % for prop in data.longhands: - % if prop.animatable: + % if prop.animatable and not prop.logical: (&AnimatedProperty::${prop.camel_case}(_, ref this_end_value), &AnimatedProperty::${prop.camel_case}(_, ref other_end_value)) => { this_end_value == other_end_value @@ -173,7 +173,7 @@ impl AnimatedProperty { { match *self { % for prop in data.longhands: - % if prop.animatable: + % if prop.animatable and not prop.logical: AnimatedProperty::${prop.camel_case}(ref from, ref to) => { // https://drafts.csswg.org/web-animations/#discrete-animation-type % if prop.animation_value_type == "discrete": @@ -203,9 +203,12 @@ impl AnimatedProperty { old_style: &ComputedValues, new_style: &ComputedValues, ) -> Option { + // FIXME(emilio): Handle the case where old_style and new_style's + // writing mode differ. + let property = property.to_physical(new_style.writing_mode); Some(match property { % for prop in data.longhands: - % if prop.animatable: + % if prop.animatable and not prop.logical: LonghandId::${prop.camel_case} => { let old_computed = old_style.clone_${prop.ident}(); let new_computed = new_style.clone_${prop.ident}(); @@ -254,11 +257,10 @@ unsafe impl HasSimpleFFI for AnimationValueMap {} #[repr(u16)] pub enum AnimationValue { % for prop in data.longhands: - % if prop.animatable: /// `${prop.name}` + % if prop.animatable and not prop.logical: ${prop.camel_case}(${prop.animated_type()}), % else: - /// `${prop.name}` (not animatable) ${prop.camel_case}(Void), % endif % endfor @@ -267,8 +269,11 @@ pub enum AnimationValue { <% animated = [] unanimated = [] + animated_with_logical = [] for prop in data.longhands: if prop.animatable: + animated_with_logical.append(prop) + if prop.animatable and not prop.logical: animated.append(prop) else: unanimated.append(prop) @@ -370,7 +375,7 @@ impl AnimationValue { let id = unsafe { *(self as *const _ as *const LonghandId) }; debug_assert_eq!(id, match *self { % for prop in data.longhands: - % if prop.animatable: + % if prop.animatable and not prop.logical: AnimationValue::${prop.camel_case}(..) => LonghandId::${prop.camel_case}, % else: AnimationValue::${prop.camel_case}(void) => void::unreachable(void), @@ -444,17 +449,18 @@ impl AnimationValue { %> let animatable = match *decl { - % for (specified_ty, ty, boxed, to_animated, inherit, system), props in groupby(animated, key=keyfunc): + % for (specified_ty, ty, boxed, to_animated, inherit, system), props in groupby(animated_with_logical, key=keyfunc): ${" |\n".join("PropertyDeclaration::{}(ref value)".format(prop.camel_case) for prop in props)} => { let decl_repr = unsafe { &*(decl as *const _ as *const PropertyDeclarationVariantRepr<${specified_ty}>) }; + let longhand_id = unsafe { + *(&decl_repr.tag as *const u16 as *const LonghandId) + }; % if inherit: context.for_non_inherited_property = None; % else: - context.for_non_inherited_property = unsafe { - Some(*(&decl_repr.tag as *const u16 as *const LonghandId)) - }; + context.for_non_inherited_property = Some(longhand_id); % endif % if system: if let Some(sf) = value.get_system() { @@ -475,7 +481,7 @@ impl AnimationValue { ptr::write( &mut out as *mut _ as *mut AnimationValueVariantRepr<${ty}>, AnimationValueVariantRepr { - tag: decl_repr.tag, + tag: longhand_id.to_physical(context.builder.writing_mode) as u16, value, }, ); @@ -492,24 +498,40 @@ impl AnimationValue { LonghandId::${prop.camel_case} => { let style_struct = match declaration.keyword { % if not prop.style_struct.inherited: - CSSWideKeyword::Unset | + CSSWideKeyword::Unset | % endif CSSWideKeyword::Initial => { initial.get_${prop.style_struct.name_lower}() }, % if prop.style_struct.inherited: - CSSWideKeyword::Unset | + CSSWideKeyword::Unset | % endif CSSWideKeyword::Inherit => { context.builder .get_parent_${prop.style_struct.name_lower}() }, }; - let computed = style_struct.clone_${prop.ident}(); + let computed = style_struct + % if prop.logical: + .clone_${prop.ident}(context.builder.writing_mode); + % else: + .clone_${prop.ident}(); + % endif + % if not prop.is_animatable_with_computed_value: let computed = computed.to_animated_value(); % endif - AnimationValue::${prop.camel_case}(computed) + + % if prop.logical: + let wm = context.builder.writing_mode; + <%helpers:logical_setter_helper name="${prop.name}"> + <%def name="inner(physical_ident)"> + AnimationValue::${to_camel_case(physical_ident)}(computed) + + + % else: + AnimationValue::${prop.camel_case}(computed) + % endif }, % endif % endfor @@ -548,9 +570,10 @@ impl AnimationValue { property: LonghandId, style: &ComputedValues, ) -> Option { + let property = property.to_physical(style.writing_mode); Some(match property { % for prop in data.longhands: - % if prop.animatable: + % if prop.animatable and not prop.logical: LonghandId::${prop.camel_case} => { let computed = style.clone_${prop.ident}(); AnimationValue::${prop.camel_case}( @@ -656,7 +679,7 @@ impl ToAnimatedZero for AnimationValue { fn to_animated_zero(&self) -> Result { match *self { % for prop in data.longhands: - % if prop.animatable and prop.animation_value_type != "discrete": + % if prop.animatable and not prop.logical and prop.animation_value_type != "discrete": AnimationValue::${prop.camel_case}(ref base) => { Ok(AnimationValue::${prop.camel_case}(base.to_animated_zero()?)) }, @@ -2924,43 +2947,67 @@ impl ToAnimatedZero for AnimatedFilter { } } -/// A comparator to sort PropertyIds such that longhands are sorted before shorthands, -/// shorthands with fewer components are sorted before shorthands with more components, -/// and otherwise shorthands are sorted by IDL name as defined by [Web Animations][property-order]. +/// The category a property falls into for ordering purposes. +/// +/// https://drafts.csswg.org/web-animations/#calculating-computed-keyframes +/// +#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] +enum PropertyCategory { + Custom, + PhysicalLonghand, + LogicalLonghand, + Shorthand, +} + +impl PropertyCategory { + fn of(id: &PropertyId) -> Self { + match *id { + PropertyId::Shorthand(..) | + PropertyId::ShorthandAlias(..) => PropertyCategory::Shorthand, + PropertyId::Longhand(id) | + PropertyId::LonghandAlias(id, ..) => { + if id.is_logical() { + PropertyCategory::LogicalLonghand + } else { + PropertyCategory::PhysicalLonghand + } + } + PropertyId::Custom(..) => PropertyCategory::Custom, + } + } +} + +/// A comparator to sort PropertyIds such that physical longhands are sorted +/// before logical longhands and shorthands, shorthands with fewer components +/// are sorted before shorthands with more components, and otherwise shorthands +/// are sorted by IDL name as defined by [Web Animations][property-order]. /// /// Using this allows us to prioritize values specified by longhands (or smaller -/// shorthand subsets) when longhands and shorthands are both specified on the one keyframe. -/// -/// Example orderings that result from this: -/// -/// margin-left, margin -/// -/// and: -/// -/// border-top-color, border-color, border-top, border +/// shorthand subsets) when longhands and shorthands are both specified on the +/// one keyframe. /// /// [property-order] https://drafts.csswg.org/web-animations/#calculating-computed-keyframes pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering { - match (a.as_shorthand(), b.as_shorthand()) { - // Within shorthands, sort by the number of subproperties, then by IDL name. - (Ok(a), Ok(b)) => { - let subprop_count_a = a.longhands().count(); - let subprop_count_b = b.longhands().count(); - subprop_count_a - .cmp(&subprop_count_b) - .then_with(|| { - get_idl_name_sort_order(a).cmp(&get_idl_name_sort_order(b)) - }) - }, + let a_category = PropertyCategory::of(a); + let b_category = PropertyCategory::of(b); - // Longhands go before shorthands. - (Ok(_), Err(_)) => cmp::Ordering::Greater, - (Err(_), Ok(_)) => cmp::Ordering::Less, - - // Both are longhands or custom properties in which case they don't overlap and should - // sort equally. - _ => cmp::Ordering::Equal, + if a_category != b_category { + return a_category.cmp(&b_category); } + + if a_category == PropertyCategory::Shorthand { + let a = a.as_shorthand().unwrap(); + let b = b.as_shorthand().unwrap(); + // Within shorthands, sort by the number of subproperties, then by IDL + // name. + let subprop_count_a = a.longhands().count(); + let subprop_count_b = b.longhands().count(); + return subprop_count_a.cmp(&subprop_count_b).then_with(|| { + get_idl_name_sort_order(a).cmp(&get_idl_name_sort_order(b)) + }); + } + + cmp::Ordering::Equal } fn get_idl_name_sort_order(shorthand: ShorthandId) -> u32 { diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs index 0f7673365b50..3933e17dbf27 100644 --- a/servo/ports/geckolib/glue.rs +++ b/servo/ports/geckolib/glue.rs @@ -4675,6 +4675,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues( } let mut maybe_append_animation_value = |property: LonghandId, value: Option| { + debug_assert!(!property.is_logical()); if seen.contains(property) { return; } @@ -4881,6 +4882,7 @@ fn fill_in_missing_keyframe_values( pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName( raw_data: RawServoStyleSetBorrowed, element: RawGeckoElementBorrowed, + style: ComputedStyleBorrowed, name: *mut nsAtom, inherited_timing_function: nsTimingFunctionBorrowed, keyframes: RawGeckoKeyframeListBorrowedMut, @@ -4906,6 +4908,8 @@ pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName( let mut has_complete_final_keyframe = false; let mut current_offset = -1.; + let writing_mode = style.writing_mode; + // Iterate over the keyframe rules backwards so we can drop overridden // properties (since declarations in later rules override those in earlier // ones). @@ -4937,7 +4941,14 @@ pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName( // to represent that all properties animated by the keyframes // animation should be set to the underlying computed value for // that keyframe. + let mut seen = LonghandIdSet::new(); for property in animation.properties_changed.iter() { + let property = property.to_physical(writing_mode); + if seen.contains(property) { + continue; + } + seen.insert(property); + Gecko_AppendPropertyValuePair( &mut (*keyframe).mPropertyValues, property.to_nscsspropertyid(), @@ -4956,7 +4967,10 @@ pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName( // Filter out non-animatable properties and properties with // !important. - for declaration in guard.normal_declaration_iter() { + // + // Also, iterate in reverse to respect the source order in case + // there are logical and physical longhands in the same block. + for declaration in guard.normal_declaration_iter().rev() { let id = declaration.id(); let id = match id { @@ -4972,7 +4986,7 @@ pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName( continue; } - id + id.to_physical(writing_mode) } PropertyDeclarationId::Custom(..) => { custom_properties.push( @@ -4996,7 +5010,7 @@ pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName( (*pair).mServoDeclarationBlock.set_arc_leaky( Arc::new(global_style_data.shared_lock.wrap( PropertyDeclarationBlock::with_one( - declaration.clone(), + declaration.to_physical(writing_mode), Importance::Normal, ) )) @@ -5024,10 +5038,15 @@ pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName( } } + let mut properties_changed = LonghandIdSet::new(); + for property in animation.properties_changed.iter() { + properties_changed.insert(property.to_physical(writing_mode)); + } + // Append property values that are missing in the initial or the final keyframes. if !has_complete_initial_keyframe { fill_in_missing_keyframe_values( - &animation.properties_changed, + &properties_changed, inherited_timing_function, &properties_set_at_start, Offset::Zero, @@ -5036,7 +5055,7 @@ pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName( } if !has_complete_final_keyframe { fill_in_missing_keyframe_values( - &animation.properties_changed, + &properties_changed, inherited_timing_function, &properties_set_at_end, Offset::One,