Bug 1049975 - Part 9: Implement writable Animation effect.
MozReview-Commit-ID: 1dwHpcYJto3
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -125,30 +125,74 @@ Animation::SetId(const nsAString& aId)
void
Animation::SetEffect(AnimationEffectReadOnly* aEffect)
{
SetEffectNoUpdate(aEffect);
PostUpdate();
}
+// https://w3c.github.io/web-animations/#setting-the-target-effect
void
Animation::SetEffectNoUpdate(AnimationEffectReadOnly* aEffect)
{
RefPtr<Animation> kungFuDeathGrip(this);
if (mEffect == aEffect) {
return;
}
+
if (mEffect) {
- mEffect->SetAnimation(nullptr);
+ if (!aEffect) {
+ // If the new effect is null, call ResetPendingTasks before clearing
+ // mEffect since ResetPendingTasks needs it to get the appropriate
+ // PendingAnimationTracker.
+ ResetPendingTasks();
+ }
+
+ // Break links with the old effect and then drop it.
+ RefPtr<AnimationEffectReadOnly> oldEffect = mEffect;
+ mEffect = nullptr;
+ oldEffect->SetAnimation(nullptr);
}
- mEffect = aEffect;
- if (mEffect) {
+
+ if (aEffect) {
+ // Break links from the new effect to its previous animation, if any.
+ RefPtr<AnimationEffectReadOnly> newEffect = aEffect;
+ Animation* prevAnim = aEffect->GetAnimation();
+ if (prevAnim) {
+ prevAnim->SetEffect(nullptr);
+ }
+
+ // Create links with the new effect.
+ mEffect = newEffect;
mEffect->SetAnimation(this);
+
+ // Reschedule pending pause or pending play tasks.
+ // If we have a pending animation, it will either be registered
+ // in the pending animation tracker and have a null pending ready time,
+ // or, after it has been painted, it will be removed from the tracker
+ // and assigned a pending ready time.
+ // After updating the effect we'll typically need to repaint so if we've
+ // already been assigned a pending ready time, we should clear it and put
+ // the animation back in the tracker.
+ if (!mPendingReadyTime.IsNull()) {
+ mPendingReadyTime.SetNull();
+
+ nsIDocument* doc = GetRenderedDocument();
+ if (doc) {
+ PendingAnimationTracker* tracker =
+ doc->GetOrCreatePendingAnimationTracker();
+ if (mPendingState == PendingState::PlayPending) {
+ tracker->AddPlayPending(*this);
+ } else {
+ tracker->AddPausePending(*this);
+ }
+ }
+ }
}
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
}
void
Animation::SetTimeline(AnimationTimeline* aTimeline)
{
@@ -1104,17 +1148,21 @@ Animation::UpdateFinishedState(SeekFlag
mPreviousCurrentTime = GetCurrentTime();
}
void
Animation::UpdateEffect()
{
if (mEffect) {
UpdateRelevance();
- mEffect->NotifyAnimationTimingUpdated();
+
+ KeyframeEffectReadOnly* keyframeEffect = mEffect->AsKeyframeEffect();
+ if (keyframeEffect) {
+ keyframeEffect->NotifyAnimationTimingUpdated();
+ }
}
}
void
Animation::FlushStyle() const
{
nsIDocument* doc = GetRenderedDocument();
if (doc) {
--- a/dom/animation/AnimationEffectReadOnly.cpp
+++ b/dom/animation/AnimationEffectReadOnly.cpp
@@ -88,19 +88,19 @@ AnimationEffectReadOnly::SetSpecifiedTim
{
if (mTiming->AsTimingParams() == aTiming) {
return;
}
mTiming->SetTimingParams(aTiming);
if (mAnimation) {
mAnimation->NotifyEffectTimingUpdated();
}
- // NotifyEffectTimingUpdated will eventually cause
- // NotifyAnimationTimingUpdated to be called on this object which will
- // update our registration with the target element.
+ // For keyframe effects, NotifyEffectTimingUpdated above will eventually cause
+ // KeyframeEffectReadOnly::NotifyAnimationTimingUpdated to be called so it can
+ // update its registration with the target element as necessary.
}
ComputedTiming
AnimationEffectReadOnly::GetComputedTimingAt(
const Nullable<TimeDuration>& aLocalTime,
const TimingParams& aTiming,
double aPlaybackRate)
{
@@ -318,23 +318,16 @@ AnimationEffectReadOnly::GetComputedTimi
GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
SpecifiedTiming(),
playbackRate),
currentTime,
SpecifiedTiming(),
aRetVal);
}
-void
-AnimationEffectReadOnly::SetAnimation(Animation* aAnimation)
-{
- mAnimation = aAnimation;
- NotifyAnimationTimingUpdated();
-}
-
AnimationEffectReadOnly::~AnimationEffectReadOnly()
{
// mTiming is cycle collected, so we have to do null check first even though
// mTiming shouldn't be null during the lifetime of KeyframeEffect.
if (mTiming) {
mTiming->Unlink();
}
}
--- a/dom/animation/AnimationEffectReadOnly.h
+++ b/dom/animation/AnimationEffectReadOnly.h
@@ -54,17 +54,16 @@ public:
bool IsInEffect() const;
already_AddRefed<AnimationEffectTimingReadOnly> Timing();
const TimingParams& SpecifiedTiming() const
{
return mTiming->AsTimingParams();
}
void SetSpecifiedTiming(const TimingParams& aTiming);
- virtual void NotifyAnimationTimingUpdated() = 0;
// This function takes as input the timing parameters of an animation and
// returns the computed timing at the specified local time.
//
// The local time may be null in which case only static parameters such as the
// active duration are calculated. All other members of the returned object
// are given a null/initial value.
//
@@ -75,17 +74,17 @@ public:
GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
const TimingParams& aTiming,
double aPlaybackRate);
// Shortcut that gets the computed timing using the current local time as
// calculated from the timeline time.
ComputedTiming GetComputedTiming(const TimingParams* aTiming = nullptr) const;
void GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const;
- void SetAnimation(Animation* aAnimation);
+ virtual void SetAnimation(Animation* aAnimation) = 0;
Animation* GetAnimation() const { return mAnimation; };
protected:
virtual ~AnimationEffectReadOnly();
Nullable<TimeDuration> GetLocalTime() const;
protected:
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -1215,16 +1215,42 @@ KeyframeEffectReadOnly::CalculateCumulat
&equalStructs,
&samePointerStructs);
mCumulativeChangeHint |= changeHint;
}
}
}
+void
+KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation)
+{
+ if (mAnimation == aAnimation) {
+ return;
+ }
+
+ // Restyle for the old animation.
+ RequestRestyle(EffectCompositor::RestyleType::Layer);
+
+ mAnimation = aAnimation;
+
+ // Restyle for the new animation.
+ RequestRestyle(EffectCompositor::RestyleType::Layer);
+
+ if (mTarget) {
+ EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
+ mTarget->mPseudoType);
+ if (effectSet) {
+ effectSet->MarkCascadeNeedsUpdate();
+ }
+ }
+
+ NotifyAnimationTimingUpdated();
+}
+
bool
KeyframeEffectReadOnly::CanIgnoreIfNotVisible() const
{
if (!AnimationUtils::IsOffscreenThrottlingEnabled()) {
return false;
}
// FIXME: For further sophisticated optimization we need to check
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -225,17 +225,19 @@ public:
IterationCompositeOperation IterationComposite() const;
CompositeOperation Composite() const;
void GetSpacing(nsString& aRetVal) const
{
mEffectOptions.GetSpacingAsString(aRetVal);
}
- void NotifyAnimationTimingUpdated() override;
+ void NotifyAnimationTimingUpdated();
+
+ void SetAnimation(Animation* aAnimation) override;
void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
ErrorResult& aRv);
void SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
nsStyleContext* aStyleContext);
const AnimationProperty*
GetAnimationOfProperty(nsCSSPropertyID aProperty) const;
bool HasAnimationOfProperty(nsCSSPropertyID aProperty) const
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -793,20 +793,16 @@ nsTransitionManager::ConsiderStartingTra
RefPtr<CSSTransition> animation =
new CSSTransition(mPresContext->Document()->GetScopeObject());
animation->SetOwningElement(
OwningElementRef(*aElement, aNewStyleContext->GetPseudoType()));
animation->SetTimelineNoUpdate(timeline);
animation->SetCreationSequence(
mPresContext->RestyleManager()->AsGecko()->GetAnimationGeneration());
- // The order of the following two calls is important since PlayFromStyle
- // will add the animation to the PendingAnimationTracker of its effect's
- // document. When we come to make effect writeable (bug 1049975) we should
- // remove this dependency.
animation->SetEffectFromStyle(pt);
animation->PlayFromStyle();
if (!aElementTransitions) {
bool createdCollection = false;
aElementTransitions =
CSSTransitionCollection::GetOrCreateAnimationCollection(
aElement, aNewStyleContext->GetPseudoType(), &createdCollection);