Bug 1353202 - Add support for iteration composite modes draft
authorBrian Birtles <birtles@gmail.com>
Mon, 22 May 2017 10:37:25 +0900
changeset 582192 92872e07d225fb6ed947bc0e9c4b484faa3a30cb
parent 582191 402c14e0c774c6c5e83ade209ff49061cf015203
child 629692 51b782d07c50ef85d64155d9d7e3114b9720f340
push id59996
push userbbirtles@mozilla.com
push dateMon, 22 May 2017 01:51:27 +0000
bugs1353202
milestone55.0a1
Bug 1353202 - Add support for iteration composite modes MozReview-Commit-ID: IRbTv6LdF48
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(|| {
@@ -360,57 +369,84 @@ pub extern "C" fn Servo_AnimationCompose
         None
     };
 
     if need_underlying_value && underlying_value.is_none() {
         warn!("Underlying value should be valid when we expect to use it");
         return;
     }
 
+    let from_iteration_composite_result;
+    let to_iteration_composite_result;
+
     // Temporaries used in the following if-block whose lifetimes we need to prolong.
     let raw_from_value;
     let from_composite_result;
-    let from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() {
+    let mut from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() {
         raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr };
         match segment.mFromComposite {
             CompositeOperation::Add | CompositeOperation::Accumulate => {
                 let value_to_composite = AnimationValue::as_arc(&raw_from_value).as_ref();
                 from_composite_result = if segment.mFromComposite == CompositeOperation::Add {
                     underlying_value.as_ref().unwrap().add(value_to_composite)
                 } else {
                     underlying_value.as_ref().unwrap().accumulate(value_to_composite, 1)
                 };
                 from_composite_result.as_ref().unwrap_or(value_to_composite)
             }
             _ => { AnimationValue::as_arc(&raw_from_value) }
         }
     } else {
+        assert!(need_underlying_value, "Should have detected we need an underlying value");
         underlying_value.as_ref().unwrap()
     };
 
     let raw_to_value;
     let to_composite_result;
-    let to_value = if !segment.mToValue.mServo.mRawPtr.is_null() {
+    let mut to_value = if !segment.mToValue.mServo.mRawPtr.is_null() {
         raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr };
         match segment.mToComposite {
             CompositeOperation::Add | CompositeOperation::Accumulate => {
                 let value_to_composite = AnimationValue::as_arc(&raw_to_value).as_ref();
                 to_composite_result = if segment.mToComposite == CompositeOperation::Add {
                     underlying_value.as_ref().unwrap().add(value_to_composite)
                 } else {
                     underlying_value.as_ref().unwrap().accumulate(value_to_composite, 1)
                 };
                 to_composite_result.as_ref().unwrap_or(value_to_composite)
             }
             _ => { AnimationValue::as_arc(&raw_to_value) }
         }
     } else {
+        assert!(need_underlying_value, "Should have detected we need an underlying value");
         underlying_value.as_ref().unwrap()
     };
 
+    // 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()
+        };
+
+        from_iteration_composite_result =
+            last_value.accumulate(from_value, computed_timing.mCurrentIteration);
+        from_value = from_iteration_composite_result.as_ref().unwrap_or(from_value);
+
+        to_iteration_composite_result =
+            last_value.accumulate(to_value, computed_timing.mCurrentIteration);
+        to_value = to_iteration_composite_result.as_ref().unwrap_or(to_value);
+    }
+
     let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
     if segment.mToKey == segment.mFromKey {
         if progress < 0. {
             value_map.insert(property, from_value.clone());
         } else {
             value_map.insert(property, to_value.clone());
         }
         return;