In preparation for deferring the start of animations in bug 927349, this patch
makes the code that resolves the start time a separate method. In this patch we
call the method immediately from AnimationPlayer::DoPlay. However, in the future
this will be called at a later point when the first frame of the animation has
been rendered.
Now that there is a public accessor for mStartTime, we can make it a protected
member of AnimationPlayer. The only time mStartTime is ever set is when playing
the animation so we can replace external modifications to mStartTime with calls
to Play(). This simplifies implementing deferred starting of animations
in bug 927349 by isolating the deferred playback logic to AnimationPlayer.
Note that even when we call PauseFromStyle immediately afterwards we still need
to call PlayFromStyle (or Play) first in order to resolve the time at which the
player should be paused. A newly created player doesn't have a current time so
if we were simply to call pause it wouldn't pause at the start of the animation
as we might expect. The call to Play(FromStyle) will cause the current time to
become zero and then we pause at that time.
In forthcoming patches we will encapsulate AnimationPlayer::mStartTime so we can
ensure that related state is updated appropriately. We would like to expose
mStartTime via GetStartTime() but currently a method of that name returns the
start time as a double.
This patch applies the pattern used for currentTime to startTime; specifically,
GetCurrentTime() returns the TimeDuration (since that's what C++ callers should
use) while GetCurrentTimeAsDouble() returns a double.
At the same time, this patch also removes the [Pure] extended attribute from
startTime in the WebIDL definition since subsequent patches either in this bug
or in bug 927349 will mean that startTime can be updated out-of-band.
Specifically, we will implement deferred playback of animation such that the
startTime remains null until we've finished rendering the first frame of the
animation.
This patch reworks AnimationPlayer to represent the paused state by a null start
time. This brings it into line with recent changes in the Web Animations spec
and removes the need for the mIsPaused member variable.
The idea is that in order for a player to play, it needs a start time and
an active timeline. The processing is roughly:
* If it is seeked to a particular time (through setting the currentTime or
calling pause() or finish()) but has no start time, it is paused.
* Otherwise, if it has no active timeline or no start time, it is idle.
By removing the mIsPaused flag the number of possible permutations of states is
reduced so the model is easier to reason about (see:
http://lists.w3.org/Archives/Public/public-fx/2014OctDec/0026.html).
This patch replaces the mIsPaused flag with checks for mStartTime.IsNull()
according to the rules outlined above.
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.
This patch introduces an abstract method to AnimationPlayer to fetch the manager
object associated with the player. This method is implemented separate by
CSSAnimationPlayer and CSSTransitionPlayer to return the nsAnimationManager or
nsTransitionManager accordingly.
In order for AnimationPlayer objects to be able to notify their
collection/manager, the can either store an extra pointer member, or they can
navigate to the collection as follows:
player->source(animation)->target(element)->document
->presShell->presContext->manager->collection
This patch adds a getter for the first part of this journey up to the document.
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).
In order to be able to find the collection a player belongs to from its source
content, we first need to be able to determine which manager--the animation
manager or transition manager--to look up.
We eventually plan to push transition event dispatch down to a CSS
transitions-specific subclass of AnimationPlayer, so this seems like a suitable
point to introduce this class.
Using this subclass we can define a virtual GetManager method that will
return the appropriate animation/transition manager for the player.
This patch moves the code for queuing CSS animation events from
nsAnimationManager to CSSAnimationPlayer. In doing so, it also moves the
mLastNotification member and associated enum values.
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.
This patch takes the CSSAnimationPlayer object, currently defined in
dom/animation/AnimationPlayer.{cpp,h}, and moves it to
layout/style/nsAnimationManager.{cpp,h} where the rest of the CSS
Animations-specific code lives.
At the same time it extends the scope of the mozilla namespace block in
nsAnimationManager.h to also include the AnimationEventInfo and EventArray types
since these classes, which don't have an ns* prefix, probably should be in the
mozilla namespace anyway.
This patch extracts the logic for calculating animation styles from
AnimationPlayerCollection and puts the bulk of it into the Animation objects.
Some of the initial logic surrounding the animation player state (e.g. is it
paused or not, etc.) is put into AnimationPlayer.
In future we may shift this logic even further down to the AnimationEffect
objects but currently we don't create such objects unless necessary.
This patch moves code from AnimationPlayerCollection to AnimationPlayer.
However, there is one subtle change in logic involved. Previously, we would test
if the player had finished by getting the computed time of its source content
and checking if it was in the after phase or not. In this patch, however, we
simply check the play state to see if it is finished or not.
These two approaches differ in the case where an animation is paused after it
has finished. The animation phase approach will indicate the player has
finished, but the play state approach will indicate the player has paused (since
the "paused" state trumps the "finished" state). This, however, should not
produce any observable effect because when an animation is paused
mIsRunningOnCompositor will be false (we don't put paused animations on the
compositor).
This patch uses the PlayFromStyle/PauseFromStyle methods on CSSAnimationPlayer
to perform play/pause control. (This allows us to encapsulate mHoldTime and
mPaused. We will encapsulate mStartTime etc. in subsequent bugs.
The override behavior of play()/pause() with regard to animation-play-state is:
* pause()/play() override the current animation-play-state
* pause() causes the player to remain paused until play() is called regardless
of changes to animation-play-state
(* Calling play() will override the animation-play-state but won't "stick". i.e.
subsequently setting animation-play-state: paused will pause the animation.)
These different permutations are tested in the next patch in this series.
This interaction will probably become more complicated once we introduce
finishing behavior (since we might not want animations to restart when
setting animation-play-state: running).
For players running CSS animations and CSS transitions we should perform a style
flush before running play() so that we are operating on the most up-to-date
state of animation-play-state.
For transitions, which don't have a play-state property, we will still need to
run this style flush before running play() to get the right finishing behavior
if transition-duration has changed. We haven't implemented finishing yet (that's
bug 1074630) but I've kept the flush for both cases now since I'm afraid we'll
forget it later.
Also, since we don't have a subclass of AnimationPlayer for transitions yet I've
put the style flush in the base class. In future, when we add
CSSTransitionPlayer we can move the flush to only those players that need up to
date style information.
When an animation player is paused by script this overrides whatever
animation-play-state is set by style. The logic for producing the correct
override behavior will be added to nsAnimationManager but first we need
to record in the player where it was paused.
This patch introduces a CSSAnimationPlayer subclass of AnimationPlayer. This is
because when we have an animation player running a CSS animation it has some
special behavior not needed in the general case such as:
* Flushing style before performing certain operations to ensure we are using
the latest information.
* Dispatching CSS Animation events when we start/end.
* Storing extra state with regards to "how" the animation was paused.
This is because, for example, if an animation is paused by script this
overrides specified style.
This patch introduces the basic implementation of play() and pause().
There are a lot of gaps still because we don't yet:
* Support the pending state (to be covered in bug 927349)
* Support finishing behavior (to be covered in bug 1074630)
* Have a good way of updating animation state outside of style resolution (bug
1073336)
Also, we don't call these methods from CSS yet because the interaction between
play()/pause() and animation-play-state requires storing some extra state which
we introduce in subsequent patches in this series.
This patch introduces, temporarily, an update flag to indicate whether
play()/pause() operations need to post a restyle event. When these methods are
triggered by processing restyles we don't want to post another (unnecessary)
restyle event. In bug 1073336 we will remove the need for this.
We only need to store if an animation is paused or not, hence a bool is
sufficient. Furthermore, the convenience of using the same type as the specified
style of animation-play-state will disappear once pausing behavior is wrapped up
behind Play() and Pause() methods.
This patch makes Element::GetAnimationPlayers return not only current animations
but also animations that have finished but are filling forwards. This brings the
implementation into line with recent changes to the Web Animations spec and
allows querying all the animations that are currently affecting an element or
which are scheduled to do so in the future.
This patch adds a utility method to return if an animation is "in effect" or not
as defined by Web Animations:
http://w3c.github.io/web-animations/#in-effect
It also moves the utility method for querying if an animation is "current"
(IsCurrent) to the .cpp file since it is fairly long. (Bug 1046055 makes one of
the callers of IsCurrent inline-able which should offset any cost introduced by
this no longer being inline-able.)
It's not the player that's "current" (a Web Animations term for an animation
that hasn't yet finished), but its source content, if any. This patch renames
the method on AnimationPlayer accordingly.
At the same time this patch moves the method to the header file since it's
quite simple and could possibly benefit from inlining.
This patch takes the StickyTimeDuration defined in the previous patch and
uses it within the calculation of animation timing for parameters that are
expected to be +/- Forever.
This patch stores the animation name on the Animation object rather than its
AnimationPlayer. This is because Animation objects don't have a reference to
their AnimationPlayer but their AnimationEffect needs access to the animation
name.
This patch also adds an accessor for AnimationPlayer to get the name from its
Animation (since players *do* have a reference to their source animation
content).