Bug 1264125 part 5 - Call the queueing events when canceling transition via Style or Script. r?birtles
MozReview-Commit-ID: 5qWef4xCZXX
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -225,26 +225,33 @@ Animation::SetTimeline(AnimationTimeline
// https://w3c.github.io/web-animations/#setting-the-timeline
void
Animation::SetTimelineNoUpdate(AnimationTimeline* aTimeline)
{
if (mTimeline == aTimeline) {
return;
}
+ StickyTimeDuration activeTime = mEffect
+ ? mEffect->GetComputedTiming().mActiveTime
+ : StickyTimeDuration();
+
RefPtr<AnimationTimeline> oldTimeline = mTimeline;
if (oldTimeline) {
oldTimeline->RemoveAnimation(this);
}
mTimeline = aTimeline;
if (!mStartTime.IsNull()) {
mHoldTime.SetNull();
}
+ if (!aTimeline) {
+ MaybeQueueCancelEvent(activeTime);
+ }
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
}
// https://w3c.github.io/web-animations/#set-the-animation-start-time
void
Animation::SetStartTime(const Nullable<TimeDuration>& aNewStartTime)
{
if (aNewStartTime == mStartTime) {
@@ -765,24 +772,29 @@ Animation::CancelNoUpdate()
if (mFinished) {
mFinished->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
ResetFinishedPromise();
DispatchPlaybackEvent(NS_LITERAL_STRING("cancel"));
+ StickyTimeDuration activeTime = mEffect
+ ? mEffect->GetComputedTiming().mActiveTime
+ : StickyTimeDuration();
+
mHoldTime.SetNull();
mStartTime.SetNull();
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
if (mTimeline) {
mTimeline->RemoveAnimation(this);
}
+ MaybeQueueCancelEvent(activeTime);
}
bool
Animation::ShouldBeSynchronizedWithMainThread(
nsCSSPropertyID aProperty,
const nsIFrame* aFrame,
AnimationPerformanceWarning::Type& aPerformanceWarning) const
{
@@ -851,16 +863,27 @@ Animation::HasLowerCompositeOrderThan(co
nullptr;
};
auto thisTransition = asCSSTransitionForSorting(*this);
auto otherTransition = asCSSTransitionForSorting(aOther);
if (thisTransition && otherTransition) {
return thisTransition->HasLowerCompositeOrderThan(*otherTransition);
}
if (thisTransition || otherTransition) {
+ // Cancelled transitions no longer have an owning element. To be strictly
+ // correct we should store a strong reference to the owning element
+ // so that if we arrive here while sorting cancel events, we can sort
+ // them in the correct order.
+ //
+ // However, given that cancel events are almost always queued
+ // synchronously in some deterministic manner, we can be fairly sure
+ // that cancel events will be dispatched in a deterministic order
+ // (which is our only hard requirement until specs say otherwise).
+ // Furthermore, we only reach here when we have events with equal
+ // timestamps so this is an edge case we can probably ignore for now.
return thisTransition;
}
}
// 2. CSS Animations sort next
{
auto asCSSAnimationForSorting =
[] (const Animation& anim) -> const CSSAnimation*
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -318,16 +318,26 @@ public:
* updated in |aStyleRule|.
*/
void ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
const nsCSSPropertyIDSet& aPropertiesToSkip);
void NotifyEffectTimingUpdated();
void NotifyGeometricAnimationsStartingThisFrame();
+ /**
+ * Used by subclasses to synchronously queue a cancel event in situations
+ * where the Animation may have been cancelled.
+ *
+ * We need to do this synchronously because after a CSS animation/transition
+ * is canceled, it will be released by its owning element and may not still
+ * exist when we would normally go to queue events on the next tick.
+ */
+ virtual void MaybeQueueCancelEvent(StickyTimeDuration aActiveTime) {};
+
protected:
void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime);
void SilentlySetPlaybackRate(double aPlaybackRate);
void CancelNoUpdate();
void PlayNoUpdate(ErrorResult& aRv, LimitBehavior aLimitBehavior);
void PauseNoUpdate(ErrorResult& aRv);
void ResumeAt(const TimeDuration& aReadyTime);
void PauseAt(const TimeDuration& aReadyTime);
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -175,17 +175,17 @@ CSSTransition::UpdateTiming(SeekFlag aSe
mAnimationIndex = sNextAnimationIndex++;
mNeedsNewAnimationIndexWhenRun = false;
}
Animation::UpdateTiming(aSeekFlag, aSyncNotifyFlag);
}
void
-CSSTransition::QueueEvents()
+CSSTransition::QueueEvents(StickyTimeDuration aActiveTime)
{
if (!mEffect ||
!mOwningElement.IsSet()) {
return;
}
dom::Element* owningElement;
CSSPseudoElementType owningPseudoType;
@@ -225,22 +225,19 @@ CSSTransition::QueueEvents()
currentPhase = static_cast<TransitionPhase>(computedTiming.mPhase);
}
AutoTArray<TransitionEventParams, 3> events;
// Handle cancel events firts
if (mPreviousTransitionPhase != TransitionPhase::Idle &&
currentPhase == TransitionPhase::Idle) {
- // FIXME: bug 1264125: We will need to get active time when cancelling
- // the transition.
- StickyTimeDuration activeTime(0);
- TimeStamp activeTimeStamp = ElapsedTimeToTimeStamp(activeTime);
+ TimeStamp activeTimeStamp = ElapsedTimeToTimeStamp(aActiveTime);
events.AppendElement(TransitionEventParams{ eTransitionCancel,
- activeTime,
+ aActiveTime,
activeTimeStamp });
}
// All other events
switch (mPreviousTransitionPhase) {
case TransitionPhase::Idle:
if (currentPhase == TransitionPhase::Pending ||
currentPhase == TransitionPhase::Before) {
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -157,16 +157,28 @@ public:
// defined until that moment.
//
// See longer explanation in CSSAnimation::CancelFromStyle.
mAnimationIndex = sNextAnimationIndex++;
mNeedsNewAnimationIndexWhenRun = true;
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.
// However, once we clear the owning element, CascadeLevel() will begin
// returning CascadeLevel::Animations.
mOwningElement = OwningElementRef();
}
@@ -209,28 +221,32 @@ public:
// because the animation on the compositor may be running ahead while
// main-thread is busy.
static Nullable<TimeDuration> GetCurrentTimeAt(
const DocumentTimeline& aTimeline,
const TimeStamp& aBaseTime,
const TimeDuration& aStartTime,
double aPlaybackRate);
+ void MaybeQueueCancelEvent(StickyTimeDuration aActiveTime) override {
+ QueueEvents(aActiveTime);
+ }
+
protected:
virtual ~CSSTransition()
{
MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared "
"before a CSS transition is destroyed");
}
// Animation overrides
void UpdateTiming(SeekFlag aSeekFlag,
SyncNotifyFlag aSyncNotifyFlag) override;
- void QueueEvents();
+ void QueueEvents(StickyTimeDuration activeTime = StickyTimeDuration());
// The (pseudo-)element whose computed transition-property refers to this
// transition (if any).
//
// This is used for determining the relative composite order of transitions
// generated from CSS markup.
//
// Typically this will be the same as the target element of the keyframe