Previously AnimationPlayer::Play() and AnimationPlayer::PlayState() would flush styles as part of their operation. This, however, is only needed when the player corresponds to a CSS Animation or CSS Transition. Now that we have concrete subclasses for each of these cases we can move style flushing to the subclasses and remove it from the base class (which is expected to be shared with animations that are not dependent on style).
259 lines
6.0 KiB
C++
259 lines
6.0 KiB
C++
/* 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"
|
|
#include "AnimationUtils.h"
|
|
#include "mozilla/dom/AnimationPlayerBinding.h"
|
|
#include "nsLayoutUtils.h" // For PostRestyleEvent (remove after bug 1073336)
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline, mSource)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationPlayer, AddRef)
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationPlayer, Release)
|
|
|
|
JSObject*
|
|
AnimationPlayer::WrapObject(JSContext* aCx)
|
|
{
|
|
return dom::AnimationPlayerBinding::Wrap(aCx, this);
|
|
}
|
|
|
|
Nullable<double>
|
|
AnimationPlayer::GetStartTime() const
|
|
{
|
|
return AnimationUtils::TimeDurationToDouble(mStartTime);
|
|
}
|
|
|
|
Nullable<TimeDuration>
|
|
AnimationPlayer::GetCurrentTime() const
|
|
{
|
|
Nullable<TimeDuration> result;
|
|
if (!mHoldTime.IsNull()) {
|
|
result = mHoldTime;
|
|
} else {
|
|
Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
|
|
if (!timelineTime.IsNull() && !mStartTime.IsNull()) {
|
|
result.SetValue(timelineTime.Value() - mStartTime.Value());
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
AnimationPlayState
|
|
AnimationPlayer::PlayState() const
|
|
{
|
|
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
|
if (currentTime.IsNull()) {
|
|
return AnimationPlayState::Idle;
|
|
}
|
|
|
|
if (mIsPaused) {
|
|
return AnimationPlayState::Paused;
|
|
}
|
|
|
|
if (currentTime.Value() >= SourceContentEnd()) {
|
|
return AnimationPlayState::Finished;
|
|
}
|
|
|
|
return AnimationPlayState::Running;
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::Play(UpdateFlags aFlags)
|
|
{
|
|
// FIXME: When we implement finishing behavior (bug 1074630) we should
|
|
// not return early if mIsPaused is false since we may still need to seek.
|
|
// (However, we will need to pass a flag so that when we start playing due to
|
|
// a change in animation-play-state we *don't* trigger finishing behavior.)
|
|
if (!mIsPaused) {
|
|
return;
|
|
}
|
|
mIsPaused = false;
|
|
|
|
Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
|
|
if (timelineTime.IsNull()) {
|
|
// FIXME: We should just sit in the pending state in this case.
|
|
// We will introduce the pending state in Bug 927349.
|
|
return;
|
|
}
|
|
|
|
// Update start time to an appropriate offset from the current timeline time
|
|
MOZ_ASSERT(!mHoldTime.IsNull(), "Hold time should not be null when paused");
|
|
mStartTime.SetValue(timelineTime.Value() - mHoldTime.Value());
|
|
mHoldTime.SetNull();
|
|
|
|
if (aFlags == eUpdateStyle) {
|
|
MaybePostRestyle();
|
|
}
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::Pause(UpdateFlags aFlags)
|
|
{
|
|
if (mIsPaused) {
|
|
return;
|
|
}
|
|
mIsPaused = true;
|
|
mIsRunningOnCompositor = false;
|
|
|
|
// Bug 927349 - check for null result here and go to pending state
|
|
mHoldTime = GetCurrentTime();
|
|
mStartTime.SetNull();
|
|
|
|
if (aFlags == eUpdateStyle) {
|
|
MaybePostRestyle();
|
|
}
|
|
}
|
|
|
|
Nullable<double>
|
|
AnimationPlayer::GetCurrentTimeAsDouble() const
|
|
{
|
|
return AnimationUtils::TimeDurationToDouble(GetCurrentTime());
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::PlayFromJS()
|
|
{
|
|
Play(eUpdateStyle);
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::PauseFromJS()
|
|
{
|
|
Pause(eUpdateStyle);
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::SetSource(Animation* aSource)
|
|
{
|
|
if (mSource) {
|
|
mSource->SetParentTime(Nullable<TimeDuration>());
|
|
}
|
|
mSource = aSource;
|
|
if (mSource) {
|
|
mSource->SetParentTime(GetCurrentTime());
|
|
}
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::Tick()
|
|
{
|
|
if (mSource) {
|
|
mSource->SetParentTime(GetCurrentTime());
|
|
}
|
|
}
|
|
|
|
bool
|
|
AnimationPlayer::IsRunning() const
|
|
{
|
|
if (IsPaused() || !GetSource() || GetSource()->IsFinishedTransition()) {
|
|
return false;
|
|
}
|
|
|
|
ComputedTiming computedTiming = GetSource()->GetComputedTiming();
|
|
return computedTiming.mPhase == ComputedTiming::AnimationPhase_Active;
|
|
}
|
|
|
|
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).
|
|
return mIsPreviousStateFinished;
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
|
|
nsCSSPropertySet& aSetProperties,
|
|
bool& aNeedsRefreshes)
|
|
{
|
|
if (!mSource || mSource->IsFinishedTransition()) {
|
|
return;
|
|
}
|
|
|
|
AnimationPlayState playState = PlayState();
|
|
if (playState == AnimationPlayState::Running) {
|
|
aNeedsRefreshes = true;
|
|
}
|
|
|
|
mSource->ComposeStyle(aStyleRule, aSetProperties);
|
|
|
|
mIsPreviousStateFinished = (playState == AnimationPlayState::Finished);
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::FlushStyle() const
|
|
{
|
|
if (!mSource) {
|
|
return;
|
|
}
|
|
|
|
Element* targetElement;
|
|
nsCSSPseudoElements::Type pseudoType;
|
|
mSource->GetTarget(targetElement, pseudoType);
|
|
if (!targetElement) {
|
|
return;
|
|
}
|
|
|
|
nsIDocument* doc = targetElement->GetComposedDoc();
|
|
if (doc) {
|
|
doc->FlushPendingNotifications(Flush_Style);
|
|
}
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::MaybePostRestyle() const
|
|
{
|
|
if (!mSource) {
|
|
return;
|
|
}
|
|
|
|
Element* targetElement;
|
|
nsCSSPseudoElements::Type pseudoType;
|
|
mSource->GetTarget(targetElement, pseudoType);
|
|
if (!targetElement) {
|
|
return;
|
|
}
|
|
|
|
// FIXME: This is a bit heavy-handed but in bug 1073336 we hope to
|
|
// introduce a better means for players to update style.
|
|
nsLayoutUtils::PostRestyleEvent(targetElement,
|
|
eRestyle_Self,
|
|
nsChangeHint_AllReflowHints);
|
|
}
|
|
|
|
StickyTimeDuration
|
|
AnimationPlayer::SourceContentEnd() const
|
|
{
|
|
if (!mSource) {
|
|
return StickyTimeDuration(0);
|
|
}
|
|
|
|
return mSource->Timing().mDelay
|
|
+ mSource->GetComputedTiming().mActiveDuration;
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|