Bug 1353202 - Add support for iteration composite modes draft
authorBrian Birtles <birtles@gmail.com>
Mon, 22 May 2017 16:36:25 +0900
changeset 582330 e8e9cf9a147c5077bc2f5cfb3c5d0428e8bb8b01
parent 582329 dcd58e251c4bbea7da72503f4180b82f74912973
child 582331 9a9817372de059e31de4b1a5b827fe8f19f9ea46
push id60038
push userbbirtles@mozilla.com
push dateMon, 22 May 2017 07:45:43 +0000
bugs1353202
milestone55.0a1
Bug 1353202 - Add support for iteration composite modes MozReview-Commit-ID: KTmLf16LHei
dom/animation/KeyframeEffectReadOnly.cpp
layout/style/ServoBindingList.h
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
servo/ports/geckolib/glue.rs
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -650,33 +650,30 @@ KeyframeEffectReadOnly::ComposeStyleRule
     aStyleRule->AddValue(aProperty.mProperty, Move(val));
   } else if (valuePosition < 0.5) {
     aStyleRule->AddValue(aProperty.mProperty, Move(fromValue));
   } else {
     aStyleRule->AddValue(aProperty.mProperty, Move(toValue));
   }
 }
 
-// Bug 1333311 - We use two branches for Gecko and Stylo. However, it's
-// better to remove the duplicated code.
 void
 KeyframeEffectReadOnly::ComposeStyleRule(
   RawServoAnimationValueMap& aAnimationValues,
   const AnimationProperty& aProperty,
   const AnimationPropertySegment& aSegment,
   const ComputedTiming& aComputedTiming)
 {
-  // Bug 1329878 - Stylo: Implement accumulate and addition on Servo
-  // AnimationValue.
-
   Servo_AnimationCompose(&aAnimationValues,
                          &mBaseStyleValuesForServo,
                          aProperty.mProperty,
                          &aSegment,
-                         &aComputedTiming);
+                         &aProperty.mSegments.LastElement(),
+                         &aComputedTiming,
+                         mEffectOptions.mIterationComposite);
 }
 
 template<typename ComposeAnimationResult>
 void
 KeyframeEffectReadOnly::ComposeStyle(
   ComposeAnimationResult&& aComposeResult,
   const nsCSSPropertyIDSet& aPropertiesToSkip)
 {
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -311,17 +311,19 @@ SERVO_BINDING_FUNC(Servo_DeclarationBloc
 // Compose animation value for a given property.
 // |base_values| is nsRefPtrHashtable<nsUint32HashKey, RawServoAnimationValue>.
 // We use void* to avoid exposing nsRefPtrHashtable in FFI.
 SERVO_BINDING_FUNC(Servo_AnimationCompose, void,
                    RawServoAnimationValueMapBorrowedMut animation_values,
                    void* base_values,
                    nsCSSPropertyID property,
                    RawGeckoAnimationPropertySegmentBorrowed animation_segment,
-                   RawGeckoComputedTimingBorrowed computed_timing)
+                   RawGeckoAnimationPropertySegmentBorrowed last_segment,
+                   RawGeckoComputedTimingBorrowed computed_timing,
+                   mozilla::dom::IterationCompositeOperation iteration_composite)
 
 // presentation attributes
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_PropertyIsSet, bool,
                    RawServoDeclarationBlockBorrowed declarations,
                    nsCSSPropertyID property)
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetIdentStringValue, void,
                    RawServoDeclarationBlockBorrowed declarations,
                    nsCSSPropertyID property,
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -38,16 +38,19 @@ namespace mozilla {
   enum class CSSPseudoElementType : uint8_t;
   struct Keyframe;
   enum Side;
   struct StyleTransition;
   namespace css {
     struct URLValue;
     struct ImageValue;
   };
+  namespace dom {
+    enum class IterationCompositeOperation : uint8_t;
+  };
   enum class UpdateAnimationsTasks : uint8_t;
   struct LangGroupFontPrefs;
   class ServoStyleSheet;
   class ServoElementSnapshotTable;
 }
 using mozilla::FontFamilyList;
 using mozilla::FontFamilyType;
 using mozilla::ServoElementSnapshot;
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -43,16 +43,17 @@ args = [
 [structs]
 headers = [
     "nsStyleStruct.h",
     "mozilla/ServoPropPrefList.h",
     "mozilla/StyleAnimationValue.h",
     "gfxFontConstants.h",
     "nsThemeConstants.h",
     "mozilla/dom/AnimationEffectReadOnlyBinding.h",
+    "mozilla/dom/KeyframeEffectBinding.h",
     "mozilla/AnimationPropertySegment.h",
     "mozilla/ComputedTiming.h",
     "mozilla/ComputedTimingFunction.h",
     "mozilla/Keyframe.h",
     "mozilla/ServoElementSnapshot.h",
     "mozilla/ServoElementSnapshotTable.h",
     "mozilla/dom/Element.h",
     "mozilla/dom/NameSpaceConstants.h",
@@ -113,16 +114,17 @@ whitelist-types = [
     "mozilla::ComputedTiming",
     "mozilla::ComputedTimingFunction",
     "mozilla::ComputedTimingFunction::BeforeFlag",
     "mozilla::ServoStyleSheet",
     "mozilla::ServoElementSnapshot.*",
     "mozilla::CSSPseudoClassType",
     "mozilla::css::SheetParsingMode",
     "mozilla::css::URLMatchingFunction",
+    "mozilla::dom::IterationCompositeOperation",
     "mozilla::HalfCorner",
     "mozilla::PropertyStyleAnimationValuePair",
     "mozilla::TraversalRestyleBehavior",
     "mozilla::TraversalRootBehavior",
     "mozilla::StyleShapeRadius",
     "mozilla::StyleGrid.*",
     "mozilla::UpdateAnimationsTasks",
     "mozilla::LookAndFeel",
@@ -337,16 +339,17 @@ structs-types = [
     "CSSPseudoElementType",
     "TraversalRestyleBehavior",
     "TraversalRootBehavior",
     "ComputedTimingFunction_BeforeFlag",
     "FontFamilyList",
     "FontFamilyType",
     "FontSizePrefs",
     "GeckoFontMetrics",
+    "IterationCompositeOperation",
     "Keyframe",
     "ServoBundledURI",
     "ServoElementSnapshot",
     "ServoElementSnapshotTable",
     "SheetParsingMode",
     "StyleBasicShape",
     "StyleBasicShapeType",
     "StyleShapeSource",
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -63,16 +63,17 @@ use style::gecko_bindings::bindings::nsT
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowed;
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowedMut;
 use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::{CSSPseudoElementType, CompositeOperation};
 use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleSheet};
 use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID};
 use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule};
 use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint};
+use style::gecko_bindings::structs::IterationCompositeOperation;
 use style::gecko_bindings::structs::Loader;
 use style::gecko_bindings::structs::RawGeckoPresContextOwned;
 use style::gecko_bindings::structs::ServoElementSnapshotTable;
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::structs::nsCSSValueSharedList;
 use style::gecko_bindings::structs::nsCompatibility;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
@@ -326,30 +327,38 @@ pub extern "C" fn Servo_AnimationValueMa
     value_map.insert(property.into(), value.clone());
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMapBorrowedMut,
                                          base_values: *mut ::std::os::raw::c_void,
                                          css_property: nsCSSPropertyID,
                                          segment: RawGeckoAnimationPropertySegmentBorrowed,
-                                         computed_timing: RawGeckoComputedTimingBorrowed)
+                                         last_segment: RawGeckoAnimationPropertySegmentBorrowed,
+                                         computed_timing: RawGeckoComputedTimingBorrowed,
+                                         iteration_composite: IterationCompositeOperation)
 {
     use style::gecko_bindings::bindings::Gecko_AnimationGetBaseStyle;
     use style::gecko_bindings::bindings::Gecko_GetPositionInSegment;
     use style::gecko_bindings::bindings::Gecko_GetProgressFromComputedTiming;
     use style::properties::animated_properties::AnimationValueMap;
 
     let property: TransitionProperty = css_property.into();
     let value_map = AnimationValueMap::from_ffi_mut(raw_value_map);
 
+    // We will need an underlying value if either of the endpoints is null...
     let need_underlying_value = segment.mFromValue.mServo.mRawPtr.is_null() ||
                                 segment.mToValue.mServo.mRawPtr.is_null() ||
+                                // ... or if they have a non-replace composite mode ...
                                 segment.mFromComposite != CompositeOperation::Replace ||
-                                segment.mToComposite != CompositeOperation::Replace;
+                                segment.mToComposite != CompositeOperation::Replace ||
+                                // ... or if we accumulate onto the last value and it is null.
+                                (iteration_composite == IterationCompositeOperation::Accumulate &&
+                                 computed_timing.mCurrentIteration > 0 &&
+                                 last_segment.mToValue.mServo.mRawPtr.is_null());
 
     // If either of the segment endpoints are null, get the underlying value to
     // use from the current value in the values map (set by a lower-priority
     // effect), or, if there is no current value, look up the cached base value
     // for this property.
     let underlying_value = if need_underlying_value {
         let previous_composed_value = value_map.get(&property).cloned();
         previous_composed_value.or_else(|| {
@@ -404,24 +413,57 @@ pub extern "C" fn Servo_AnimationCompose
             },
             _ => {
                 assert!(need_underlying_value,
                         "Should have detected we need an underlying value");
                 underlying_value.as_ref().cloned()
             },
         }
     };
-    let composited_from_value = composite_endpoint(keyframe_from_value, segment.mFromComposite);
-    let composited_to_value = composite_endpoint(keyframe_to_value, segment.mToComposite);
+    let mut composited_from_value = composite_endpoint(keyframe_from_value, segment.mFromComposite);
+    let mut composited_to_value = composite_endpoint(keyframe_to_value, segment.mToComposite);
 
     assert!(keyframe_from_value.is_some() || composited_from_value.is_some(),
             "Should have a suitable from value to use");
     assert!(keyframe_to_value.is_some() || composited_to_value.is_some(),
             "Should have a suitable to value to use");
 
+    // Apply iteration composite behavior.
+    if iteration_composite == IterationCompositeOperation::Accumulate &&
+       computed_timing.mCurrentIteration > 0 {
+
+        let raw_last_value;
+        let last_value = if !last_segment.mToValue.mServo.mRawPtr.is_null() {
+            raw_last_value = unsafe { &*last_segment.mToValue.mServo.mRawPtr };
+            AnimationValue::as_arc(&raw_last_value).as_ref()
+        } else {
+            assert!(need_underlying_value, "Should have detected we need an underlying value");
+            underlying_value.as_ref().unwrap()
+        };
+
+        // As with composite_endpoint, a return value of None means, "Use keyframe_value as-is."
+        let apply_iteration_composite = |keyframe_value: Option<&Arc<AnimationValue>>,
+                                         composited_value: Option<AnimationValue>|
+                                        -> Option<AnimationValue> {
+            let count = computed_timing.mCurrentIteration;
+            match composited_value {
+                Some(endpoint) => last_value.accumulate(&endpoint, count)
+                                            .ok()
+                                            .or(Some(endpoint)),
+                _ => last_value.accumulate(keyframe_value.unwrap(), count)
+                                .ok(),
+            }
+        };
+
+        composited_from_value = apply_iteration_composite(keyframe_from_value,
+                                                          composited_from_value);
+        composited_to_value = apply_iteration_composite(keyframe_to_value,
+                                                        composited_to_value);
+    }
+
     // Use the composited value if there is one, otherwise, use the original keyframe value.
     let from_value = composited_from_value.as_ref().unwrap_or(keyframe_from_value.unwrap());
     let to_value   = composited_to_value.as_ref().unwrap_or(keyframe_to_value.unwrap());
 
     let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
     if segment.mToKey == segment.mFromKey {
         if progress < 0. {
             value_map.insert(property, from_value.clone());