Bug 1202333: AnimationEvent elapsedTime should reflect playbackRate (without tests)

Issue #55
This commit is contained in:
janekptacijarabaci 2018-03-14 08:26:26 +01:00 committed by Roy Tam
commit 0c3ebf1a3b
2 changed files with 93 additions and 73 deletions

View file

@ -33,11 +33,15 @@ using mozilla::dom::AnimationPlayState;
using mozilla::dom::KeyframeEffectReadOnly;
using mozilla::dom::CSSAnimation;
typedef mozilla::ComputedTiming::AnimationPhase AnimationPhase;
namespace {
// Pair of an event message and elapsed time used when determining the set of
// events to queue.
typedef Pair<EventMessage, StickyTimeDuration> EventPair;
struct AnimationEventParams {
EventMessage mMessage;
StickyTimeDuration mElapsedTime;
TimeStamp mTimeStamp;
};
} // anonymous namespace
@ -196,75 +200,93 @@ CSSAnimation::QueueEvents()
ComputedTiming computedTiming = mEffect->GetComputedTiming();
if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Null) {
return; // do nothing
ComputedTiming::AnimationPhase currentPhase = computedTiming.mPhase;
uint64_t currentIteration = computedTiming.mCurrentIteration;
if (currentPhase == mPreviousPhase &&
currentIteration == mPreviousIteration) {
return;
}
// Note that script can change the start time, so we have to handle moving
// backwards through the animation as well as forwards. An 'animationstart'
// is dispatched if we enter the active phase (regardless if that is from
// before or after the animation's active phase). An 'animationend' is
// dispatched if we leave the active phase (regardless if that is to before
// or after the animation's active phase).
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);
bool wasActive = mPreviousPhaseOrIteration != PREVIOUS_PHASE_BEFORE &&
mPreviousPhaseOrIteration != PREVIOUS_PHASE_AFTER;
bool isActive =
computedTiming.mPhase == ComputedTiming::AnimationPhase::Active;
bool isSameIteration =
computedTiming.mCurrentIteration == mPreviousPhaseOrIteration;
bool skippedActivePhase =
(mPreviousPhaseOrIteration == PREVIOUS_PHASE_BEFORE &&
computedTiming.mPhase == ComputedTiming::AnimationPhase::After) ||
(mPreviousPhaseOrIteration == PREVIOUS_PHASE_AFTER &&
computedTiming.mPhase == ComputedTiming::AnimationPhase::Before);
bool skippedFirstIteration =
isActive &&
mPreviousPhaseOrIteration == PREVIOUS_PHASE_BEFORE &&
computedTiming.mCurrentIteration > 0;
uint64_t iterationBoundary = mPreviousIteration > currentIteration
? currentIteration + 1
: currentIteration;
StickyTimeDuration iterationStartTime =
computedTiming.mDuration.MultDouble(
(iterationBoundary - computedTiming.mIterationStart));
MOZ_ASSERT(!skippedActivePhase || (!isActive && !wasActive),
"skippedActivePhase only makes sense if we were & are inactive");
TimeStamp startTimeStamp = ElapsedTimeToTimeStamp(intervalStartTime);
TimeStamp endTimeStamp = ElapsedTimeToTimeStamp(intervalEndTime);
TimeStamp iterationTimeStamp = ElapsedTimeToTimeStamp(iterationStartTime);
if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Before) {
mPreviousPhaseOrIteration = PREVIOUS_PHASE_BEFORE;
} else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Active) {
mPreviousPhaseOrIteration = computedTiming.mCurrentIteration;
} else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::After) {
mPreviousPhaseOrIteration = PREVIOUS_PHASE_AFTER;
AutoTArray<AnimationEventParams, 2> events;
switch (mPreviousPhase) {
case AnimationPhase::Null:
case AnimationPhase::Before:
if (currentPhase == AnimationPhase::Active) {
events.AppendElement(AnimationEventParams{ eAnimationStart,
intervalStartTime,
startTimeStamp });
} else if (currentPhase == AnimationPhase::After) {
events.AppendElement(AnimationEventParams{ eAnimationStart,
intervalStartTime,
startTimeStamp });
events.AppendElement(AnimationEventParams{ eAnimationEnd,
intervalEndTime,
endTimeStamp });
}
break;
case AnimationPhase::Active:
if (currentPhase == AnimationPhase::Before) {
events.AppendElement(AnimationEventParams{ eAnimationEnd,
intervalStartTime,
startTimeStamp });
} else if (currentPhase == AnimationPhase::Active) {
// The currentIteration must have changed or element we would have
// returned early above.
MOZ_ASSERT(currentIteration != mPreviousIteration);
events.AppendElement(AnimationEventParams{ eAnimationIteration,
iterationStartTime,
iterationTimeStamp });
} else if (currentPhase == AnimationPhase::After) {
events.AppendElement(AnimationEventParams{ eAnimationEnd,
intervalEndTime,
endTimeStamp });
}
break;
case AnimationPhase::After:
if (currentPhase == AnimationPhase::Before) {
events.AppendElement(AnimationEventParams{ eAnimationStart,
intervalEndTime,
startTimeStamp});
events.AppendElement(AnimationEventParams{ eAnimationEnd,
intervalStartTime,
endTimeStamp });
} else if (currentPhase == AnimationPhase::Active) {
events.AppendElement(AnimationEventParams{ eAnimationStart,
intervalEndTime,
endTimeStamp });
}
break;
}
AutoTArray<EventPair, 2> events;
StickyTimeDuration initialAdvance = StickyTimeDuration(InitialAdvance());
StickyTimeDuration iterationStart = computedTiming.mDuration *
computedTiming.mCurrentIteration;
const StickyTimeDuration& activeDuration = computedTiming.mActiveDuration;
mPreviousPhase = currentPhase;
mPreviousIteration = currentIteration;
if (skippedFirstIteration) {
// Notify animationstart and animationiteration in same tick.
events.AppendElement(EventPair(eAnimationStart, initialAdvance));
events.AppendElement(EventPair(eAnimationIteration,
std::max(iterationStart, initialAdvance)));
} else if (!wasActive && isActive) {
events.AppendElement(EventPair(eAnimationStart, initialAdvance));
} else if (wasActive && !isActive) {
events.AppendElement(EventPair(eAnimationEnd, activeDuration));
} else if (wasActive && isActive && !isSameIteration) {
events.AppendElement(EventPair(eAnimationIteration, iterationStart));
} else if (skippedActivePhase) {
events.AppendElement(EventPair(eAnimationStart,
std::min(initialAdvance, activeDuration)));
events.AppendElement(EventPair(eAnimationEnd, activeDuration));
} else {
return; // No events need to be sent
}
for (const EventPair& pair : events){
for (const AnimationEventParams& event : events){
manager->QueueEvent(
AnimationEventInfo(owningElement, owningPseudoType,
pair.first(), mAnimationName,
pair.second(),
ElapsedTimeToTimeStamp(pair.second()),
event.mMessage, mAnimationName,
event.mElapsedTime, event.mTimeStamp,
this));
}
}