2014-08-10 00:06:44 -07:00
|
|
|
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
#include "AnimationPlayer.h"
|
2014-08-29 23:11:57 -07:00
|
|
|
#include "AnimationUtils.h"
|
2014-08-10 00:06:44 -07:00
|
|
|
#include "mozilla/dom/AnimationPlayerBinding.h"
|
2014-11-16 20:45:59 -08:00
|
|
|
#include "AnimationCommon.h" // For AnimationPlayerCollection,
|
|
|
|
// CommonAnimationManager
|
2014-11-16 20:45:58 -08:00
|
|
|
#include "nsIDocument.h" // For nsIDocument
|
2014-11-16 20:45:58 -08:00
|
|
|
#include "nsIPresShell.h" // For nsIPresShell
|
2014-10-19 21:55:43 -07:00
|
|
|
#include "nsLayoutUtils.h" // For PostRestyleEvent (remove after bug 1073336)
|
2014-12-17 15:42:41 -08:00
|
|
|
#include "PendingPlayerTracker.h" // For PendingPlayerTracker
|
2014-08-10 00:06:44 -07:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2014-12-17 15:42:40 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline,
|
2015-03-18 06:22:11 -07:00
|
|
|
mSource, mReady, mFinished)
|
2014-12-17 15:42:40 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationPlayer)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationPlayer)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationPlayer)
|
|
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
2014-08-10 00:06:44 -07:00
|
|
|
|
|
|
|
JSObject*
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 07:13:33 -07:00
|
|
|
AnimationPlayer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
2014-08-10 00:06:44 -07:00
|
|
|
{
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 07:13:33 -07:00
|
|
|
return dom::AnimationPlayerBinding::Wrap(aCx, this, aGivenProto);
|
2014-08-10 00:06:44 -07:00
|
|
|
}
|
|
|
|
|
2015-02-09 02:26:27 -08:00
|
|
|
void
|
|
|
|
AnimationPlayer::SetStartTime(const Nullable<TimeDuration>& aNewStartTime)
|
|
|
|
{
|
|
|
|
#if 1
|
|
|
|
// Bug 1096776: once we support inactive/missing timelines we'll want to take
|
|
|
|
// the disabled branch.
|
|
|
|
MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
|
|
|
|
"We don't support inactive/missing timelines yet");
|
|
|
|
#else
|
|
|
|
Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
Nullable<TimeDuration> previousCurrentTime = GetCurrentTime();
|
|
|
|
mStartTime = aNewStartTime;
|
|
|
|
if (!aNewStartTime.IsNull()) {
|
2015-03-13 13:10:45 -07:00
|
|
|
if (mPlaybackRate != 0.0) {
|
|
|
|
mHoldTime.SetNull();
|
|
|
|
}
|
2015-02-09 02:26:27 -08:00
|
|
|
} else {
|
|
|
|
mHoldTime = previousCurrentTime;
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:56:45 -07:00
|
|
|
CancelPendingTasks();
|
2015-02-09 02:26:27 -08: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-03-18 06:22:11 -07:00
|
|
|
UpdateTiming();
|
2015-02-12 09:56:57 -08:00
|
|
|
PostUpdate();
|
2015-02-09 02:26:27 -08:00
|
|
|
}
|
|
|
|
|
2014-10-19 21:55:45 -07:00
|
|
|
Nullable<TimeDuration>
|
2014-08-29 23:11:56 -07:00
|
|
|
AnimationPlayer::GetCurrentTime() const
|
2014-08-10 00:06:44 -07:00
|
|
|
{
|
2014-10-19 21:55:45 -07:00
|
|
|
Nullable<TimeDuration> result;
|
|
|
|
if (!mHoldTime.IsNull()) {
|
|
|
|
result = mHoldTime;
|
2014-12-04 12:13:38 -08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mStartTime.IsNull()) {
|
2014-10-19 21:55:45 -07:00
|
|
|
Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
|
2014-12-04 12:13:38 -08:00
|
|
|
if (!timelineTime.IsNull()) {
|
2015-03-13 13:10:45 -07:00
|
|
|
result.SetValue((timelineTime.Value() - mStartTime.Value())
|
|
|
|
.MultDouble(mPlaybackRate));
|
2014-10-19 21:55:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2014-08-10 00:06:44 -07:00
|
|
|
}
|
|
|
|
|
2015-03-09 09:50:39 -07:00
|
|
|
// Implements http://w3c.github.io/web-animations/#silently-set-the-current-time
|
|
|
|
void
|
|
|
|
AnimationPlayer::SilentlySetCurrentTime(const TimeDuration& aSeekTime)
|
|
|
|
{
|
|
|
|
if (!mHoldTime.IsNull() ||
|
|
|
|
!mTimeline ||
|
2015-03-13 13:10:45 -07:00
|
|
|
mTimeline->GetCurrentTime().IsNull() ||
|
|
|
|
mPlaybackRate == 0.0
|
|
|
|
/*or, once supported, if we have a pending pause task*/) {
|
2015-03-09 09:50:39 -07:00
|
|
|
mHoldTime.SetValue(aSeekTime);
|
|
|
|
if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) {
|
|
|
|
mStartTime.SetNull();
|
|
|
|
}
|
|
|
|
} else {
|
2015-03-13 13:10:45 -07:00
|
|
|
mStartTime.SetValue(mTimeline->GetCurrentTime().Value() -
|
|
|
|
(aSeekTime / mPlaybackRate));
|
2015-03-09 09:50:39 -07:00
|
|
|
}
|
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
mPreviousCurrentTime.SetNull();
|
2015-03-09 09:50:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Implements http://w3c.github.io/web-animations/#set-the-current-time
|
|
|
|
void
|
|
|
|
AnimationPlayer::SetCurrentTime(const TimeDuration& aSeekTime)
|
|
|
|
{
|
|
|
|
SilentlySetCurrentTime(aSeekTime);
|
|
|
|
|
2015-03-26 23:56:45 -07:00
|
|
|
if (mPendingState == PendingState::PausePending) {
|
|
|
|
CancelPendingTasks();
|
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
}
|
2015-03-09 09:50:39 -07:00
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
UpdateFinishedState(true);
|
2015-03-09 09:50:39 -07:00
|
|
|
UpdateSourceContent();
|
|
|
|
PostUpdate();
|
|
|
|
}
|
|
|
|
|
2015-03-13 13:10:45 -07:00
|
|
|
void
|
|
|
|
AnimationPlayer::SetPlaybackRate(double aPlaybackRate)
|
|
|
|
{
|
|
|
|
Nullable<TimeDuration> previousTime = GetCurrentTime();
|
|
|
|
mPlaybackRate = aPlaybackRate;
|
|
|
|
if (!previousTime.IsNull()) {
|
|
|
|
ErrorResult rv;
|
|
|
|
SetCurrentTime(previousTime.Value());
|
|
|
|
MOZ_ASSERT(!rv.Failed(), "Should not assert for non-null time");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AnimationPlayer::SilentlySetPlaybackRate(double aPlaybackRate)
|
|
|
|
{
|
|
|
|
Nullable<TimeDuration> previousTime = GetCurrentTime();
|
|
|
|
mPlaybackRate = aPlaybackRate;
|
|
|
|
if (!previousTime.IsNull()) {
|
|
|
|
ErrorResult rv;
|
|
|
|
SilentlySetCurrentTime(previousTime.Value());
|
|
|
|
MOZ_ASSERT(!rv.Failed(), "Should not assert for non-null time");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-19 21:55:45 -07:00
|
|
|
AnimationPlayState
|
|
|
|
AnimationPlayer::PlayState() const
|
|
|
|
{
|
2015-03-26 23:56:45 -07:00
|
|
|
if (mPendingState != PendingState::NotPending) {
|
2014-12-17 15:42:41 -08:00
|
|
|
return AnimationPlayState::Pending;
|
|
|
|
}
|
|
|
|
|
2014-10-19 21:55:45 -07:00
|
|
|
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
2014-10-19 21:55:45 -07:00
|
|
|
if (currentTime.IsNull()) {
|
|
|
|
return AnimationPlayState::Idle;
|
|
|
|
}
|
|
|
|
|
2014-12-04 08:28:37 -08:00
|
|
|
if (mStartTime.IsNull()) {
|
2014-10-19 21:55:45 -07:00
|
|
|
return AnimationPlayState::Paused;
|
|
|
|
}
|
|
|
|
|
2015-03-13 13:10:45 -07:00
|
|
|
if ((mPlaybackRate > 0.0 && currentTime.Value() >= SourceContentEnd()) ||
|
|
|
|
(mPlaybackRate < 0.0 && currentTime.Value().ToMilliseconds() <= 0.0)) {
|
2014-10-19 21:55:45 -07:00
|
|
|
return AnimationPlayState::Finished;
|
|
|
|
}
|
|
|
|
|
|
|
|
return AnimationPlayState::Running;
|
|
|
|
}
|
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
static inline already_AddRefed<Promise>
|
|
|
|
CreatePromise(AnimationTimeline* aTimeline, ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
nsIGlobalObject* global = aTimeline->GetParentObject();
|
|
|
|
if (global) {
|
|
|
|
return Promise::Create(global, aRv);
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-12-17 15:42:40 -08:00
|
|
|
Promise*
|
|
|
|
AnimationPlayer::GetReady(ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
if (!mReady) {
|
2015-03-18 06:22:11 -07:00
|
|
|
mReady = CreatePromise(mTimeline, aRv); // Lazily create on demand
|
2014-12-17 15:42:40 -08:00
|
|
|
}
|
|
|
|
if (!mReady) {
|
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
2015-03-18 06:22:11 -07:00
|
|
|
} else if (PlayState() != AnimationPlayState::Pending) {
|
|
|
|
mReady->MaybeResolve(this);
|
2014-12-17 15:42:40 -08:00
|
|
|
}
|
|
|
|
return mReady;
|
|
|
|
}
|
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
Promise*
|
|
|
|
AnimationPlayer::GetFinished(ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
if (!mFinished) {
|
|
|
|
mFinished = CreatePromise(mTimeline, aRv); // Lazily create on demand
|
|
|
|
}
|
|
|
|
if (!mFinished) {
|
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
} else if (IsFinished()) {
|
|
|
|
mFinished->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
return mFinished;
|
|
|
|
}
|
|
|
|
|
2014-10-19 21:55:43 -07:00
|
|
|
void
|
2015-03-18 06:22:11 -07:00
|
|
|
AnimationPlayer::Play(LimitBehavior aLimitBehavior)
|
2014-10-19 21:55:43 -07:00
|
|
|
{
|
2015-03-18 06:22:11 -07:00
|
|
|
DoPlay(aLimitBehavior);
|
2014-11-16 20:46:01 -08:00
|
|
|
PostUpdate();
|
2014-10-19 21:55:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-11-16 20:46:01 -08:00
|
|
|
AnimationPlayer::Pause()
|
2014-10-19 21:55:43 -07:00
|
|
|
{
|
2015-03-18 06:22:11 -07:00
|
|
|
// TODO: The DoPause() call should not be synchronous (bug 1109390). See
|
|
|
|
// http://w3c.github.io/web-animations/#pausing-an-animation-section
|
2014-11-16 20:46:01 -08:00
|
|
|
DoPause();
|
|
|
|
PostUpdate();
|
2014-10-19 21:55:43 -07:00
|
|
|
}
|
|
|
|
|
2014-12-04 08:28:38 -08:00
|
|
|
Nullable<double>
|
|
|
|
AnimationPlayer::GetStartTimeAsDouble() const
|
|
|
|
{
|
|
|
|
return AnimationUtils::TimeDurationToDouble(mStartTime);
|
|
|
|
}
|
|
|
|
|
2015-02-09 02:26:27 -08:00
|
|
|
void
|
|
|
|
AnimationPlayer::SetStartTimeAsDouble(const Nullable<double>& aStartTime)
|
|
|
|
{
|
|
|
|
return SetStartTime(AnimationUtils::DoubleToTimeDuration(aStartTime));
|
|
|
|
}
|
|
|
|
|
2014-10-19 21:55:45 -07:00
|
|
|
Nullable<double>
|
|
|
|
AnimationPlayer::GetCurrentTimeAsDouble() const
|
|
|
|
{
|
|
|
|
return AnimationUtils::TimeDurationToDouble(GetCurrentTime());
|
|
|
|
}
|
|
|
|
|
2015-03-09 09:50:39 -07:00
|
|
|
void
|
|
|
|
AnimationPlayer::SetCurrentTimeAsDouble(const Nullable<double>& aCurrentTime,
|
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
if (aCurrentTime.IsNull()) {
|
|
|
|
if (!GetCurrentTime().IsNull()) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SetCurrentTime(TimeDuration::FromMilliseconds(aCurrentTime.Value()));
|
|
|
|
}
|
|
|
|
|
2014-08-10 00:06:47 -07:00
|
|
|
void
|
|
|
|
AnimationPlayer::SetSource(Animation* aSource)
|
|
|
|
{
|
2014-08-10 00:06:48 -07:00
|
|
|
if (mSource) {
|
|
|
|
mSource->SetParentTime(Nullable<TimeDuration>());
|
|
|
|
}
|
2014-08-10 00:06:47 -07:00
|
|
|
mSource = aSource;
|
2014-08-10 00:06:48 -07:00
|
|
|
if (mSource) {
|
2014-10-19 21:55:45 -07:00
|
|
|
mSource->SetParentTime(GetCurrentTime());
|
2014-08-10 00:06:48 -07:00
|
|
|
}
|
2015-03-13 22:34:40 -07:00
|
|
|
UpdateRelevance();
|
2014-08-10 00:06:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AnimationPlayer::Tick()
|
|
|
|
{
|
2015-01-08 14:57:58 -08: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-26 23:56:45 -07:00
|
|
|
if (mPendingState != PendingState::NotPending &&
|
2015-01-08 14:57:58 -08:00
|
|
|
!mPendingReadyTime.IsNull() &&
|
|
|
|
mPendingReadyTime.Value() <= mTimeline->GetCurrentTime().Value()) {
|
2015-03-31 20:23:24 -07:00
|
|
|
FinishPendingAt(mPendingReadyTime.Value());
|
2015-01-08 14:57:58 -08:00
|
|
|
mPendingReadyTime.SetNull();
|
|
|
|
}
|
|
|
|
|
2015-01-08 14:57:58 -08:00
|
|
|
if (IsPossiblyOrphanedPendingPlayer()) {
|
|
|
|
MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
|
|
|
|
"Orphaned pending players should have an active timeline");
|
2015-03-31 20:23:24 -07:00
|
|
|
FinishPendingAt(mTimeline->GetCurrentTime().Value());
|
2015-01-08 14:57:58 -08:00
|
|
|
}
|
2014-12-24 23:28:24 -08:00
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
UpdateTiming();
|
2014-08-10 00:06:47 -07:00
|
|
|
}
|
|
|
|
|
2015-01-08 14:57:58 -08:00
|
|
|
void
|
2015-03-26 23:56:45 -07:00
|
|
|
AnimationPlayer::TriggerOnNextTick(const Nullable<TimeDuration>& aReadyTime)
|
2015-01-08 14:57:58 -08:00
|
|
|
{
|
|
|
|
// Normally we expect the play state to be pending but it's possible that,
|
2015-03-26 23:56:45 -07:00
|
|
|
// due to the handling of possibly orphaned players in Tick(), this player got
|
|
|
|
// started whilst still being in another document's pending player map.
|
2015-01-08 14:57:58 -08:00
|
|
|
if (PlayState() != AnimationPlayState::Pending) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-08 14:57:58 -08:00
|
|
|
// If aReadyTime.IsNull() we'll detect this in Tick() where we check for
|
|
|
|
// orphaned players and trigger this animation anyway
|
2015-01-08 14:57:58 -08:00
|
|
|
mPendingReadyTime = aReadyTime;
|
|
|
|
}
|
|
|
|
|
2014-12-04 08:28:38 -08:00
|
|
|
void
|
2015-03-26 23:56:45 -07:00
|
|
|
AnimationPlayer::TriggerNow()
|
2014-12-04 08:28:38 -08:00
|
|
|
{
|
2014-12-24 23:28:24 -08:00
|
|
|
MOZ_ASSERT(PlayState() == AnimationPlayState::Pending,
|
|
|
|
"Expected to start a pending player");
|
2015-01-08 14:57:58 -08:00
|
|
|
MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
|
|
|
|
"Expected an active timeline");
|
2014-12-17 15:42:40 -08:00
|
|
|
|
2015-03-31 20:23:24 -07:00
|
|
|
FinishPendingAt(mTimeline->GetCurrentTime().Value());
|
2014-12-04 08:28:38 -08:00
|
|
|
}
|
|
|
|
|
2015-02-02 21:08:37 -08:00
|
|
|
Nullable<TimeDuration>
|
|
|
|
AnimationPlayer::GetCurrentOrPendingStartTime() const
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-12-17 15:42:41 -08:00
|
|
|
void
|
|
|
|
AnimationPlayer::Cancel()
|
|
|
|
{
|
2015-03-26 23:56:45 -07:00
|
|
|
if (mPendingState != PendingState::NotPending) {
|
2015-03-26 23:56:45 -07:00
|
|
|
CancelPendingTasks();
|
2014-12-17 15:42:41 -08:00
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
if (mFinished) {
|
|
|
|
mFinished->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
|
|
|
}
|
|
|
|
// Clear finished promise. We'll create a new one lazily.
|
|
|
|
mFinished = nullptr;
|
|
|
|
|
2014-12-17 15:42:41 -08:00
|
|
|
mHoldTime.SetNull();
|
|
|
|
mStartTime.SetNull();
|
2015-03-13 22:34:40 -07:00
|
|
|
|
|
|
|
UpdateSourceContent();
|
2014-12-17 15:42:41 -08:00
|
|
|
}
|
|
|
|
|
2015-03-13 22:34:40 -07:00
|
|
|
void
|
|
|
|
AnimationPlayer::UpdateRelevance()
|
|
|
|
{
|
2015-03-13 22:34:40 -07:00
|
|
|
bool wasRelevant = mIsRelevant;
|
2015-03-13 22:34:40 -07:00
|
|
|
mIsRelevant = HasCurrentSource() || HasInEffectSource();
|
2015-03-13 22:34:40 -07:00
|
|
|
|
|
|
|
// Notify animation observers.
|
|
|
|
if (wasRelevant && !mIsRelevant) {
|
|
|
|
nsNodeUtils::AnimationRemoved(this);
|
|
|
|
} else if (!wasRelevant && mIsRelevant) {
|
|
|
|
nsNodeUtils::AnimationAdded(this);
|
|
|
|
}
|
2015-03-13 22:34:40 -07:00
|
|
|
}
|
|
|
|
|
2014-10-19 21:55:45 -07:00
|
|
|
bool
|
|
|
|
AnimationPlayer::CanThrottle() const
|
|
|
|
{
|
|
|
|
if (!mSource ||
|
|
|
|
mSource->IsFinishedTransition() ||
|
|
|
|
mSource->Properties().IsEmpty()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mIsRunningOnCompositor) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PlayState() != AnimationPlayState::Finished) {
|
|
|
|
// Unfinished animations can be throttled.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The animation has finished but, if this is the first sample since
|
|
|
|
// finishing, 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).
|
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-19 21:55:47 -07:00
|
|
|
return mIsPreviousStateFinished;
|
2014-10-19 21:55:45 -07:00
|
|
|
}
|
|
|
|
|
2014-10-19 21:55:46 -07:00
|
|
|
void
|
|
|
|
AnimationPlayer::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
|
|
|
|
nsCSSPropertySet& aSetProperties,
|
|
|
|
bool& aNeedsRefreshes)
|
|
|
|
{
|
|
|
|
if (!mSource || mSource->IsFinishedTransition()) {
|
|
|
|
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-19 21:55:47 -07:00
|
|
|
AnimationPlayState playState = PlayState();
|
2014-12-17 15:42:41 -08:00
|
|
|
if (playState == AnimationPlayState::Running ||
|
|
|
|
playState == AnimationPlayState::Pending) {
|
2014-10-19 21:55:46 -07:00
|
|
|
aNeedsRefreshes = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
mSource->ComposeStyle(aStyleRule, aSetProperties);
|
|
|
|
}
|
|
|
|
|
2014-11-16 20:46:01 -08:00
|
|
|
void
|
2015-03-18 06:22:11 -07:00
|
|
|
AnimationPlayer::DoPlay(LimitBehavior aLimitBehavior)
|
2014-11-16 20:46:01 -08:00
|
|
|
{
|
2015-03-26 23:56:45 -07:00
|
|
|
bool reuseReadyPromise = false;
|
|
|
|
if (mPendingState != PendingState::NotPending) {
|
|
|
|
CancelPendingTasks();
|
|
|
|
reuseReadyPromise = true;
|
|
|
|
}
|
|
|
|
|
2014-12-04 08:28:37 -08:00
|
|
|
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
2015-03-13 13:10:45 -07:00
|
|
|
if (mPlaybackRate > 0.0 &&
|
2015-03-18 06:22:11 -07:00
|
|
|
(currentTime.IsNull() ||
|
|
|
|
(aLimitBehavior == LimitBehavior::AutoRewind &&
|
|
|
|
(currentTime.Value().ToMilliseconds() < 0.0 ||
|
|
|
|
currentTime.Value() >= SourceContentEnd())))) {
|
2014-12-04 08:28:37 -08:00
|
|
|
mHoldTime.SetValue(TimeDuration(0));
|
2015-03-13 13:10:45 -07:00
|
|
|
} else if (mPlaybackRate < 0.0 &&
|
2015-03-18 06:22:11 -07:00
|
|
|
(currentTime.IsNull() ||
|
|
|
|
(aLimitBehavior == LimitBehavior::AutoRewind &&
|
|
|
|
(currentTime.Value().ToMilliseconds() <= 0.0 ||
|
|
|
|
currentTime.Value() > SourceContentEnd())))) {
|
2015-03-13 13:10:45 -07:00
|
|
|
mHoldTime.SetValue(TimeDuration(SourceContentEnd()));
|
|
|
|
} else if (mPlaybackRate == 0.0 && currentTime.IsNull()) {
|
|
|
|
mHoldTime.SetValue(TimeDuration(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mHoldTime.IsNull()) {
|
2014-11-16 20:46:01 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-23 17:21:08 -07:00
|
|
|
// Clear the start time until we resolve a new one
|
|
|
|
mStartTime.SetNull();
|
|
|
|
|
2015-03-26 23:56:45 -07:00
|
|
|
if (!reuseReadyPromise) {
|
|
|
|
// Clear ready promise. We'll create a new one lazily.
|
|
|
|
mReady = nullptr;
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:56:45 -07:00
|
|
|
mPendingState = PendingState::PlayPending;
|
2014-12-24 23:28:24 -08:00
|
|
|
|
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
|
|
|
if (!doc) {
|
2015-03-26 23:56:45 -07:00
|
|
|
TriggerOnNextTick(Nullable<TimeDuration>());
|
2014-12-24 23:28:24 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingPlayerTracker* tracker = doc->GetOrCreatePendingPlayerTracker();
|
|
|
|
tracker->AddPlayPending(*this);
|
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
// We may have updated the current time when we set the hold time above.
|
|
|
|
UpdateTiming();
|
2014-11-16 20:46:01 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AnimationPlayer::DoPause()
|
|
|
|
{
|
2015-03-26 23:56:45 -07:00
|
|
|
if (mPendingState == PendingState::PlayPending) {
|
2015-03-26 23:56:45 -07:00
|
|
|
CancelPendingTasks();
|
2014-12-17 15:42:41 -08:00
|
|
|
// Resolve the ready promise since we currently only use it for
|
|
|
|
// players that are waiting to play. Later (in bug 1109390), we will
|
|
|
|
// use this for players waiting to pause as well and then we won't
|
|
|
|
// want to resolve it just yet.
|
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeResolve(this);
|
|
|
|
}
|
2014-11-16 20:46:01 -08:00
|
|
|
}
|
2014-12-17 15:42:41 -08:00
|
|
|
|
2014-12-04 08:28:37 -08: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-16 20:46:01 -08:00
|
|
|
mIsRunningOnCompositor = false;
|
|
|
|
|
2014-12-17 15:42:41 -08:00
|
|
|
// Bug 1109390 - check for null result here and go to pending state
|
2014-11-16 20:46:01 -08:00
|
|
|
mHoldTime = GetCurrentTime();
|
|
|
|
mStartTime.SetNull();
|
2015-03-18 06:22:11 -07:00
|
|
|
|
|
|
|
UpdateFinishedState();
|
2014-11-16 20:46:01 -08:00
|
|
|
}
|
|
|
|
|
2015-01-08 14:57:58 -08:00
|
|
|
void
|
2015-03-31 20:23:24 -07:00
|
|
|
AnimationPlayer::ResumeAt(const TimeDuration& aReadyTime)
|
2015-01-08 14:57:58 -08:00
|
|
|
{
|
|
|
|
// This method is only expected to be called for a player that is
|
|
|
|
// waiting to play. We can easily adapt it to handle other states
|
|
|
|
// but it's currently not necessary.
|
2015-03-26 23:56:45 -07:00
|
|
|
MOZ_ASSERT(mPendingState == PendingState::PlayPending,
|
|
|
|
"Expected to resume a play-pending player");
|
2015-01-08 14:57:58 -08:00
|
|
|
MOZ_ASSERT(!mHoldTime.IsNull(),
|
2015-03-26 23:56:45 -07:00
|
|
|
"A player in the play-pending state should have a resolved"
|
|
|
|
" hold time");
|
2015-01-08 14:57:58 -08:00
|
|
|
|
2015-03-13 13:10:45 -07:00
|
|
|
if (mPlaybackRate != 0) {
|
2015-03-31 20:23:24 -07:00
|
|
|
mStartTime.SetValue(aReadyTime - (mHoldTime.Value() / mPlaybackRate));
|
2015-03-13 13:10:45 -07:00
|
|
|
mHoldTime.SetNull();
|
|
|
|
} else {
|
2015-03-31 20:23:24 -07:00
|
|
|
mStartTime.SetValue(aReadyTime);
|
2015-03-13 13:10:45 -07:00
|
|
|
}
|
2015-03-26 23:56:45 -07:00
|
|
|
mPendingState = PendingState::NotPending;
|
2015-01-08 14:57:58 -08:00
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
UpdateTiming();
|
2015-01-08 14:57:58 -08:00
|
|
|
|
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-31 20:23:24 -07:00
|
|
|
void
|
|
|
|
AnimationPlayer::PauseAt(const TimeDuration& aReadyTime)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mPendingState == PendingState::PausePending,
|
|
|
|
"Expected to pause a pause-pending player");
|
|
|
|
|
|
|
|
if (!mStartTime.IsNull()) {
|
|
|
|
mHoldTime.SetValue((aReadyTime - mStartTime.Value())
|
|
|
|
.MultDouble(mPlaybackRate));
|
|
|
|
}
|
|
|
|
mStartTime.SetNull();
|
|
|
|
mPendingState = PendingState::NotPending;
|
|
|
|
|
|
|
|
UpdateTiming();
|
|
|
|
|
|
|
|
if (mReady) {
|
|
|
|
mReady->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
void
|
|
|
|
AnimationPlayer::UpdateTiming()
|
|
|
|
{
|
|
|
|
// We call UpdateFinishedState before UpdateSourceContent because the former
|
|
|
|
// can change the current time, which is used by the latter.
|
|
|
|
UpdateFinishedState();
|
|
|
|
UpdateSourceContent();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AnimationPlayer::UpdateFinishedState(bool aSeekFlag)
|
|
|
|
{
|
|
|
|
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
|
|
|
TimeDuration targetEffectEnd = TimeDuration(SourceContentEnd());
|
|
|
|
|
|
|
|
if (!mStartTime.IsNull() &&
|
|
|
|
mPendingState == PendingState::NotPending) {
|
|
|
|
if (mPlaybackRate > 0.0 &&
|
|
|
|
!currentTime.IsNull() &&
|
|
|
|
currentTime.Value() >= targetEffectEnd) {
|
|
|
|
if (aSeekFlag) {
|
|
|
|
mHoldTime = currentTime;
|
|
|
|
} else if (!mPreviousCurrentTime.IsNull()) {
|
|
|
|
mHoldTime.SetValue(std::max(mPreviousCurrentTime.Value(),
|
|
|
|
targetEffectEnd));
|
|
|
|
} else {
|
|
|
|
mHoldTime.SetValue(targetEffectEnd);
|
|
|
|
}
|
|
|
|
} else if (mPlaybackRate < 0.0 &&
|
|
|
|
!currentTime.IsNull() &&
|
|
|
|
currentTime.Value().ToMilliseconds() <= 0.0) {
|
|
|
|
if (aSeekFlag) {
|
|
|
|
mHoldTime = currentTime;
|
|
|
|
} else {
|
|
|
|
mHoldTime.SetValue(0);
|
|
|
|
}
|
|
|
|
} else if (mPlaybackRate != 0.0 &&
|
|
|
|
!currentTime.IsNull()) {
|
|
|
|
if (aSeekFlag && !mHoldTime.IsNull()) {
|
|
|
|
mStartTime.SetValue(mTimeline->GetCurrentTime().Value() -
|
|
|
|
(mHoldTime.Value() / mPlaybackRate));
|
|
|
|
}
|
|
|
|
mHoldTime.SetNull();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool currentFinishedState = IsFinished();
|
|
|
|
if (currentFinishedState && !mIsPreviousStateFinished) {
|
|
|
|
if (mFinished) {
|
|
|
|
mFinished->MaybeResolve(this);
|
|
|
|
}
|
|
|
|
} else if (!currentFinishedState && mIsPreviousStateFinished) {
|
|
|
|
// Clear finished promise. We'll create a new one lazily.
|
|
|
|
mFinished = nullptr;
|
|
|
|
}
|
|
|
|
mIsPreviousStateFinished = currentFinishedState;
|
2015-03-23 06:23:19 -07:00
|
|
|
// We must recalculate the current time to take account of any mHoldTime
|
|
|
|
// changes the code above made.
|
|
|
|
mPreviousCurrentTime = GetCurrentTime();
|
2015-03-18 06:22:11 -07:00
|
|
|
}
|
|
|
|
|
2014-12-21 16:35:42 -08:00
|
|
|
void
|
|
|
|
AnimationPlayer::UpdateSourceContent()
|
|
|
|
{
|
|
|
|
if (mSource) {
|
|
|
|
mSource->SetParentTime(GetCurrentTime());
|
2015-03-13 22:34:40 -07:00
|
|
|
UpdateRelevance();
|
2014-12-21 16:35:42 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-19 21:55:43 -07:00
|
|
|
void
|
|
|
|
AnimationPlayer::FlushStyle() const
|
|
|
|
{
|
2014-11-16 20:45:58 -08:00
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
2014-10-19 21:55:47 -07:00
|
|
|
if (doc) {
|
|
|
|
doc->FlushPendingNotifications(Flush_Style);
|
2014-10-19 21:55:43 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-16 20:45:59 -08:00
|
|
|
void
|
|
|
|
AnimationPlayer::PostUpdate()
|
|
|
|
{
|
|
|
|
AnimationPlayerCollection* collection = GetCollection();
|
|
|
|
if (collection) {
|
|
|
|
collection->NotifyPlayerUpdated();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-17 15:42:41 -08:00
|
|
|
void
|
2015-03-26 23:56:45 -07:00
|
|
|
AnimationPlayer::CancelPendingTasks()
|
2014-12-17 15:42:41 -08:00
|
|
|
{
|
2015-03-26 23:56:45 -07:00
|
|
|
if (mPendingState == PendingState::NotPending) {
|
2014-12-17 15:42:41 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
|
|
|
if (doc) {
|
|
|
|
PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker();
|
|
|
|
if (tracker) {
|
2015-03-26 23:56:45 -07:00
|
|
|
if (mPendingState == PendingState::PlayPending) {
|
|
|
|
tracker->RemovePlayPending(*this);
|
|
|
|
} else {
|
|
|
|
tracker->RemovePausePending(*this);
|
|
|
|
}
|
2014-12-17 15:42:41 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:56:45 -07:00
|
|
|
mPendingState = PendingState::NotPending;
|
2015-01-08 14:57:58 -08:00
|
|
|
mPendingReadyTime.SetNull();
|
2014-12-17 15:42:41 -08:00
|
|
|
}
|
|
|
|
|
2015-03-18 06:22:11 -07:00
|
|
|
bool
|
|
|
|
AnimationPlayer::IsFinished() const
|
|
|
|
{
|
|
|
|
// Unfortunately there's some weirdness in the spec at the moment where if
|
|
|
|
// you're finished and paused, the playState is paused. This prevents us
|
|
|
|
// from just checking |PlayState() == AnimationPlayState::Finished| here,
|
|
|
|
// and we need this much more messy check to see if we're finished.
|
|
|
|
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
|
|
|
return !currentTime.IsNull() &&
|
|
|
|
((mPlaybackRate > 0.0 && currentTime.Value() >= SourceContentEnd()) ||
|
|
|
|
(mPlaybackRate < 0.0 && currentTime.Value().ToMilliseconds() <= 0.0));
|
|
|
|
}
|
|
|
|
|
2015-01-08 14:57:58 -08:00
|
|
|
bool
|
|
|
|
AnimationPlayer::IsPossiblyOrphanedPendingPlayer() const
|
|
|
|
{
|
|
|
|
// Check if we are pending but might never start because we are not being
|
|
|
|
// tracked.
|
|
|
|
//
|
|
|
|
// This covers the following cases:
|
|
|
|
//
|
|
|
|
// * We started playing but our source content's target element was orphaned
|
|
|
|
// or bound to a different document.
|
|
|
|
// (note that for the case of our source content changing we should handle
|
|
|
|
// that in SetSource)
|
|
|
|
// * We started playing but our timeline became inactive.
|
|
|
|
// In this case the pending player tracker will drop us from its hashmap
|
|
|
|
// when we have been painted.
|
|
|
|
// * When we started playing we couldn't find a PendingPlayerTracker to
|
|
|
|
// register with (perhaps the source content had no document) so we simply
|
2015-03-26 23:56:45 -07:00
|
|
|
// set mPendingState in DoPlay and relied on this method to catch us on the
|
2015-01-08 14:57:58 -08:00
|
|
|
// next tick.
|
|
|
|
|
|
|
|
// If we're not pending we're ok.
|
2015-03-26 23:56:45 -07:00
|
|
|
if (mPendingState == PendingState::NotPending) {
|
2015-01-08 14:57:58 -08: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
|
|
|
|
// PendingPlayerTracker then there's a good chance no one is tracking us.
|
|
|
|
//
|
|
|
|
// If we're wrong and another document is tracking us then, at worst, we'll
|
2015-03-26 23:56:45 -07:00
|
|
|
// simply start/pause the animation one tick too soon. That's better than
|
|
|
|
// never starting/pausing the animation and is unlikely.
|
2015-01-08 14:57:58 -08:00
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
2015-03-26 23:56:45 -07:00
|
|
|
if (!doc) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker();
|
|
|
|
return !tracker ||
|
|
|
|
(!tracker->IsWaitingToPlay(*this) &&
|
|
|
|
!tracker->IsWaitingToPause(*this));
|
2015-01-08 14:57:58 -08:00
|
|
|
}
|
|
|
|
|
2014-10-19 21:55:45 -07:00
|
|
|
StickyTimeDuration
|
|
|
|
AnimationPlayer::SourceContentEnd() const
|
|
|
|
{
|
|
|
|
if (!mSource) {
|
|
|
|
return StickyTimeDuration(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return mSource->Timing().mDelay
|
|
|
|
+ mSource->GetComputedTiming().mActiveDuration;
|
|
|
|
}
|
|
|
|
|
2014-11-16 20:45:58 -08:00
|
|
|
nsIDocument*
|
|
|
|
AnimationPlayer::GetRenderedDocument() const
|
|
|
|
{
|
|
|
|
if (!mSource) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Element* targetElement;
|
|
|
|
nsCSSPseudoElements::Type pseudoType;
|
|
|
|
mSource->GetTarget(targetElement, pseudoType);
|
|
|
|
if (!targetElement) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return targetElement->GetComposedDoc();
|
|
|
|
}
|
|
|
|
|
2014-11-16 20:45:58 -08:00
|
|
|
nsPresContext*
|
|
|
|
AnimationPlayer::GetPresContext() const
|
|
|
|
{
|
|
|
|
nsIDocument* doc = GetRenderedDocument();
|
|
|
|
if (!doc) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
nsIPresShell* shell = doc->GetShell();
|
|
|
|
if (!shell) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return shell->GetPresContext();
|
|
|
|
}
|
|
|
|
|
2014-11-16 20:45:59 -08:00
|
|
|
AnimationPlayerCollection*
|
|
|
|
AnimationPlayer::GetCollection() const
|
|
|
|
{
|
|
|
|
css::CommonAnimationManager* manager = GetAnimationManager();
|
|
|
|
if (!manager) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(mSource, "A player with an animation manager must have a source");
|
|
|
|
|
|
|
|
Element* targetElement;
|
|
|
|
nsCSSPseudoElements::Type targetPseudoType;
|
|
|
|
mSource->GetTarget(targetElement, targetPseudoType);
|
|
|
|
MOZ_ASSERT(targetElement,
|
|
|
|
"A player with an animation manager must have a target");
|
|
|
|
|
2015-03-20 11:20:49 -07:00
|
|
|
return manager->GetAnimations(targetElement, targetPseudoType, false);
|
2014-11-16 20:45:59 -08:00
|
|
|
}
|
|
|
|
|
2014-08-10 00:06:44 -07:00
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|