summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dom/animation/Animation.cpp13
-rw-r--r--dom/animation/ComputedTiming.h4
-rw-r--r--dom/base/nsGkAtomList.h1
-rw-r--r--dom/events/EventNameList.h4
-rw-r--r--dom/webidl/EventHandler.webidl1
-rw-r--r--layout/style/AnimationCommon.h20
-rw-r--r--layout/style/nsAnimationManager.cpp75
-rw-r--r--layout/style/nsAnimationManager.h15
-rw-r--r--layout/style/nsTransitionManager.cpp21
-rw-r--r--layout/style/nsTransitionManager.h17
-rw-r--r--widget/EventMessageList.h1
11 files changed, 99 insertions, 73 deletions
diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp
index 242a0c6d6..bd318f79e 100644
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -787,12 +787,21 @@ Animation::CancelNoUpdate()
mHoldTime.SetNull();
mStartTime.SetNull();
- UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
-
if (mTimeline) {
mTimeline->RemoveAnimation(this);
}
MaybeQueueCancelEvent(activeTime);
+
+ // When an animation is cancelled it no longer needs further ticks from the
+ // timeline. However, if we queued a cancel event and this was the last
+ // animation attached to the timeline, the timeline will stop observing the
+ // refresh driver and there may be no subsequent refresh driver tick for
+ // dispatching the queued event.
+ //
+ // By calling UpdateTiming *after* removing ourselves from our timeline, we
+ // ensure the timeline will register with the refresh driver for at least one
+ // more tick.
+ UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
}
void
diff --git a/dom/animation/ComputedTiming.h b/dom/animation/ComputedTiming.h
index addf15865..525d59d23 100644
--- a/dom/animation/ComputedTiming.h
+++ b/dom/animation/ComputedTiming.h
@@ -64,12 +64,12 @@ struct ComputedTiming
}
enum class AnimationPhase {
- Null, // Not sampled (null sample time)
+ Idle, // Not sampled (null sample time)
Before, // Sampled prior to the start of the active interval
Active, // Sampled within the active interval
After // Sampled after (or at) the end of the active interval
};
- AnimationPhase mPhase = AnimationPhase::Null;
+ AnimationPhase mPhase = AnimationPhase::Idle;
ComputedTimingFunction::BeforeFlag mBeforeFlag =
ComputedTimingFunction::BeforeFlag::Unset;
diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h
index 09d73e617..948487083 100644
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -694,6 +694,7 @@ GK_ATOM(onadapterremoved, "onadapterremoved")
GK_ATOM(onafterprint, "onafterprint")
GK_ATOM(onafterscriptexecute, "onafterscriptexecute")
GK_ATOM(onalerting, "onalerting")
+GK_ATOM(onanimationcancel, "onanimationcancel")
GK_ATOM(onanimationend, "onanimationend")
GK_ATOM(onanimationiteration, "onanimationiteration")
GK_ATOM(onanimationstart, "onanimationstart")
diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h
index 61942b251..00478c87b 100644
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -1019,6 +1019,10 @@ EVENT(animationiteration,
eAnimationIteration,
EventNameType_All,
eAnimationEventClass)
+EVENT(animationcancel,
+ eAnimationCancel,
+ EventNameType_All,
+ eAnimationEventClass)
// Webkit-prefixed versions of Transition & Animation events, for web compat:
EVENT(webkitAnimationEnd,
diff --git a/dom/webidl/EventHandler.webidl b/dom/webidl/EventHandler.webidl
index 6691ca224..306372ff1 100644
--- a/dom/webidl/EventHandler.webidl
+++ b/dom/webidl/EventHandler.webidl
@@ -128,6 +128,7 @@ interface GlobalEventHandlers {
attribute EventHandler onmozpointerlockerror;
// CSS-Animation and CSS-Transition handlers.
+ attribute EventHandler onanimationcancel;
attribute EventHandler onanimationend;
attribute EventHandler onanimationiteration;
attribute EventHandler onanimationstart;
diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h
index 37030411c..025c034a4 100644
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -251,6 +251,26 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
aField.Traverse(&aCallback, aName);
}
+// Return the TransitionPhase or AnimationPhase to use when the animation
+// doesn't have a target effect.
+template <typename PhaseType>
+PhaseType GetAnimationPhaseWithoutEffect(const dom::Animation& aAnimation)
+{
+ MOZ_ASSERT(!aAnimation.GetEffect(),
+ "Should only be called when we do not have an effect");
+
+ Nullable<TimeDuration> currentTime = aAnimation.GetCurrentTime();
+ if (currentTime.IsNull()) {
+ return PhaseType::Idle;
+ }
+
+ // If we don't have a target effect, the duration will be zero so the phase is
+ // 'before' if the current time is less than zero.
+ return currentTime.Value() < TimeDuration()
+ ? PhaseType::Before
+ : PhaseType::After;
+};
+
} // namespace mozilla
#endif /* !defined(mozilla_css_AnimationCommon_h) */
diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp
index 8d4e8fcee..aa1b6fe78 100644
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -158,12 +158,8 @@ CSSAnimation::HasLowerCompositeOrderThan(const CSSAnimation& aOther) const
}
void
-CSSAnimation::QueueEvents()
+CSSAnimation::QueueEvents(StickyTimeDuration aActiveTime)
{
- if (!mEffect) {
- return;
- }
-
// If the animation is pending, we ignore animation events until we finish
// pending.
if (mPendingState != PendingState::NotPending) {
@@ -198,39 +194,60 @@ CSSAnimation::QueueEvents()
}
nsAnimationManager* manager = presContext->AnimationManager();
- ComputedTiming computedTiming = mEffect->GetComputedTiming();
-
- ComputedTiming::AnimationPhase currentPhase = computedTiming.mPhase;
- uint64_t currentIteration = computedTiming.mCurrentIteration;
- if (currentPhase == mPreviousPhase &&
- currentIteration == mPreviousIteration) {
- return;
- }
const StickyTimeDuration zeroDuration;
- StickyTimeDuration intervalStartTime =
- std::max(std::min(StickyTimeDuration(-mEffect->SpecifiedTiming().mDelay),
- computedTiming.mActiveDuration),
- zeroDuration);
- StickyTimeDuration intervalEndTime =
- std::max(std::min((EffectEnd() - mEffect->SpecifiedTiming().mDelay),
- computedTiming.mActiveDuration),
- zeroDuration);
-
- uint64_t iterationBoundary = mPreviousIteration > currentIteration
- ? currentIteration + 1
- : currentIteration;
- StickyTimeDuration iterationStartTime =
- computedTiming.mDuration.MultDouble(
- (iterationBoundary - computedTiming.mIterationStart));
+ uint64_t currentIteration = 0;
+ ComputedTiming::AnimationPhase currentPhase;
+ StickyTimeDuration intervalStartTime;
+ StickyTimeDuration intervalEndTime;
+ StickyTimeDuration iterationStartTime;
+
+ if (!mEffect) {
+ currentPhase = GetAnimationPhaseWithoutEffect
+ <ComputedTiming::AnimationPhase>(*this);
+ } else {
+ ComputedTiming computedTiming = mEffect->GetComputedTiming();
+ currentPhase = computedTiming.mPhase;
+ currentIteration = computedTiming.mCurrentIteration;
+ if (currentPhase == mPreviousPhase &&
+ currentIteration == mPreviousIteration) {
+ return;
+ }
+ intervalStartTime =
+ std::max(std::min(StickyTimeDuration(-mEffect->SpecifiedTiming().mDelay),
+ computedTiming.mActiveDuration),
+ zeroDuration);
+ intervalEndTime =
+ std::max(std::min((EffectEnd() - mEffect->SpecifiedTiming().mDelay),
+ computedTiming.mActiveDuration),
+ zeroDuration);
+
+ uint64_t iterationBoundary = mPreviousIteration > currentIteration
+ ? currentIteration + 1
+ : currentIteration;
+ iterationStartTime =
+ computedTiming.mDuration.MultDouble(
+ (iterationBoundary - computedTiming.mIterationStart));
+ }
TimeStamp startTimeStamp = ElapsedTimeToTimeStamp(intervalStartTime);
TimeStamp endTimeStamp = ElapsedTimeToTimeStamp(intervalEndTime);
TimeStamp iterationTimeStamp = ElapsedTimeToTimeStamp(iterationStartTime);
AutoTArray<AnimationEventParams, 2> events;
+
+ // Handle cancel event first
+ if ((mPreviousPhase != AnimationPhase::Idle &&
+ mPreviousPhase != AnimationPhase::After) &&
+ currentPhase == AnimationPhase::Idle) {
+ TimeStamp activeTimeStamp = ElapsedTimeToTimeStamp(aActiveTime);
+ events.AppendElement(AnimationEventParams{ eAnimationCancel,
+ aActiveTime,
+ activeTimeStamp });
+ }
+
switch (mPreviousPhase) {
- case AnimationPhase::Null:
+ case AnimationPhase::Idle:
case AnimationPhase::Before:
if (currentPhase == AnimationPhase::Active) {
events.AppendElement(AnimationEventParams{ eAnimationStart,
diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h
index 868d4bb42..d838d090a 100644
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -76,7 +76,7 @@ public:
, mIsStylePaused(false)
, mPauseShouldStick(false)
, mNeedsNewAnimationIndexWhenRun(false)
- , mPreviousPhase(ComputedTiming::AnimationPhase::Null)
+ , mPreviousPhase(ComputedTiming::AnimationPhase::Idle)
, mPreviousIteration(0)
{
// We might need to drop this assertion once we add a script-accessible
@@ -110,8 +110,6 @@ public:
void PauseFromStyle();
void CancelFromStyle() override
{
- mOwningElement = OwningElementRef();
-
// When an animation is disassociated with style it enters an odd state
// where its composite order is undefined until it first transitions
// out of the idle state.
@@ -126,10 +124,15 @@ public:
mNeedsNewAnimationIndexWhenRun = true;
Animation::CancelFromStyle();
+
+ // We need to do this *after* calling CancelFromStyle() since
+ // CancelFromStyle might synchronously trigger a cancel event for which
+ // we need an owning element to target the event at.
+ mOwningElement = OwningElementRef();
}
void Tick() override;
- void QueueEvents();
+ void QueueEvents(StickyTimeDuration aActiveTime = StickyTimeDuration());
bool IsStylePaused() const { return mIsStylePaused; }
@@ -158,6 +161,10 @@ public:
// reflect changes to that markup.
bool IsTiedToMarkup() const { return mOwningElement.IsSet(); }
+ void MaybeQueueCancelEvent(StickyTimeDuration aActiveTime) override {
+ QueueEvents(aActiveTime);
+ }
+
protected:
virtual ~CSSAnimation()
{
diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp
index abb9e2311..118702e8f 100644
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -46,8 +46,6 @@ using mozilla::dom::KeyframeEffectReadOnly;
using namespace mozilla;
using namespace mozilla::css;
-typedef mozilla::ComputedTiming::AnimationPhase AnimationPhase;
-
namespace {
struct TransitionEventParams {
EventMessage mMessage;
@@ -203,7 +201,7 @@ CSSTransition::QueueEvents(StickyTimeDuration aActiveTime)
StickyTimeDuration intervalEndTime;
if (!mEffect) {
- currentPhase = GetTransitionPhaseWithoutEffect();
+ currentPhase = GetAnimationPhaseWithoutEffect<TransitionPhase>(*this);
intervalStartTime = zeroDuration;
intervalEndTime = zeroDuration;
} else {
@@ -329,23 +327,6 @@ CSSTransition::QueueEvents(StickyTimeDuration aActiveTime)
}
}
-CSSTransition::TransitionPhase
-CSSTransition::GetTransitionPhaseWithoutEffect() const
-{
- MOZ_ASSERT(!mEffect, "Should only be called when we do not have an effect");
-
- Nullable<TimeDuration> currentTime = GetCurrentTime();
- if (currentTime.IsNull()) {
- return TransitionPhase::Idle;
- }
-
- // If we don't have a target effect, the duration will be zero so the phase is
- // 'before' if the current time is less than zero.
- return currentTime.Value() < TimeDuration()
- ? TransitionPhase::Before
- : TransitionPhase::After;
-}
-
void
CSSTransition::Tick()
{
diff --git a/layout/style/nsTransitionManager.h b/layout/style/nsTransitionManager.h
index e2f198a40..1c48cc8cd 100644
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -162,18 +162,6 @@ public:
Animation::CancelFromStyle();
- // The above call to Animation::CancelFromStyle may cause a transitioncancel
- // event to be queued. However, it will also remove the transition from its
- // timeline. If this transition was the last animation attached to
- // the timeline, the timeline will stop observing the refresh driver and
- // there may be no subsequent tick fro dispatching animation events.
- //
- // To ensure the cancel event is dispatched we tell the timeline it needs to
- // observe the refresh driver for at least one more tick.
- if (mTimeline) {
- mTimeline->NotifyAnimationUpdated(*this);
- }
-
// It is important we do this *after* calling CancelFromStyle().
// This is because CancelFromStyle() will end up posting a restyle and
// that restyle should target the *transitions* level of the cascade.
@@ -245,9 +233,6 @@ protected:
enum class TransitionPhase;
- // Return the TransitionPhase to use when the transition doesn't have a target
- // effect.
- TransitionPhase GetTransitionPhaseWithoutEffect() const;
// The (pseudo-)element whose computed transition-property refers to this
// transition (if any).
@@ -272,7 +257,7 @@ protected:
// to be queued on this tick.
// See: https://drafts.csswg.org/css-transitions-2/#transition-phase
enum class TransitionPhase {
- Idle = static_cast<int>(ComputedTiming::AnimationPhase::Null),
+ Idle = static_cast<int>(ComputedTiming::AnimationPhase::Idle),
Before = static_cast<int>(ComputedTiming::AnimationPhase::Before),
Active = static_cast<int>(ComputedTiming::AnimationPhase::Active),
After = static_cast<int>(ComputedTiming::AnimationPhase::After),
diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h
index 474e25862..7903f6a26 100644
--- a/widget/EventMessageList.h
+++ b/widget/EventMessageList.h
@@ -344,6 +344,7 @@ NS_EVENT_MESSAGE(eTransitionCancel)
NS_EVENT_MESSAGE(eAnimationStart)
NS_EVENT_MESSAGE(eAnimationEnd)
NS_EVENT_MESSAGE(eAnimationIteration)
+NS_EVENT_MESSAGE(eAnimationCancel)
// Webkit-prefixed versions of Transition & Animation events, for web compat:
NS_EVENT_MESSAGE(eWebkitTransitionEnd)