2015-05-03 22:32:37 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2014-08-10 11:06:44 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2015-04-21 04:22:09 +03:00
|
|
|
#include "Animation.h"
|
2014-08-30 10:11:57 +04:00
|
|
|
#include "AnimationUtils.h"
|
2015-04-21 04:22:09 +03:00
|
|
|
#include "mozilla/dom/AnimationBinding.h"
|
2015-07-31 00:25:00 +03:00
|
|
|
#include "mozilla/dom/AnimationPlaybackEvent.h"
|
2015-04-01 06:23:25 +03:00
|
|
|
#include "mozilla/AutoRestore.h"
|
2015-07-31 00:25:00 +03:00
|
|
|
#include "mozilla/AsyncEventDispatcher.h" //For AsyncEventDispatcher
|
2015-04-21 04:22:10 +03:00
|
|
|
#include "AnimationCommon.h" // For AnimationCollection,
|
2014-11-17 07:45:59 +03:00
|
|
|
// CommonAnimationManager
|
2014-11-17 07:45:58 +03:00
|
|
|
#include "nsIDocument.h" // For nsIDocument
|
2014-11-17 07:45:58 +03:00
|
|
|
#include "nsIPresShell.h" // For nsIPresShell
|
2014-10-20 08:55:43 +04:00
|
|
|
#include "nsLayoutUtils.h" // For PostRestyleEvent (remove after bug 1073336)
|
2015-07-30 00:21:00 +03:00
|
|
|
#include "nsThreadUtils.h" // For nsRunnableMethod and nsRevocableEventPtr
|
2015-04-21 04:22:09 +03:00
|
|
|
#include "PendingAnimationTracker.h" // For PendingAnimationTracker
|
2014-08-10 11:06:44 +04:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2015-06-09 05:13:53 +03:00
|
|
|
// Static members
|
|
|
|
uint64_t Animation::sNextSequenceNum = 0;
|
|
|
|
|
2015-07-31 00:23:00 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(Animation, DOMEventTargetHelper,
|
|
|
|
mTimeline,
|
|
|
|
mEffect,
|
|
|
|
mReady,
|
|
|
|
mFinished)
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF_INHERITED(Animation, DOMEventTargetHelper)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(Animation, DOMEventTargetHelper)
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Animation)
|
|
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
2014-08-10 11:06:44 +04:00
|
|
|
|
|
|
|
JSObject*
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
2014-08-10 11:06:44 +04:00
|
|
|
{
|
2015-04-21 04:22:09 +03:00
|
|
|
return dom::AnimationBinding::Wrap(aCx, this, aGivenProto);
|
2014-08-10 11:06:44 +04:00
|
|
|
}
|
|
|
|
|
2015-04-27 04:05:46 +03:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Animation interface:
|
|
|
|
//
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void
|
2015-04-30 16:06:43 +03:00
|
|
|
Animation::SetEffect(KeyframeEffectReadOnly* aEffect)
|
2015-04-27 04:05:46 +03:00
|
|
|
{
|
2015-08-18 10:57:00 +03:00
|
|
|
if (mEffect == aEffect) {
|
|
|
|
return;
|
|
|
|
}
|
2015-04-27 04:05:46 +03:00
|
|
|
if (mEffect) {
|
|
|
|
mEffect->SetParentTime(Nullable<TimeDuration>());
|
|
|
|
}
|
|
|
|
mEffect = aEffect;
|
|
|
|
if (mEffect) {
|
|
|
|
mEffect->SetParentTime(GetCurrentTime());
|
|
|
|
}
|
2015-08-17 07:59:45 +03:00
|
|
|
|
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2015-04-27 04:05:46 +03:00
|
|
|
}
|
|
|
|
|
2015-04-28 11:21:58 +03:00
|
|
|
void
|
|
|
|
Animation::SetTimeline(AnimationTimeline* aTimeline)
|
|
|
|
{
|
|
|
|
if (mTimeline == aTimeline) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-15 05:05:43 +03:00
|
|
|
if (mTimeline) {
|
|
|
|
mTimeline->RemoveAnimation(*this);
|
|
|
|
}
|
|
|
|
|
2015-04-28 11:21:58 +03:00
|
|
|
mTimeline = aTimeline;
|
|
|
|
|
|
|
|
// FIXME(spec): Once we implement the seeking defined in the spec
|
|
|
|
// surely this should be SeekFlag::DidSeek but the spec says otherwise.
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2015-04-28 11:21:58 +03:00
|
|
|
|
|
|
|
// FIXME: When we expose this method to script we'll need to call PostUpdate
|
|
|
|
// (but *not* when this method gets called from style).
|
|
|
|
}
|
|
|
|
|
2015-02-09 13:26:27 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::SetStartTime(const Nullable<TimeDuration>& aNewStartTime)
|
2015-02-09 13:26:27 +03:00
|
|
|
{
|
2015-04-28 06:49:12 +03:00
|
|
|
Nullable<TimeDuration> timelineTime;
|
2015-02-09 13:26:27 +03:00
|
|
|
if (mTimeline) {
|
|
|
|
// The spec says to check if the timeline is active (has a resolved time)
|
|
|
|
// before using it here, but we don't need to since it's harmless to set
|
|
|
|
// the already null time to null.
|
|
|
|
timelineTime = mTimeline->GetCurrentTime();
|
|
|
|
}
|
|
|
|
if (timelineTime.IsNull() && !aNewStartTime.IsNull()) {
|
|
|
|
mHoldTime.SetNull();
|
|
|
|
}
|
2015-04-28 06:49:12 +03:00
|
|
|
|
2015-02-09 13:26:27 +03:00
|
|
|
Nullable<TimeDuration> previousCurrentTime = GetCurrentTime();
|
|
|
|
mStartTime = aNewStartTime;
|
|
|
|
if (!aNewStartTime.IsNull()) {
|
2015-03-13 23:10:45 +03:00
|
|
|
if (mPlaybackRate != 0.0) {
|
|
|
|
mHoldTime.SetNull();
|
|
|
|
}
|
2015-02-09 13:26:27 +03:00
|
|
|
} else {
|
|
|
|
mHoldTime = previousCurrentTime;
|
|
|
|
}
|
|
|
|
|
2015-03-27 09:56:45 +03:00
|
|
|
CancelPendingTasks();
|
2015-02-09 13:26:27 +03:00
|
|
|
if (mReady) {
|
|
|
|
// We may have already resolved mReady, but in that case calling
|
|
|
|
// MaybeResolve is a no-op, so that's okay.
|
|
|
|
mReady->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2015-02-12 20:56:57 +03:00
|
|
|
PostUpdate();
|
2015-02-09 13:26:27 +03:00
|
|
|
}
|
|
|
|
|
2015-04-27 04:05:46 +03:00
|
|
|
// http://w3c.github.io/web-animations/#current-time
|
2014-10-20 08:55:45 +04:00
|
|
|
Nullable<TimeDuration>
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::GetCurrentTime() const
|
2014-08-10 11:06:44 +04:00
|
|
|
{
|
2014-10-20 08:55:45 +04:00
|
|
|
Nullable<TimeDuration> result;
|
|
|
|
if (!mHoldTime.IsNull()) {
|
|
|
|
result = mHoldTime;
|
2014-12-04 23:13:38 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-04-28 06:49:12 +03:00
|
|
|
if (mTimeline && !mStartTime.IsNull()) {
|
2014-10-20 08:55:45 +04:00
|
|
|
Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
|
2014-12-04 23:13:38 +03:00
|
|
|
if (!timelineTime.IsNull()) {
|
2015-03-13 23:10:45 +03:00
|
|
|
result.SetValue((timelineTime.Value() - mStartTime.Value())
|
|
|
|
.MultDouble(mPlaybackRate));
|
2014-10-20 08:55:45 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2014-08-10 11:06:44 +04:00
|
|
|
}
|
|
|
|
|
2015-03-09 19:50:39 +03:00
|
|
|
// Implements http://w3c.github.io/web-animations/#set-the-current-time
|
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::SetCurrentTime(const TimeDuration& aSeekTime)
|
2015-03-09 19:50:39 +03:00
|
|
|
{
|
|
|
|
SilentlySetCurrentTime(aSeekTime);
|
|
|
|
|
2015-03-27 09:56:45 +03:00
|
|
|
if (mPendingState == PendingState::PausePending) {
|
2015-05-18 05:41:19 +03:00
|
|
|
// Finish the pause operation
|
|
|
|
mHoldTime.SetValue(aSeekTime);
|
|
|
|
mStartTime.SetNull();
|
|
|
|
|
2015-03-27 09:56:45 +03:00
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeResolve(this);
|
|
|
|
}
|
2015-05-18 05:41:19 +03:00
|
|
|
CancelPendingTasks();
|
2015-03-27 09:56:45 +03:00
|
|
|
}
|
2015-03-09 19:50:39 +03:00
|
|
|
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async);
|
2015-03-09 19:50:39 +03:00
|
|
|
PostUpdate();
|
|
|
|
}
|
|
|
|
|
2015-03-13 23:10:45 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::SetPlaybackRate(double aPlaybackRate)
|
2015-03-13 23:10:45 +03:00
|
|
|
{
|
|
|
|
Nullable<TimeDuration> previousTime = GetCurrentTime();
|
|
|
|
mPlaybackRate = aPlaybackRate;
|
|
|
|
if (!previousTime.IsNull()) {
|
|
|
|
SetCurrentTime(previousTime.Value());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-20 08:55:45 +04:00
|
|
|
AnimationPlayState
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::PlayState() const
|
2014-10-20 08:55:45 +04:00
|
|
|
{
|
2015-03-27 09:56:45 +03:00
|
|
|
if (mPendingState != PendingState::NotPending) {
|
2014-12-18 02:42:41 +03:00
|
|
|
return AnimationPlayState::Pending;
|
|
|
|
}
|
|
|
|
|
2014-10-20 08:55:45 +04:00
|
|
|
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
2014-10-20 08:55:45 +04:00
|
|
|
if (currentTime.IsNull()) {
|
|
|
|
return AnimationPlayState::Idle;
|
|
|
|
}
|
|
|
|
|
2014-12-04 19:28:37 +03:00
|
|
|
if (mStartTime.IsNull()) {
|
2014-10-20 08:55:45 +04:00
|
|
|
return AnimationPlayState::Paused;
|
|
|
|
}
|
|
|
|
|
2015-04-15 02:48:21 +03:00
|
|
|
if ((mPlaybackRate > 0.0 && currentTime.Value() >= EffectEnd()) ||
|
2015-03-13 23:10:45 +03:00
|
|
|
(mPlaybackRate < 0.0 && currentTime.Value().ToMilliseconds() <= 0.0)) {
|
2014-10-20 08:55:45 +04:00
|
|
|
return AnimationPlayState::Finished;
|
|
|
|
}
|
|
|
|
|
|
|
|
return AnimationPlayState::Running;
|
|
|
|
}
|
|
|
|
|
2014-12-18 02:42:40 +03:00
|
|
|
Promise*
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::GetReady(ErrorResult& aRv)
|
2014-12-18 02:42:40 +03:00
|
|
|
{
|
2015-07-31 00:23:00 +03:00
|
|
|
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
|
|
|
|
if (!mReady && global) {
|
|
|
|
mReady = Promise::Create(global, aRv); // Lazily create on demand
|
2014-12-18 02:42:40 +03:00
|
|
|
}
|
|
|
|
if (!mReady) {
|
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
2015-03-18 16:22:11 +03:00
|
|
|
} else if (PlayState() != AnimationPlayState::Pending) {
|
|
|
|
mReady->MaybeResolve(this);
|
2014-12-18 02:42:40 +03:00
|
|
|
}
|
|
|
|
return mReady;
|
|
|
|
}
|
|
|
|
|
2015-03-18 16:22:11 +03:00
|
|
|
Promise*
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::GetFinished(ErrorResult& aRv)
|
2015-03-18 16:22:11 +03:00
|
|
|
{
|
2015-07-31 00:23:00 +03:00
|
|
|
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
|
|
|
|
if (!mFinished && global) {
|
|
|
|
mFinished = Promise::Create(global, aRv); // Lazily create on demand
|
2015-03-18 16:22:11 +03:00
|
|
|
}
|
|
|
|
if (!mFinished) {
|
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
2015-07-30 00:21:00 +03:00
|
|
|
} else if (mFinishedIsResolved) {
|
|
|
|
MaybeResolveFinishedPromise();
|
2015-03-18 16:22:11 +03:00
|
|
|
}
|
|
|
|
return mFinished;
|
|
|
|
}
|
|
|
|
|
2015-04-27 02:53:19 +03:00
|
|
|
void
|
|
|
|
Animation::Cancel()
|
|
|
|
{
|
|
|
|
DoCancel();
|
|
|
|
PostUpdate();
|
|
|
|
}
|
|
|
|
|
2015-04-27 04:05:46 +03:00
|
|
|
// https://w3c.github.io/web-animations/#finish-an-animation
|
2015-04-16 19:15:20 +03:00
|
|
|
void
|
|
|
|
Animation::Finish(ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
if (mPlaybackRate == 0 ||
|
|
|
|
(mPlaybackRate > 0 && EffectEnd() == TimeDuration::Forever())) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TimeDuration limit =
|
|
|
|
mPlaybackRate > 0 ? TimeDuration(EffectEnd()) : TimeDuration(0);
|
|
|
|
|
2015-07-14 22:45:00 +03:00
|
|
|
SilentlySetCurrentTime(limit);
|
2015-04-16 19:15:20 +03:00
|
|
|
|
2015-05-19 04:08:46 +03:00
|
|
|
// If we are paused or play-pending we need to fill in the start time in
|
|
|
|
// order to transition to the finished state.
|
|
|
|
//
|
|
|
|
// We only do this, however, if we have an active timeline. If we have an
|
|
|
|
// inactive timeline we can't transition into the finished state just like
|
|
|
|
// we can't transition to the running state (this finished state is really
|
|
|
|
// a substate of the running state).
|
|
|
|
if (mStartTime.IsNull() &&
|
|
|
|
mTimeline &&
|
|
|
|
!mTimeline->GetCurrentTime().IsNull()) {
|
|
|
|
mStartTime.SetValue(mTimeline->GetCurrentTime().Value() -
|
|
|
|
limit.MultDouble(1.0 / mPlaybackRate));
|
|
|
|
}
|
|
|
|
|
2015-07-14 22:45:00 +03:00
|
|
|
// If we just resolved the start time for a pause or play-pending
|
|
|
|
// animation, we need to clear the task. We don't do this as a branch of
|
|
|
|
// the above however since we can have a play-pending animation with a
|
|
|
|
// resolved start time if we aborted a pause operation.
|
|
|
|
if (!mStartTime.IsNull() &&
|
|
|
|
(mPendingState == PendingState::PlayPending ||
|
|
|
|
mPendingState == PendingState::PausePending)) {
|
|
|
|
if (mPendingState == PendingState::PausePending) {
|
|
|
|
mHoldTime.SetNull();
|
|
|
|
}
|
2015-04-16 19:15:20 +03:00
|
|
|
CancelPendingTasks();
|
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
}
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Sync);
|
2015-04-16 19:15:20 +03:00
|
|
|
PostUpdate();
|
|
|
|
}
|
|
|
|
|
2014-10-20 08:55:43 +04:00
|
|
|
void
|
2015-05-19 08:00:48 +03:00
|
|
|
Animation::Play(ErrorResult& aRv, LimitBehavior aLimitBehavior)
|
2014-10-20 08:55:43 +04:00
|
|
|
{
|
2015-05-19 08:00:48 +03:00
|
|
|
DoPlay(aRv, aLimitBehavior);
|
2014-11-17 07:46:01 +03:00
|
|
|
PostUpdate();
|
2014-10-20 08:55:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-05-19 08:55:26 +03:00
|
|
|
Animation::Pause(ErrorResult& aRv)
|
2014-10-20 08:55:43 +04:00
|
|
|
{
|
2015-05-19 08:55:26 +03:00
|
|
|
DoPause(aRv);
|
2014-11-17 07:46:01 +03:00
|
|
|
PostUpdate();
|
2014-10-20 08:55:43 +04:00
|
|
|
}
|
|
|
|
|
2015-07-09 23:54:00 +03:00
|
|
|
void
|
|
|
|
Animation::Reverse(ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mPlaybackRate == 0.0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SilentlySetPlaybackRate(-mPlaybackRate);
|
|
|
|
Play(aRv, LimitBehavior::AutoRewind);
|
|
|
|
}
|
|
|
|
|
2015-04-27 04:05:46 +03:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// JS wrappers for Animation interface:
|
|
|
|
//
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
2014-12-04 19:28:38 +03:00
|
|
|
Nullable<double>
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::GetStartTimeAsDouble() const
|
2014-12-04 19:28:38 +03:00
|
|
|
{
|
|
|
|
return AnimationUtils::TimeDurationToDouble(mStartTime);
|
|
|
|
}
|
|
|
|
|
2015-02-09 13:26:27 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::SetStartTimeAsDouble(const Nullable<double>& aStartTime)
|
2015-02-09 13:26:27 +03:00
|
|
|
{
|
|
|
|
return SetStartTime(AnimationUtils::DoubleToTimeDuration(aStartTime));
|
|
|
|
}
|
2015-04-21 04:22:09 +03:00
|
|
|
|
2014-10-20 08:55:45 +04:00
|
|
|
Nullable<double>
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::GetCurrentTimeAsDouble() const
|
2014-10-20 08:55:45 +04:00
|
|
|
{
|
|
|
|
return AnimationUtils::TimeDurationToDouble(GetCurrentTime());
|
|
|
|
}
|
|
|
|
|
2015-03-09 19:50:39 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::SetCurrentTimeAsDouble(const Nullable<double>& aCurrentTime,
|
2015-03-09 19:50:39 +03:00
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
if (aCurrentTime.IsNull()) {
|
|
|
|
if (!GetCurrentTime().IsNull()) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SetCurrentTime(TimeDuration::FromMilliseconds(aCurrentTime.Value()));
|
|
|
|
}
|
|
|
|
|
2015-04-27 04:05:46 +03:00
|
|
|
// ---------------------------------------------------------------------------
|
2014-08-10 11:06:48 +04:00
|
|
|
|
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::Tick()
|
2014-08-10 11:06:48 +04:00
|
|
|
{
|
2015-01-09 01:57:58 +03:00
|
|
|
// Since we are not guaranteed to get only one call per refresh driver tick,
|
|
|
|
// it's possible that mPendingReadyTime is set to a time in the future.
|
|
|
|
// In that case, we should wait until the next refresh driver tick before
|
|
|
|
// resuming.
|
2015-03-27 09:56:45 +03:00
|
|
|
if (mPendingState != PendingState::NotPending &&
|
2015-01-09 01:57:58 +03:00
|
|
|
!mPendingReadyTime.IsNull() &&
|
2015-04-28 06:49:12 +03:00
|
|
|
mTimeline &&
|
|
|
|
!mTimeline->GetCurrentTime().IsNull() &&
|
2015-01-09 01:57:58 +03:00
|
|
|
mPendingReadyTime.Value() <= mTimeline->GetCurrentTime().Value()) {
|
2015-04-01 06:23:24 +03:00
|
|
|
FinishPendingAt(mPendingReadyTime.Value());
|
2015-01-09 01:57:58 +03:00
|
|
|
mPendingReadyTime.SetNull();
|
|
|
|
}
|
|
|
|
|
2015-04-21 04:22:09 +03:00
|
|
|
if (IsPossiblyOrphanedPendingAnimation()) {
|
2015-01-09 01:57:58 +03:00
|
|
|
MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
|
2015-04-21 04:22:09 +03:00
|
|
|
"Orphaned pending animtaions should have an active timeline");
|
2015-04-01 06:23:24 +03:00
|
|
|
FinishPendingAt(mTimeline->GetCurrentTime().Value());
|
2015-01-09 01:57:58 +03:00
|
|
|
}
|
2014-12-25 10:28:24 +03:00
|
|
|
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2015-08-18 10:11:55 +03:00
|
|
|
|
|
|
|
// FIXME: Detect the no-change case and don't request a restyle at all
|
2015-08-18 10:11:55 +03:00
|
|
|
// FIXME: Detect changes to IsPlaying() state and request RestyleType::Layer
|
|
|
|
// so that layers get updated immediately
|
2015-08-18 10:11:55 +03:00
|
|
|
AnimationCollection* collection = GetCollection();
|
|
|
|
if (collection) {
|
|
|
|
collection->RequestRestyle(CanThrottle() ?
|
|
|
|
AnimationCollection::RestyleType::Throttled :
|
|
|
|
AnimationCollection::RestyleType::Standard);
|
|
|
|
}
|
2014-08-10 11:06:47 +04:00
|
|
|
}
|
|
|
|
|
2015-01-09 01:57:58 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::TriggerOnNextTick(const Nullable<TimeDuration>& aReadyTime)
|
2015-01-09 01:57:58 +03:00
|
|
|
{
|
|
|
|
// Normally we expect the play state to be pending but it's possible that,
|
2015-04-21 04:22:09 +03:00
|
|
|
// due to the handling of possibly orphaned animations in Tick(), this
|
|
|
|
// animation got started whilst still being in another document's pending
|
|
|
|
// animation map.
|
2015-01-09 01:57:58 +03:00
|
|
|
if (PlayState() != AnimationPlayState::Pending) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-09 01:57:58 +03:00
|
|
|
// If aReadyTime.IsNull() we'll detect this in Tick() where we check for
|
2015-04-21 04:22:09 +03:00
|
|
|
// orphaned animations and trigger this animation anyway
|
2015-01-09 01:57:58 +03:00
|
|
|
mPendingReadyTime = aReadyTime;
|
|
|
|
}
|
|
|
|
|
2014-12-04 19:28:38 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::TriggerNow()
|
2014-12-04 19:28:38 +03:00
|
|
|
{
|
2015-06-09 05:13:53 +03:00
|
|
|
// Normally we expect the play state to be pending but when an animation
|
|
|
|
// is cancelled and its rendered document can't be reached, we can end up
|
|
|
|
// with the animation still in a pending player tracker even after it is
|
|
|
|
// no longer pending.
|
|
|
|
if (PlayState() != AnimationPlayState::Pending) {
|
|
|
|
return;
|
|
|
|
}
|
2015-04-28 06:49:12 +03:00
|
|
|
|
|
|
|
// If we don't have an active timeline we can't trigger the animation.
|
|
|
|
// However, this is a test-only method that we don't expect to be used in
|
|
|
|
// conjunction with animations without an active timeline so generate
|
|
|
|
// a warning if we do find ourselves in that situation.
|
|
|
|
if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) {
|
|
|
|
NS_WARNING("Failed to trigger an animation with an active timeline");
|
|
|
|
return;
|
|
|
|
}
|
2014-12-18 02:42:40 +03:00
|
|
|
|
2015-04-01 06:23:24 +03:00
|
|
|
FinishPendingAt(mTimeline->GetCurrentTime().Value());
|
2014-12-04 19:28:38 +03:00
|
|
|
}
|
|
|
|
|
2015-02-03 08:08:37 +03:00
|
|
|
Nullable<TimeDuration>
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::GetCurrentOrPendingStartTime() const
|
2015-02-03 08:08:37 +03:00
|
|
|
{
|
|
|
|
Nullable<TimeDuration> result;
|
|
|
|
|
|
|
|
if (!mStartTime.IsNull()) {
|
|
|
|
result = mStartTime;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mPendingReadyTime.IsNull() || mHoldTime.IsNull()) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the equivalent start time from the pending ready time.
|
|
|
|
// This is the same as the calculation performed in ResumeAt and will
|
|
|
|
// need to incorporate the playbackRate when implemented (bug 1127380).
|
|
|
|
result.SetValue(mPendingReadyTime.Value() - mHoldTime.Value());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-04-27 04:05:46 +03:00
|
|
|
// http://w3c.github.io/web-animations/#silently-set-the-current-time
|
|
|
|
void
|
|
|
|
Animation::SilentlySetCurrentTime(const TimeDuration& aSeekTime)
|
|
|
|
{
|
|
|
|
if (!mHoldTime.IsNull() ||
|
|
|
|
mStartTime.IsNull() ||
|
|
|
|
!mTimeline ||
|
|
|
|
mTimeline->GetCurrentTime().IsNull() ||
|
|
|
|
mPlaybackRate == 0.0) {
|
|
|
|
mHoldTime.SetValue(aSeekTime);
|
|
|
|
if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) {
|
|
|
|
mStartTime.SetNull();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mStartTime.SetValue(mTimeline->GetCurrentTime().Value() -
|
|
|
|
(aSeekTime.MultDouble(1 / mPlaybackRate)));
|
|
|
|
}
|
|
|
|
|
|
|
|
mPreviousCurrentTime.SetNull();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Animation::SilentlySetPlaybackRate(double aPlaybackRate)
|
|
|
|
{
|
|
|
|
Nullable<TimeDuration> previousTime = GetCurrentTime();
|
|
|
|
mPlaybackRate = aPlaybackRate;
|
|
|
|
if (!previousTime.IsNull()) {
|
|
|
|
SilentlySetCurrentTime(previousTime.Value());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-18 02:42:41 +03:00
|
|
|
void
|
2015-04-27 02:53:19 +03:00
|
|
|
Animation::DoCancel()
|
2014-12-18 02:42:41 +03:00
|
|
|
{
|
2015-03-27 09:56:45 +03:00
|
|
|
if (mPendingState != PendingState::NotPending) {
|
2015-03-27 09:56:45 +03:00
|
|
|
CancelPendingTasks();
|
2014-12-18 02:42:41 +03:00
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-18 16:22:11 +03:00
|
|
|
if (mFinished) {
|
|
|
|
mFinished->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
|
|
|
}
|
2015-07-30 00:21:00 +03:00
|
|
|
ResetFinishedPromise();
|
2015-03-18 16:22:11 +03:00
|
|
|
|
2015-07-31 00:26:00 +03:00
|
|
|
DispatchPlaybackEvent(NS_LITERAL_STRING("cancel"));
|
|
|
|
|
2014-12-18 02:42:41 +03:00
|
|
|
mHoldTime.SetNull();
|
|
|
|
mStartTime.SetNull();
|
2015-03-14 08:34:40 +03:00
|
|
|
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2014-12-18 02:42:41 +03:00
|
|
|
}
|
|
|
|
|
2015-03-14 08:34:40 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::UpdateRelevance()
|
2015-03-14 08:34:40 +03:00
|
|
|
{
|
2015-03-14 08:34:40 +03:00
|
|
|
bool wasRelevant = mIsRelevant;
|
2015-04-15 02:48:21 +03:00
|
|
|
mIsRelevant = HasCurrentEffect() || IsInEffect();
|
2015-03-14 08:34:40 +03:00
|
|
|
|
|
|
|
// Notify animation observers.
|
|
|
|
if (wasRelevant && !mIsRelevant) {
|
|
|
|
nsNodeUtils::AnimationRemoved(this);
|
|
|
|
} else if (!wasRelevant && mIsRelevant) {
|
|
|
|
nsNodeUtils::AnimationAdded(this);
|
|
|
|
}
|
2015-03-14 08:34:40 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 05:13:53 +03:00
|
|
|
bool
|
|
|
|
Animation::HasLowerCompositeOrderThan(const Animation& aOther) const
|
|
|
|
{
|
|
|
|
// We only ever sort non-idle animations so we don't ever expect
|
|
|
|
// mSequenceNum to be set to kUnsequenced
|
|
|
|
MOZ_ASSERT(mSequenceNum != kUnsequenced &&
|
|
|
|
aOther.mSequenceNum != kUnsequenced,
|
|
|
|
"Animations to compare should not be idle");
|
|
|
|
MOZ_ASSERT(mSequenceNum != aOther.mSequenceNum || &aOther == this,
|
|
|
|
"Sequence numbers should be unique");
|
|
|
|
|
|
|
|
return mSequenceNum < aOther.mSequenceNum;
|
|
|
|
}
|
|
|
|
|
2014-10-20 08:55:45 +04:00
|
|
|
bool
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::CanThrottle() const
|
2014-10-20 08:55:45 +04:00
|
|
|
{
|
2015-08-07 06:29:35 +03:00
|
|
|
// This method answers the question, "Can we get away with NOT updating
|
|
|
|
// style on the main thread for this animation on this tick?"
|
|
|
|
|
|
|
|
// Ignore animations that were never going to have any effect anyway.
|
|
|
|
if (!mEffect || mEffect->Properties().IsEmpty()) {
|
2014-10-20 08:55:45 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-08-07 06:29:35 +03:00
|
|
|
// Finished animations can be throttled unless this is the first
|
|
|
|
// sample since finishing. In that case we need an unthrottled sample
|
|
|
|
// so we can apply the correct end-of-animation behavior on the main
|
|
|
|
// thread (either removing the animation style or applying the fill mode).
|
|
|
|
if (PlayState() == AnimationPlayState::Finished) {
|
|
|
|
return mFinishedAtLastComposeStyle;
|
2014-10-20 08:55:45 +04:00
|
|
|
}
|
|
|
|
|
2015-08-07 06:29:35 +03:00
|
|
|
// We should also ignore animations which are not "in effect"--i.e. not
|
|
|
|
// producing an output. This includes animations that are idle or in their
|
|
|
|
// delay phase but with no backwards fill.
|
|
|
|
//
|
|
|
|
// Note that unlike newly-finished animations, we don't need to worry about
|
|
|
|
// special handling for newly-idle animations or animations that are newly
|
|
|
|
// yet-to-start since any operation that would cause that change (e.g. a call
|
|
|
|
// to cancel() on the animation, or seeking its current time) will trigger an
|
|
|
|
// unthrottled sample.
|
|
|
|
if (!IsInEffect()) {
|
2014-10-20 08:55:45 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-08-07 06:29:35 +03:00
|
|
|
return mIsRunningOnCompositor;
|
2014-10-20 08:55:45 +04:00
|
|
|
}
|
|
|
|
|
2014-10-20 08:55:46 +04:00
|
|
|
void
|
2015-07-29 04:57:39 +03:00
|
|
|
Animation::ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule,
|
2015-04-21 04:22:09 +03:00
|
|
|
nsCSSPropertySet& aSetProperties,
|
|
|
|
bool& aNeedsRefreshes)
|
2014-10-20 08:55:46 +04:00
|
|
|
{
|
2015-08-07 06:29:35 +03:00
|
|
|
if (!mEffect) {
|
2014-10-20 08:55:46 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
Bug 1078122 part 6 - Store the previous finished state; r=dholbert
AnimationPlayer::CanThrottle determines if an animation player has just finished
by inspecting the value of mLastNotification. This is problematic for two
reasons:
1. mLastNotification is intended to be used for events (as the XXX comment
notes)
2. mLastNotification is specific to CSS Animations and should be moved to
CSSAnimationPlayer.
To address this, this patch adds an extra member mIsPreviousStateFinished. The
Web Animations spec already defines animation players as having such a member:
http://w3c.github.io/web-animations/#previous-finished-state
We set it to true when we calculate the style for an animation that has
finished. This differs slightly from the code it is replacing as explained
below.
In the case of CSS Animations we perform the following sequence of steps on each
sample.
1. EnsureStyleRuleFor (calls CanThrottle, and maybe ComposeStyle)
2. GetEventsForCurrentTime
In the existing code, we update mLastNotification in (2) which happens on every
sample, even throttled samples.
In this patch, however, we update mIsPreviousStateFinished in (1) during the
ComposeStyle step which only happens for unthrottled samples. So, as of this
patch, in CanThrottle, we ask "have we newly entered the finished state since
the last *unthrottled* sample?", whereas previously we simply looked for
a change since the last sample, throttled or not. However, if the answer to the
question is "yes", then we'll run an unthrottled sample and update
mIsPreviousStateFinished so these should be functionally equivalent.
Another subtle difference is that this patch looks at the player's finished
state rather than the animation phase of its source content, and these will
produce different results in the case where the player is paused. However, since
paused animations are not run on the compositor, this should not matter.
In the case of CSS Transitions, AnimationPlayer::CanThrottle() is not currently
used and so mIsPreviousStateFinished is irrelevant.
Ultimately, both the existing and the new code is somewhat fragile but hopefully
this will be addressed by:
* Replacing mIsPreviousStateFinished with inspecting whether the finished
promise is settled (bug 1074630),
* Merging more of the code in nsAnimationManager and nsTransitionManager and
applying a unified approach to sampling that better accommodates these
considerations.
2014-10-20 08:55:47 +04:00
|
|
|
AnimationPlayState playState = PlayState();
|
2014-12-18 02:42:41 +03:00
|
|
|
if (playState == AnimationPlayState::Running ||
|
|
|
|
playState == AnimationPlayState::Pending) {
|
2014-10-20 08:55:46 +04:00
|
|
|
aNeedsRefreshes = true;
|
|
|
|
}
|
|
|
|
|
2015-08-07 06:29:35 +03:00
|
|
|
if (!IsInEffect()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-01 06:23:25 +03:00
|
|
|
// In order to prevent flicker, there are a few cases where we want to use
|
|
|
|
// a different time for rendering that would otherwise be returned by
|
|
|
|
// GetCurrentTime. These are:
|
|
|
|
//
|
|
|
|
// (a) For animations that are pausing but which are still running on the
|
|
|
|
// compositor. In this case we send a layer transaction that removes the
|
|
|
|
// animation but which also contains the animation values calculated on
|
|
|
|
// the main thread. To prevent flicker when this occurs we want to ensure
|
|
|
|
// the timeline time used to calculate the main thread animation values
|
|
|
|
// does not lag far behind the time used on the compositor. Ideally we
|
|
|
|
// would like to use the "animation ready time" calculated at the end of
|
|
|
|
// the layer transaction as the timeline time but it will be too late to
|
|
|
|
// update the style rule at that point so instead we just use the current
|
|
|
|
// wallclock time.
|
|
|
|
//
|
|
|
|
// (b) For animations that are pausing that we have already taken off the
|
|
|
|
// compositor. In this case we record a pending ready time but we don't
|
|
|
|
// apply it until the next tick. However, while waiting for the next tick,
|
|
|
|
// we should still use the pending ready time as the timeline time. If we
|
|
|
|
// use the regular timeline time the animation may appear jump backwards
|
|
|
|
// if the main thread's timeline time lags behind the compositor.
|
|
|
|
//
|
|
|
|
// (c) For animations that are play-pending due to an aborted pause operation
|
|
|
|
// (i.e. a pause operation that was interrupted before we entered the
|
|
|
|
// paused state). When we cancel a pending pause we might momentarily take
|
|
|
|
// the animation off the compositor, only to re-add it moments later. In
|
|
|
|
// that case the compositor might have been ahead of the main thread so we
|
|
|
|
// should use the current wallclock time to ensure the animation doesn't
|
|
|
|
// temporarily jump backwards.
|
|
|
|
//
|
|
|
|
// To address each of these cases we temporarily tweak the hold time
|
|
|
|
// immediately before updating the style rule and then restore it immediately
|
|
|
|
// afterwards. This is purely to prevent visual flicker. Other behavior
|
|
|
|
// such as dispatching events continues to rely on the regular timeline time.
|
|
|
|
{
|
|
|
|
AutoRestore<Nullable<TimeDuration>> restoreHoldTime(mHoldTime);
|
|
|
|
bool updatedHoldTime = false;
|
|
|
|
|
2015-04-02 06:30:17 +03:00
|
|
|
AnimationPlayState playState = PlayState();
|
|
|
|
|
|
|
|
if (playState == AnimationPlayState::Pending &&
|
2015-04-01 06:23:25 +03:00
|
|
|
mHoldTime.IsNull() &&
|
|
|
|
!mStartTime.IsNull()) {
|
|
|
|
Nullable<TimeDuration> timeToUse = mPendingReadyTime;
|
|
|
|
if (timeToUse.IsNull() &&
|
|
|
|
mTimeline &&
|
2015-04-28 05:17:10 +03:00
|
|
|
mTimeline->TracksWallclockTime()) {
|
2015-04-01 06:23:25 +03:00
|
|
|
timeToUse = mTimeline->ToTimelineTime(TimeStamp::Now());
|
|
|
|
}
|
|
|
|
if (!timeToUse.IsNull()) {
|
|
|
|
mHoldTime.SetValue((timeToUse.Value() - mStartTime.Value())
|
|
|
|
.MultDouble(mPlaybackRate));
|
2015-04-15 02:48:21 +03:00
|
|
|
// Push the change down to the effect
|
|
|
|
UpdateEffect();
|
2015-04-01 06:23:25 +03:00
|
|
|
updatedHoldTime = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-15 02:48:21 +03:00
|
|
|
mEffect->ComposeStyle(aStyleRule, aSetProperties);
|
2015-04-01 06:23:25 +03:00
|
|
|
|
|
|
|
if (updatedHoldTime) {
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2015-04-01 06:23:25 +03:00
|
|
|
}
|
2015-04-02 06:30:17 +03:00
|
|
|
|
|
|
|
mFinishedAtLastComposeStyle = (playState == AnimationPlayState::Finished);
|
2015-04-01 06:23:25 +03:00
|
|
|
}
|
2014-10-20 08:55:46 +04:00
|
|
|
}
|
|
|
|
|
2015-08-17 21:28:00 +03:00
|
|
|
void
|
|
|
|
Animation::NotifyEffectTimingUpdated()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mEffect,
|
|
|
|
"We should only update timing effect when we have a target "
|
|
|
|
"effect");
|
|
|
|
UpdateTiming(Animation::SeekFlag::NoSeek,
|
|
|
|
Animation::SyncNotifyFlag::Async);
|
|
|
|
}
|
|
|
|
|
2015-04-27 04:05:46 +03:00
|
|
|
// http://w3c.github.io/web-animations/#play-an-animation
|
2014-11-17 07:46:01 +03:00
|
|
|
void
|
2015-05-19 08:00:48 +03:00
|
|
|
Animation::DoPlay(ErrorResult& aRv, LimitBehavior aLimitBehavior)
|
2014-11-17 07:46:01 +03:00
|
|
|
{
|
2015-04-01 06:23:25 +03:00
|
|
|
bool abortedPause = mPendingState == PendingState::PausePending;
|
|
|
|
|
2014-12-04 19:28:37 +03:00
|
|
|
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
2015-03-13 23:10:45 +03:00
|
|
|
if (mPlaybackRate > 0.0 &&
|
2015-03-18 16:22:11 +03:00
|
|
|
(currentTime.IsNull() ||
|
|
|
|
(aLimitBehavior == LimitBehavior::AutoRewind &&
|
|
|
|
(currentTime.Value().ToMilliseconds() < 0.0 ||
|
2015-04-15 02:48:21 +03:00
|
|
|
currentTime.Value() >= EffectEnd())))) {
|
2014-12-04 19:28:37 +03:00
|
|
|
mHoldTime.SetValue(TimeDuration(0));
|
2015-03-13 23:10:45 +03:00
|
|
|
} else if (mPlaybackRate < 0.0 &&
|
2015-03-18 16:22:11 +03:00
|
|
|
(currentTime.IsNull() ||
|
|
|
|
(aLimitBehavior == LimitBehavior::AutoRewind &&
|
|
|
|
(currentTime.Value().ToMilliseconds() <= 0.0 ||
|
2015-04-15 02:48:21 +03:00
|
|
|
currentTime.Value() > EffectEnd())))) {
|
2015-05-19 08:00:48 +03:00
|
|
|
if (EffectEnd() == TimeDuration::Forever()) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
return;
|
|
|
|
}
|
2015-04-15 02:48:21 +03:00
|
|
|
mHoldTime.SetValue(TimeDuration(EffectEnd()));
|
2015-03-13 23:10:45 +03:00
|
|
|
} else if (mPlaybackRate == 0.0 && currentTime.IsNull()) {
|
|
|
|
mHoldTime.SetValue(TimeDuration(0));
|
|
|
|
}
|
|
|
|
|
2015-05-19 08:00:48 +03:00
|
|
|
bool reuseReadyPromise = false;
|
|
|
|
if (mPendingState != PendingState::NotPending) {
|
|
|
|
CancelPendingTasks();
|
|
|
|
reuseReadyPromise = true;
|
|
|
|
}
|
|
|
|
|
2015-04-01 06:23:25 +03:00
|
|
|
// If the hold time is null then we're either already playing normally (and
|
|
|
|
// we can ignore this call) or we aborted a pending pause operation (in which
|
|
|
|
// case, for consistency, we need to go through the motions of doing an
|
|
|
|
// asynchronous start even though we already have a resolved start time).
|
|
|
|
if (mHoldTime.IsNull() && !abortedPause) {
|
2014-11-17 07:46:01 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-08 10:17:13 +03:00
|
|
|
// Clear the start time until we resolve a new one. We do this except
|
|
|
|
// for the case where we are aborting a pause and don't have a hold time.
|
|
|
|
//
|
|
|
|
// If we're aborting a pause and *do* have a hold time (e.g. because
|
|
|
|
// the animation is finished or we just applied the auto-rewind behavior
|
|
|
|
// above) we should respect it by clearing the start time. If we *don't*
|
|
|
|
// have a hold time we should keep the current start time so that the
|
|
|
|
// the animation continues moving uninterrupted by the aborted pause.
|
|
|
|
//
|
|
|
|
// (If we're not aborting a pause, mHoldTime must be resolved by now
|
|
|
|
// or else we would have returned above.)
|
|
|
|
if (!mHoldTime.IsNull()) {
|
2015-04-01 06:23:25 +03:00
|
|
|
mStartTime.SetNull();
|
|
|
|
}
|
2015-03-24 03:21:08 +03:00
|
|
|
|
2015-03-27 09:56:45 +03:00
|
|
|
if (!reuseReadyPromise) {
|
|
|
|
// Clear ready promise. We'll create a new one lazily.
|
|
|
|
mReady = nullptr;
|
|
|
|
}
|
|
|
|
|
2015-03-27 09:56:45 +03:00
|
|
|
mPendingState = PendingState::PlayPending;
|
2014-12-25 10:28:24 +03:00
|
|
|
|
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
2015-06-09 05:13:53 +03:00
|
|
|
if (doc) {
|
|
|
|
PendingAnimationTracker* tracker =
|
|
|
|
doc->GetOrCreatePendingAnimationTracker();
|
|
|
|
tracker->AddPlayPending(*this);
|
|
|
|
} else {
|
2015-03-27 09:56:45 +03:00
|
|
|
TriggerOnNextTick(Nullable<TimeDuration>());
|
2014-12-25 10:28:24 +03:00
|
|
|
}
|
|
|
|
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2014-11-17 07:46:01 +03:00
|
|
|
}
|
|
|
|
|
2015-04-27 04:05:46 +03:00
|
|
|
// http://w3c.github.io/web-animations/#pause-an-animation
|
2014-11-17 07:46:01 +03:00
|
|
|
void
|
2015-05-19 08:55:26 +03:00
|
|
|
Animation::DoPause(ErrorResult& aRv)
|
2014-11-17 07:46:01 +03:00
|
|
|
{
|
2015-04-14 03:07:41 +03:00
|
|
|
if (IsPausedOrPausing()) {
|
2015-04-01 06:23:25 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-19 08:55:26 +03:00
|
|
|
// If we are transitioning from idle, fill in the current time
|
|
|
|
if (GetCurrentTime().IsNull()) {
|
|
|
|
if (mPlaybackRate >= 0.0) {
|
|
|
|
mHoldTime.SetValue(TimeDuration(0));
|
|
|
|
} else {
|
|
|
|
if (EffectEnd() == TimeDuration::Forever()) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mHoldTime.SetValue(TimeDuration(EffectEnd()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-01 06:23:25 +03:00
|
|
|
bool reuseReadyPromise = false;
|
2015-03-27 09:56:45 +03:00
|
|
|
if (mPendingState == PendingState::PlayPending) {
|
2015-03-27 09:56:45 +03:00
|
|
|
CancelPendingTasks();
|
2015-04-01 06:23:25 +03:00
|
|
|
reuseReadyPromise = true;
|
2014-11-17 07:46:01 +03:00
|
|
|
}
|
2014-12-18 02:42:41 +03:00
|
|
|
|
2014-12-04 19:28:37 +03:00
|
|
|
// Mark this as no longer running on the compositor so that next time
|
|
|
|
// we update animations we won't throttle them and will have a chance
|
|
|
|
// to remove the animation from any layer it might be on.
|
2014-11-17 07:46:01 +03:00
|
|
|
mIsRunningOnCompositor = false;
|
|
|
|
|
2015-04-01 06:23:25 +03:00
|
|
|
if (!reuseReadyPromise) {
|
|
|
|
// Clear ready promise. We'll create a new one lazily.
|
|
|
|
mReady = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
mPendingState = PendingState::PausePending;
|
|
|
|
|
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
2015-06-09 05:13:53 +03:00
|
|
|
if (doc) {
|
|
|
|
PendingAnimationTracker* tracker =
|
|
|
|
doc->GetOrCreatePendingAnimationTracker();
|
|
|
|
tracker->AddPausePending(*this);
|
|
|
|
} else {
|
2015-04-01 06:23:25 +03:00
|
|
|
TriggerOnNextTick(Nullable<TimeDuration>());
|
|
|
|
}
|
|
|
|
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2014-11-17 07:46:01 +03:00
|
|
|
}
|
|
|
|
|
2015-01-09 01:57:58 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::ResumeAt(const TimeDuration& aReadyTime)
|
2015-01-09 01:57:58 +03:00
|
|
|
{
|
2015-04-21 04:22:09 +03:00
|
|
|
// This method is only expected to be called for an animation that is
|
2015-01-09 01:57:58 +03:00
|
|
|
// waiting to play. We can easily adapt it to handle other states
|
|
|
|
// but it's currently not necessary.
|
2015-03-27 09:56:45 +03:00
|
|
|
MOZ_ASSERT(mPendingState == PendingState::PlayPending,
|
2015-04-21 04:22:09 +03:00
|
|
|
"Expected to resume a play-pending animation");
|
2015-04-01 06:23:25 +03:00
|
|
|
MOZ_ASSERT(mHoldTime.IsNull() != mStartTime.IsNull(),
|
2015-04-21 04:22:09 +03:00
|
|
|
"An animation in the play-pending state should have either a"
|
2015-04-01 06:23:25 +03:00
|
|
|
" resolved hold time or resolved start time (but not both)");
|
2015-01-09 01:57:58 +03:00
|
|
|
|
2015-04-01 06:23:25 +03:00
|
|
|
// If we aborted a pending pause operation we will already have a start time
|
|
|
|
// we should use. In all other cases, we resolve it from the ready time.
|
|
|
|
if (mStartTime.IsNull()) {
|
|
|
|
if (mPlaybackRate != 0) {
|
2015-03-30 18:27:05 +03:00
|
|
|
mStartTime.SetValue(aReadyTime -
|
|
|
|
(mHoldTime.Value().MultDouble(1 / mPlaybackRate)));
|
2015-04-01 06:23:25 +03:00
|
|
|
mHoldTime.SetNull();
|
|
|
|
} else {
|
|
|
|
mStartTime.SetValue(aReadyTime);
|
|
|
|
}
|
2015-03-13 23:10:45 +03:00
|
|
|
}
|
2015-03-27 09:56:45 +03:00
|
|
|
mPendingState = PendingState::NotPending;
|
2015-01-09 01:57:58 +03:00
|
|
|
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2015-01-09 01:57:58 +03:00
|
|
|
|
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-01 06:23:24 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::PauseAt(const TimeDuration& aReadyTime)
|
2015-04-01 06:23:24 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(mPendingState == PendingState::PausePending,
|
2015-04-21 04:22:09 +03:00
|
|
|
"Expected to pause a pause-pending animation");
|
2015-04-01 06:23:24 +03:00
|
|
|
|
|
|
|
if (!mStartTime.IsNull()) {
|
|
|
|
mHoldTime.SetValue((aReadyTime - mStartTime.Value())
|
|
|
|
.MultDouble(mPlaybackRate));
|
|
|
|
}
|
|
|
|
mStartTime.SetNull();
|
|
|
|
mPendingState = PendingState::NotPending;
|
|
|
|
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
2015-04-01 06:23:24 +03:00
|
|
|
|
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-18 16:22:11 +03:00
|
|
|
void
|
2015-07-30 00:21:00 +03:00
|
|
|
Animation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag)
|
2015-03-18 16:22:11 +03:00
|
|
|
{
|
2015-06-09 05:13:53 +03:00
|
|
|
// Update the sequence number each time we transition in or out of the
|
|
|
|
// idle state
|
2015-06-09 05:13:54 +03:00
|
|
|
if (!IsUsingCustomCompositeOrder()) {
|
|
|
|
if (PlayState() == AnimationPlayState::Idle) {
|
|
|
|
mSequenceNum = kUnsequenced;
|
|
|
|
} else if (mSequenceNum == kUnsequenced) {
|
|
|
|
mSequenceNum = sNextSequenceNum++;
|
|
|
|
}
|
2015-06-09 05:13:53 +03:00
|
|
|
}
|
|
|
|
|
2015-04-15 02:48:21 +03:00
|
|
|
// We call UpdateFinishedState before UpdateEffect because the former
|
2015-03-18 16:22:11 +03:00
|
|
|
// can change the current time, which is used by the latter.
|
2015-07-30 00:21:00 +03:00
|
|
|
UpdateFinishedState(aSeekFlag, aSyncNotifyFlag);
|
2015-04-15 02:48:21 +03:00
|
|
|
UpdateEffect();
|
2015-06-15 05:05:43 +03:00
|
|
|
|
|
|
|
// Unconditionally Add/Remove from the timeline. This is ok because if the
|
|
|
|
// animation has already been added/removed (which will be true more often
|
|
|
|
// than not) the work done by AnimationTimeline/DocumentTimeline is still
|
|
|
|
// negligible and its easier than trying to detect whenever we are switching
|
|
|
|
// to/from being relevant.
|
|
|
|
//
|
|
|
|
// We need to do this after calling UpdateEffect since it updates some
|
|
|
|
// cached state used by IsRelevant.
|
|
|
|
//
|
|
|
|
// Note that we only store relevant animations on the timeline since they
|
|
|
|
// are the only ones that need ticks and are the only ones returned from
|
|
|
|
// AnimationTimeline::GetAnimations. Storing any more than that would mean
|
|
|
|
// that we fail to garbage collect irrelevant animations since the timeline
|
|
|
|
// keeps a strong reference to each animation.
|
|
|
|
//
|
|
|
|
// Once we tick animations from the their timeline, and once we expect
|
|
|
|
// timelines to go in and out of being inactive, we will also need to store
|
|
|
|
// non-idle animations that are waiting for their timeline to become active
|
|
|
|
// on their timeline (as otherwise once the timeline becomes active it will
|
|
|
|
// have no way of notifying its animations). For now, however, we can
|
|
|
|
// simply store just the relevant animations.
|
|
|
|
if (mTimeline) {
|
2015-06-15 05:05:43 +03:00
|
|
|
// FIXME: Once we expect animations to go back and forth betweeen being
|
|
|
|
// inactive and active, we will need to store more than just relevant
|
|
|
|
// animations on the timeline. This is because an animation might be
|
|
|
|
// deemed irrelevant because its timeline is inactive. If it is removed
|
|
|
|
// from the timeline at that point the timeline will have no way of
|
|
|
|
// getting the animation to add itself again once it becomes active.
|
2015-06-15 05:05:43 +03:00
|
|
|
if (IsRelevant()) {
|
|
|
|
mTimeline->AddAnimation(*this);
|
|
|
|
} else {
|
|
|
|
mTimeline->RemoveAnimation(*this);
|
|
|
|
}
|
|
|
|
}
|
2015-03-18 16:22:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-07-30 00:21:00 +03:00
|
|
|
Animation::UpdateFinishedState(SeekFlag aSeekFlag,
|
|
|
|
SyncNotifyFlag aSyncNotifyFlag)
|
2015-03-18 16:22:11 +03:00
|
|
|
{
|
|
|
|
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
2015-04-15 02:48:21 +03:00
|
|
|
TimeDuration effectEnd = TimeDuration(EffectEnd());
|
2015-03-18 16:22:11 +03:00
|
|
|
|
|
|
|
if (!mStartTime.IsNull() &&
|
|
|
|
mPendingState == PendingState::NotPending) {
|
|
|
|
if (mPlaybackRate > 0.0 &&
|
|
|
|
!currentTime.IsNull() &&
|
2015-04-15 02:48:21 +03:00
|
|
|
currentTime.Value() >= effectEnd) {
|
2015-05-11 11:17:06 +03:00
|
|
|
if (aSeekFlag == SeekFlag::DidSeek) {
|
2015-03-18 16:22:11 +03:00
|
|
|
mHoldTime = currentTime;
|
|
|
|
} else if (!mPreviousCurrentTime.IsNull()) {
|
2015-04-15 02:48:21 +03:00
|
|
|
mHoldTime.SetValue(std::max(mPreviousCurrentTime.Value(), effectEnd));
|
2015-03-18 16:22:11 +03:00
|
|
|
} else {
|
2015-04-15 02:48:21 +03:00
|
|
|
mHoldTime.SetValue(effectEnd);
|
2015-03-18 16:22:11 +03:00
|
|
|
}
|
|
|
|
} else if (mPlaybackRate < 0.0 &&
|
|
|
|
!currentTime.IsNull() &&
|
|
|
|
currentTime.Value().ToMilliseconds() <= 0.0) {
|
2015-05-11 11:17:06 +03:00
|
|
|
if (aSeekFlag == SeekFlag::DidSeek) {
|
2015-03-18 16:22:11 +03:00
|
|
|
mHoldTime = currentTime;
|
|
|
|
} else {
|
|
|
|
mHoldTime.SetValue(0);
|
|
|
|
}
|
|
|
|
} else if (mPlaybackRate != 0.0 &&
|
2015-04-28 06:49:12 +03:00
|
|
|
!currentTime.IsNull() &&
|
|
|
|
mTimeline &&
|
|
|
|
!mTimeline->GetCurrentTime().IsNull()) {
|
2015-05-11 11:17:06 +03:00
|
|
|
if (aSeekFlag == SeekFlag::DidSeek && !mHoldTime.IsNull()) {
|
2015-03-18 16:22:11 +03:00
|
|
|
mStartTime.SetValue(mTimeline->GetCurrentTime().Value() -
|
2015-04-28 06:49:12 +03:00
|
|
|
(mHoldTime.Value().MultDouble(1 / mPlaybackRate)));
|
2015-03-18 16:22:11 +03:00
|
|
|
}
|
|
|
|
mHoldTime.SetNull();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-19 02:51:13 +03:00
|
|
|
bool currentFinishedState = PlayState() == AnimationPlayState::Finished;
|
2015-07-30 00:21:00 +03:00
|
|
|
if (currentFinishedState && !mFinishedIsResolved) {
|
|
|
|
DoFinishNotification(aSyncNotifyFlag);
|
|
|
|
} else if (!currentFinishedState && mFinishedIsResolved) {
|
|
|
|
ResetFinishedPromise();
|
2015-03-18 16:22:11 +03:00
|
|
|
}
|
2015-03-23 16:23:19 +03:00
|
|
|
// We must recalculate the current time to take account of any mHoldTime
|
|
|
|
// changes the code above made.
|
|
|
|
mPreviousCurrentTime = GetCurrentTime();
|
2015-03-18 16:22:11 +03:00
|
|
|
}
|
|
|
|
|
2014-12-22 03:35:42 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::UpdateEffect()
|
2014-12-22 03:35:42 +03:00
|
|
|
{
|
2015-04-15 02:48:21 +03:00
|
|
|
if (mEffect) {
|
|
|
|
mEffect->SetParentTime(GetCurrentTime());
|
2015-03-14 08:34:40 +03:00
|
|
|
UpdateRelevance();
|
2014-12-22 03:35:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-20 08:55:43 +04:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::FlushStyle() const
|
2014-10-20 08:55:43 +04:00
|
|
|
{
|
2014-11-17 07:45:58 +03:00
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
2014-10-20 08:55:47 +04:00
|
|
|
if (doc) {
|
|
|
|
doc->FlushPendingNotifications(Flush_Style);
|
2014-10-20 08:55:43 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-17 07:45:59 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::PostUpdate()
|
2014-11-17 07:45:59 +03:00
|
|
|
{
|
2015-04-21 04:22:10 +03:00
|
|
|
AnimationCollection* collection = GetCollection();
|
2014-11-17 07:45:59 +03:00
|
|
|
if (collection) {
|
2015-08-18 10:11:55 +03:00
|
|
|
collection->RequestRestyle(AnimationCollection::RestyleType::Layer);
|
2014-11-17 07:45:59 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-18 02:42:41 +03:00
|
|
|
void
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::CancelPendingTasks()
|
2014-12-18 02:42:41 +03:00
|
|
|
{
|
2015-03-27 09:56:45 +03:00
|
|
|
if (mPendingState == PendingState::NotPending) {
|
2014-12-18 02:42:41 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
|
|
|
if (doc) {
|
2015-04-21 04:22:09 +03:00
|
|
|
PendingAnimationTracker* tracker = doc->GetPendingAnimationTracker();
|
2014-12-18 02:42:41 +03:00
|
|
|
if (tracker) {
|
2015-03-27 09:56:45 +03:00
|
|
|
if (mPendingState == PendingState::PlayPending) {
|
|
|
|
tracker->RemovePlayPending(*this);
|
|
|
|
} else {
|
|
|
|
tracker->RemovePausePending(*this);
|
|
|
|
}
|
2014-12-18 02:42:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-27 09:56:45 +03:00
|
|
|
mPendingState = PendingState::NotPending;
|
2015-01-09 01:57:58 +03:00
|
|
|
mPendingReadyTime.SetNull();
|
2014-12-18 02:42:41 +03:00
|
|
|
}
|
|
|
|
|
2015-01-09 01:57:58 +03:00
|
|
|
bool
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::IsPossiblyOrphanedPendingAnimation() const
|
2015-01-09 01:57:58 +03:00
|
|
|
{
|
|
|
|
// Check if we are pending but might never start because we are not being
|
|
|
|
// tracked.
|
|
|
|
//
|
|
|
|
// This covers the following cases:
|
|
|
|
//
|
2015-04-15 02:48:21 +03:00
|
|
|
// * We started playing but our effect's target element was orphaned
|
2015-01-09 01:57:58 +03:00
|
|
|
// or bound to a different document.
|
2015-04-15 02:48:21 +03:00
|
|
|
// (note that for the case of our effect changing we should handle
|
|
|
|
// that in SetEffect)
|
2015-01-09 01:57:58 +03:00
|
|
|
// * We started playing but our timeline became inactive.
|
2015-04-21 04:22:09 +03:00
|
|
|
// In this case the pending animation tracker will drop us from its hashmap
|
2015-01-09 01:57:58 +03:00
|
|
|
// when we have been painted.
|
2015-04-21 04:22:09 +03:00
|
|
|
// * When we started playing we couldn't find a PendingAnimationTracker to
|
2015-04-15 02:48:21 +03:00
|
|
|
// register with (perhaps the effect had no document) so we simply
|
2015-03-27 09:56:45 +03:00
|
|
|
// set mPendingState in DoPlay and relied on this method to catch us on the
|
2015-01-09 01:57:58 +03:00
|
|
|
// next tick.
|
|
|
|
|
|
|
|
// If we're not pending we're ok.
|
2015-03-27 09:56:45 +03:00
|
|
|
if (mPendingState == PendingState::NotPending) {
|
2015-01-09 01:57:58 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have a pending ready time then we will be started on the next
|
|
|
|
// tick.
|
|
|
|
if (!mPendingReadyTime.IsNull()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we don't have an active timeline then we shouldn't start until
|
|
|
|
// we do.
|
|
|
|
if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have no rendered document, or we're not in our rendered document's
|
2015-04-21 04:22:09 +03:00
|
|
|
// PendingAnimationTracker then there's a good chance no one is tracking us.
|
2015-01-09 01:57:58 +03:00
|
|
|
//
|
|
|
|
// If we're wrong and another document is tracking us then, at worst, we'll
|
2015-03-27 09:56:45 +03:00
|
|
|
// simply start/pause the animation one tick too soon. That's better than
|
|
|
|
// never starting/pausing the animation and is unlikely.
|
2015-01-09 01:57:58 +03:00
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
2015-03-27 09:56:45 +03:00
|
|
|
if (!doc) {
|
2015-04-28 09:47:45 +03:00
|
|
|
return true;
|
2015-03-27 09:56:45 +03:00
|
|
|
}
|
|
|
|
|
2015-04-21 04:22:09 +03:00
|
|
|
PendingAnimationTracker* tracker = doc->GetPendingAnimationTracker();
|
2015-03-27 09:56:45 +03:00
|
|
|
return !tracker ||
|
|
|
|
(!tracker->IsWaitingToPlay(*this) &&
|
|
|
|
!tracker->IsWaitingToPause(*this));
|
2015-01-09 01:57:58 +03:00
|
|
|
}
|
|
|
|
|
2014-10-20 08:55:45 +04:00
|
|
|
StickyTimeDuration
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::EffectEnd() const
|
2014-10-20 08:55:45 +04:00
|
|
|
{
|
2015-04-15 02:48:21 +03:00
|
|
|
if (!mEffect) {
|
2014-10-20 08:55:45 +04:00
|
|
|
return StickyTimeDuration(0);
|
|
|
|
}
|
|
|
|
|
2015-04-15 02:48:21 +03:00
|
|
|
return mEffect->Timing().mDelay
|
|
|
|
+ mEffect->GetComputedTiming().mActiveDuration;
|
2014-10-20 08:55:45 +04:00
|
|
|
}
|
|
|
|
|
2014-11-17 07:45:58 +03:00
|
|
|
nsIDocument*
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::GetRenderedDocument() const
|
2014-11-17 07:45:58 +03:00
|
|
|
{
|
2015-04-15 02:48:21 +03:00
|
|
|
if (!mEffect) {
|
2014-11-17 07:45:58 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Element* targetElement;
|
|
|
|
nsCSSPseudoElements::Type pseudoType;
|
2015-04-15 02:48:21 +03:00
|
|
|
mEffect->GetTarget(targetElement, pseudoType);
|
2014-11-17 07:45:58 +03:00
|
|
|
if (!targetElement) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return targetElement->GetComposedDoc();
|
|
|
|
}
|
|
|
|
|
2014-11-17 07:45:58 +03:00
|
|
|
nsPresContext*
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::GetPresContext() const
|
2014-11-17 07:45:58 +03:00
|
|
|
{
|
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
|
|
|
if (!doc) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
nsIPresShell* shell = doc->GetShell();
|
|
|
|
if (!shell) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return shell->GetPresContext();
|
|
|
|
}
|
|
|
|
|
2015-04-21 04:22:10 +03:00
|
|
|
AnimationCollection*
|
2015-04-21 04:22:09 +03:00
|
|
|
Animation::GetCollection() const
|
2014-11-17 07:45:59 +03:00
|
|
|
{
|
2015-07-29 04:57:39 +03:00
|
|
|
CommonAnimationManager* manager = GetAnimationManager();
|
2014-11-17 07:45:59 +03:00
|
|
|
if (!manager) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-04-21 04:22:09 +03:00
|
|
|
MOZ_ASSERT(mEffect,
|
|
|
|
"An animation with an animation manager must have an effect");
|
2014-11-17 07:45:59 +03:00
|
|
|
|
|
|
|
Element* targetElement;
|
|
|
|
nsCSSPseudoElements::Type targetPseudoType;
|
2015-04-15 02:48:21 +03:00
|
|
|
mEffect->GetTarget(targetElement, targetPseudoType);
|
2014-11-17 07:45:59 +03:00
|
|
|
MOZ_ASSERT(targetElement,
|
2015-04-21 04:22:09 +03:00
|
|
|
"An animation with an animation manager must have a target");
|
2014-11-17 07:45:59 +03:00
|
|
|
|
2015-03-20 21:20:49 +03:00
|
|
|
return manager->GetAnimations(targetElement, targetPseudoType, false);
|
2014-11-17 07:45:59 +03:00
|
|
|
}
|
|
|
|
|
2015-07-30 00:21:00 +03:00
|
|
|
void
|
|
|
|
Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
|
|
|
|
{
|
|
|
|
if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
|
2015-07-31 00:25:00 +03:00
|
|
|
DoFinishNotificationImmediately();
|
2015-07-30 00:21:00 +03:00
|
|
|
} else if (!mFinishNotificationTask.IsPending()) {
|
|
|
|
nsRefPtr<nsRunnableMethod<Animation>> runnable =
|
2015-07-31 00:25:00 +03:00
|
|
|
NS_NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately);
|
2015-07-30 00:21:00 +03:00
|
|
|
Promise::DispatchToMicroTask(runnable);
|
|
|
|
mFinishNotificationTask = runnable;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Animation::ResetFinishedPromise()
|
|
|
|
{
|
|
|
|
mFinishedIsResolved = false;
|
|
|
|
mFinished = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Animation::MaybeResolveFinishedPromise()
|
2015-07-31 00:25:00 +03:00
|
|
|
{
|
|
|
|
if (mFinished) {
|
|
|
|
mFinished->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
mFinishedIsResolved = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Animation::DoFinishNotificationImmediately()
|
2015-07-30 00:21:00 +03:00
|
|
|
{
|
|
|
|
mFinishNotificationTask.Revoke();
|
|
|
|
|
|
|
|
if (PlayState() != AnimationPlayState::Finished) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-31 00:25:00 +03:00
|
|
|
MaybeResolveFinishedPromise();
|
|
|
|
|
|
|
|
DispatchPlaybackEvent(NS_LITERAL_STRING("finish"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Animation::DispatchPlaybackEvent(const nsAString& aName)
|
|
|
|
{
|
|
|
|
AnimationPlaybackEventInit init;
|
|
|
|
|
2015-07-31 00:26:00 +03:00
|
|
|
if (aName.EqualsLiteral("finish")) {
|
|
|
|
init.mCurrentTime = GetCurrentTimeAsDouble();
|
|
|
|
}
|
2015-07-31 00:25:00 +03:00
|
|
|
if (mTimeline) {
|
|
|
|
init.mTimelineTime = mTimeline->GetCurrentTimeAsDouble();
|
2015-07-30 00:21:00 +03:00
|
|
|
}
|
2015-07-31 00:25:00 +03:00
|
|
|
|
|
|
|
nsRefPtr<AnimationPlaybackEvent> event =
|
|
|
|
AnimationPlaybackEvent::Constructor(this, aName, init);
|
|
|
|
event->SetTrusted(true);
|
|
|
|
|
|
|
|
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
|
|
|
|
new AsyncEventDispatcher(this, event);
|
|
|
|
asyncDispatcher->PostDOMEvent();
|
2015-07-30 00:21:00 +03:00
|
|
|
}
|
|
|
|
|
2014-08-10 11:06:44 +04:00
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|