Files
tubestation/dom/animation/AnimationPlayer.cpp
Brian Birtles e5f74b42c4 Bug 1073336 part 7 - Move style flushing to CSSAnimationPlayer and CSSTransitionPlayer; r=dbaron
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).
2014-11-17 13:45:58 +09:00

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