mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 20:47:44 +00:00
aa575c832b
The existing relationship between the particular versions of AnimationPlayer::Play* (particularly in the CSSAnimationPlayer) subclass are confusing because, for example, CSSAnimationPlayer::PlayFromStyle needs to be careful to *not* call Play on CSSAnimationPlayer, but only on the parent object (since otherwise we reset the sticky pause behavior). This patch reworks this relationship by adding a protected DoPlay method that performs the common pausing behavior. Play/PlayFromJS/PlayFromStyle then add flushing, sticky pausing etc. as necessary. This patch also removes the UpdateFlags enum and parameters previously used to control whether we forced an update to style. This is no longer necessary since we no longer call 'Play' from style. Instead we make Play always post restyles. If we come across a case where we want to call Play and *not* post restyles, we can re-add the flags then. Roughly the same arrangement is true for Pause except that we don't currently flush styles for CSS animations in PauseFromJS since it currently won't make any observable difference.
283 lines
6.6 KiB
C++
283 lines
6.6 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 "AnimationCommon.h" // For AnimationPlayerCollection,
|
|
// CommonAnimationManager
|
|
#include "nsIDocument.h" // For nsIDocument
|
|
#include "nsIPresShell.h" // For nsIPresShell
|
|
#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()
|
|
{
|
|
DoPlay();
|
|
PostUpdate();
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::Pause()
|
|
{
|
|
DoPause();
|
|
PostUpdate();
|
|
}
|
|
|
|
Nullable<double>
|
|
AnimationPlayer::GetCurrentTimeAsDouble() const
|
|
{
|
|
return AnimationUtils::TimeDurationToDouble(GetCurrentTime());
|
|
}
|
|
|
|
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::DoPlay()
|
|
{
|
|
// 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();
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::DoPause()
|
|
{
|
|
if (mIsPaused) {
|
|
return;
|
|
}
|
|
mIsPaused = true;
|
|
mIsRunningOnCompositor = false;
|
|
|
|
// Bug 927349 - check for null result here and go to pending state
|
|
mHoldTime = GetCurrentTime();
|
|
mStartTime.SetNull();
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::FlushStyle() const
|
|
{
|
|
nsIDocument* doc = GetRenderedDocument();
|
|
if (doc) {
|
|
doc->FlushPendingNotifications(Flush_Style);
|
|
}
|
|
}
|
|
|
|
void
|
|
AnimationPlayer::PostUpdate()
|
|
{
|
|
AnimationPlayerCollection* collection = GetCollection();
|
|
if (collection) {
|
|
collection->NotifyPlayerUpdated();
|
|
}
|
|
}
|
|
|
|
StickyTimeDuration
|
|
AnimationPlayer::SourceContentEnd() const
|
|
{
|
|
if (!mSource) {
|
|
return StickyTimeDuration(0);
|
|
}
|
|
|
|
return mSource->Timing().mDelay
|
|
+ mSource->GetComputedTiming().mActiveDuration;
|
|
}
|
|
|
|
nsIDocument*
|
|
AnimationPlayer::GetRenderedDocument() const
|
|
{
|
|
if (!mSource) {
|
|
return nullptr;
|
|
}
|
|
|
|
Element* targetElement;
|
|
nsCSSPseudoElements::Type pseudoType;
|
|
mSource->GetTarget(targetElement, pseudoType);
|
|
if (!targetElement) {
|
|
return nullptr;
|
|
}
|
|
|
|
return targetElement->GetComposedDoc();
|
|
}
|
|
|
|
nsPresContext*
|
|
AnimationPlayer::GetPresContext() const
|
|
{
|
|
nsIDocument* doc = GetRenderedDocument();
|
|
if (!doc) {
|
|
return nullptr;
|
|
}
|
|
nsIPresShell* shell = doc->GetShell();
|
|
if (!shell) {
|
|
return nullptr;
|
|
}
|
|
return shell->GetPresContext();
|
|
}
|
|
|
|
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");
|
|
|
|
return manager->GetAnimationPlayers(targetElement, targetPseudoType, false);
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|