Bug 1353202 - Add support for iteration composite modes
MozReview-Commit-ID: IRbTv6LdF48
--- 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;