Animation::Tick contains special handling to cope with pending ready times
that are in the future. This was originally introduced to cope with the
situation where we are called multiple times per refresh-driver tick.
As of bug 1195180, Animation::Tick should no longer be called multiple
times per refresh driver tick. It would seem, therefore, that we no longer
need to check for a future time. However, since introducing this check, the
vsync refresh driver timer has been added which means that we can still have
a recorded time from TimeStamp::Now that is ahead of the vsync time used to
update the refresh driver. In that case, however, rather than waiting for the
next tick, we should simply clamp that pending ready time to the refresh driver
time and finish pending immediately.
This patch also updates one of the tests for reversing. With this updated
behavior we can sometimes arrive at a situation where when an Animation starts
and its ready promise resolves, its currentTime is still 0. If we call
reverse() at this point on an animation with an infinite active duration it
should throw an InvalidStateError. To avoid this situation, this test makes
sure we wait an extra frame before calling reverse().
This patch renames AnimationCollection::mNeedsRefreshes to indicate that it
no longer has any relationship to whether or not we observe the refresh driver.
We need to do this so effects can query their owning animation for the current
time and avoid falling out of sync. Furthermore, this pointer is needed
for a number of other bugs (e.g. bug 1166500 comment 12, or bug 1190235)
anyway.
Since getFrames() must gather all properties set at a given keyframe
offset time for a given easing function, we need to provide a total
ordering for ComputedTimingFunction objects. Until the spec defines how
to do this, we sort first by NS_STYLE_TRANSITION_TIMING_FUNCTION_*
value, then second by the four values in a cubic-bezier() function (in
order) or the integer and optional keyword in a steps() function.
Because we don't support automatic spacing of keyframes yet,
ComputedKeyFrame.computedOffset is always the same as Keyframe.offset.
Another assumption made is that the value of easing for a Keyframe
object at 100% should be the same as the value from the previous
Keyframe for the same property. An alternative would be to leave off
easing from that Keyframe, which would need the default value for that
IDL dictionary member removed (otherwise it would always be set to
"linear").
Since Keyframe.easing should reflect the {transition,animation}-timing-
function value relevant to each keyframe, we'll need to store on
nsTimingFunction the specific timing function value that was used, and
copy it down into ComputedTimingFunction for
KeyframeEffectReadOnly.getFrames() to access. This includes storing
whether the optional start/end keyword in a steps() function was
specified.
This patch adds a test that even when we seek from being irrelevant to another
state where we no longer need ticks that we still spin the refresh driver
in order to queue and dispatch an animationstart event.
Currently AnimationTimeline stores animations in a hashmap which means that
when we go to iterate over those animations to tick them we will visit them
in an order that is non-deterministic.
Although many of the observable effects of ticking an animation (e.g. CSS
animation/transition events, mutation observer events) are later sorted so that
the result does not depend on the order in which animations are ticked, this is
not true for in all cases. In particular, the order in which Animation.finished
promises are resolved will vary depending on the order in which animations are
ticked. Likewise, for Animation finish events.
Furthermore, it seems generally desirable to have a deterministic order for
visiting animations in order to aid reproducing bugs.
To achieve this, this patch switches the storage of animations in
AnimationTimeline to use an array instead. However, when adding animations
we need to determine if the animation to add already exists. To this end we
also maintain a hashmap of the animations so we can quickly determine if
the animation to add is a duplicate or not.
Now that DocumentTimeline observes the refresh driver we can use regular
ticks to remove unnecessary animations.
We do this because in a subsequent patch, in order to provide deterministic
enumeration order when ticking animations, we will store animations in an array.
Removing an arbitrary element from an nsTArray is O(n) since we have to search
for the array index first, or O(log n) if we keep the array sorted. If we
destroy a subtree containing n animations, the operation effectively becomes
O(n^2), or, if we keep the array sorted, O(n log n). By destroying during a
tick when we are already iterating over the array, however, we will be able
to do this much more efficiently.
Whether an animation is newly associated with a timeline, or is disassociated
from a timeline, or if it merely has its timing updated, the behavior
implemented in this patch is to simply make sure we are observing the refresh
driver and deal with the animation on the next tick.
It might seem that we could be a lot more clever about this and, for example, if
an animation reports NeedsTicks() == false, not start observing the refresh
driver. There are various edge cases however that need to be taken into account.
For example, if a CSS animation is finished (IsRelevant() == false so that
animation will have been removed from the timeline), and paused
(NeedsTicks() == false), and we seek it back to the point where it is relevant
again, we actually need to observe the refresh driver so that it can dispatch an
animationstart event on the next tick. A test case in a subsequent patch tests
this specific situation.
We could possibly add logic to detect if we need to fire events on the next tick
but the complexity does not seem warranted given that even if we unnecessarily
start observing the refresh driver, we will stop watching it on the next tick.
This patch removes some rather lengthy comments from
AnimationTiming::UpdateTiming. This is, in part, because of the behavior
described above that makes these comments no longer relevant. Other parts are
removed because the Web Animations specification has been updated such that a
timeline becoming inactive now pauses the animation[1] so that the issue
regarding detecting timelines becoming active/inactive no longer applies
since animations attached to an inactive timeline remain "relevant".
[1] https://w3c.github.io/web-animations/#responding-to-a-newly-inactive-timeline
Adds a method to determine if an animation requires refresh driver ticks.
We will use this function later to determine when it is safe to stop
observing the refresh driver.
This patch adds a utility method to Animation which takes a time in the
same time space as "current time", i.e. "animation time" and convert it to
a TimeStamp. Subsequent patches in this series will use this method to
take the time when an event was scheduled to occur and convert it to a
TimeStamp so it can be compared with other event times. This allows us to
dispatch events in the order they would have fired given an infinitely
frequent sample rate.
--HG--
extra : rebase_source : 0b4f98b932bb2751bac24b4383fe20613176f0c4
Currently we define a helper method, InitialAdvance, on KeyframeEffectReadOnly.
However, this method is only used for filling out the elapsedTime member of
AnimationEvents (which are generated by CSS animations). This patch moves this
method to CSSAnimation since it is unneeded for other types of Animations.
--HG--
extra : rebase_source : 9ab3b81a8272c004aabf26fea557c9a2f5d76caf
The Web Animations specification has replaced the term "sequence number" with
references to a global animation list. This patch applies similar naming
to our animation structures.
We currently determine if we need refresh driver ticks when composing style
but sometimes we might not need ticks for composing style but we might need
one more tick in order to queue a final end event. Currently, this doesn't
seem to be a problem because FlushAnimations calls Animation::Tick where we
queue up events. When we remove the call to Animation::Tick from
FlushAnimations in order to make FlushAnimations purely responsible for
posting restyles, however, we will create a situation where we might mark an
animation collection as no longer needing refreshes and not simultaneously
queueing the corresponding event. If another animation collection is deleted in
the meantime we may trigger the code that causes us to disassociate from the
refresh driver and the corresponding event will never be dispatched.
Long-term (bug 1195180) we will check if it we can stop observing the refresh
driver and queue events in the same step. Until then, this patch adds a method
to detect this particular situation and uses it to avoid unregistering from
the refresh driver while we still have end events to queue.
The patch removes 455 occurrences of FAIL_ON_WARNINGS from moz.build files, and
adds 78 instances of ALLOW_COMPILER_WARNINGS. About half of those 78 are in
code we control and which should be removable with a little effort.
--HG--
extra : rebase_source : 82e3387abfbd5f1471e953961d301d3d97ed2973
Now KeyframeEffect.SetTiming() updates the owning animation timing and relavance, so
we don't need to call each methods respectively for the animation any more.
We currently have a series of methods that clobber various bits of animation
state to force animations on layers to be updated. This aligns closely with
the restyle code introduced in this patch series.
By re-using RequestRestyle when updating animations on layers, not only should
we be able to simplify the code somewhat but, in future, we should also be able
to have Animation objects use the same mechanism to update layers during
a regular tick.
For example, currently we have a bug where when an animation starts after
a delay with the same value as the backwards fill then we don't send the
animation to the compositor right away (see
https://dxr.mozilla.org/mozilla-central/rev/d6ea652c579992daa9041cc9718bb7c6abefbc91/layout/style/test/test_animations_omta.html#287).
By adding this Restyle::Layer value we should be able to fix that in future.
In preparation for ultimately being able to run animations without a manager,
this patch moves the request restyle code from FlushAnimations to
Animation::Tick. (Ultimately most of this functionality should move to the
KeyframeEffect but for now Animation is fine.)
We want to move the newly-introduced RequestRestyle call from FlushAnimations
to Animation::Tick. However, nsAnimationManager::CheckAnimationRule calls
Animation::Tick so this would cause us to start posting animation restyles
within a restyle.
Typically, Animations have an effect (currently there is only one type of
effect: KeyframeEffectReadOnly) and when there is any change in timing they
pass it down to their effect. However, the Animation is dependent on the
duration of the effect for determining if it is "finished" or not. As a result,
when an effect's timing changes, the owning Animation needs to know.
(The way this *should* work is that effects should tell their animation or
trigger some chain of events that causes animation's to update themselves.
However, the current implementation of effects is fairly primitive and does
not do this or even have a reference to the owning Animation. When we
implement the script API for updating the timing properties of effects we will
have to fix this but for now it is up to code in layout/style to update the
Animation when it touches the corresponding effect's timing.)
nsAnimationManager::CheckAnimationRule currently does this by calling
Animation::Tick() which ensures the Animation's finished state is updated
accordingly.
Ultimately we want to ensure that Animation::Tick is called exactly once per
frame (and at the appropriate point in that frame) so we'd like to remove this
call from CheckAnimationRule.
This patch achieves that by:
* Making Animation::SetEffect update the animation's timing - this is necessary
for animations that are created by CheckAnimationRule and will be
necessary when once we make Animation.effect writeable from script anyway.
* Calling Animation::SetEffect even for the case when we are updating the
existing effect.
Another side-effect of calling Animation::Tick within
nsAnimationManager::CheckAnimationRule is that CSSAnimation::Tick queues
events. There are some tests (e.g. layout/style/test/test_animations.html) that
assume that animationstart events are dispatched immediately when new
animations are created. That will change with bug 1134163 but for now we
should maintain this existing behavior since changing this might introduce
compatibility issues that are best dealt with as a separate bug rather than
blocking this refactoring. To that end, this patch also explicitly queues
animationstart events for newly-created animations.
KeyframeEffectReadOnly uses IsFinishedTransition to exclude finished transitions
from certain tests. This check, however, is redundant in each case.
This is because any effect marked as IsFinishedTransition will have the
following properties:
- owning animation's PlayState() == Finished or Idle
- animation phase = after or null
- progress = null (this is because transitions don't fill forwards)
Animation::ComposeStyle uses IsFinishedTransition to skip doing work for
transitions that have run their course. We can, however, generalize this to
cover all animations that are not currently contributing to the animated
style--that is animations that are not "in effect".
We need to add this check *after* we update aNeedsRefreshes since an animation
that is not "in effect" because it has a delay and no backwards fill (in this
case it will have a play state of "running") still needs refreshes.
Previously we used IsFinishedTransition so that if the only animations present
are finished transitions we could throttle the tick. In fact, this probably
shouldn't even be necessary since we shouldn't be calling CanThrottle if
AnimationCollection::mNeedsRefreshes is false. However, so long as we're
performing this test it turns out we can generalize this further and throttle
ticks for all finished animations that are not newly finished, regardless of
whether they are running on the compositor or not (although this method won't
be called unless the animation property could be run on the compositor anyway).
This method is somewhat confusing. For one, it is not strictly limited to
animations that are running on the compositor. It appears to only return true
when the animation is running on the compositor but the mIsRunningOnCompositor
flag doesn't get cleared when the animation finishes (bug 1151694). As a result
this method also deals with animations that are now running on the main thread.
This patch makes us deal with such animations more consistently.
This patch also reworks this method so that it's hopefully a little easier to
follow and a little more consistent since I spent several hours trying to
understand the different combinations of inputs this method could take and what
question it was trying to answer.
The long-term plan is to drop the mozilla::css namespace altogether. Before we
go to much further with refactoring code in AnimationCommon, we should drop
usage of the mozilla::css namespace. Specifically, this patch moves the
CommonAnimationManager and AnimValuesStyleRule classes to the mozilla namespace.
This patch prepares the way for script-generated events by making
event dispatch a separate process that happens after sampling animations.
This will allow us to sample animations from their associated timeline
(removing the need for a further manager to tracker script-generated
animations).
Furthermore, once we sample animations from timelines the order in which they
are sampled is likely to be more or less random so by making event dispatch at
separate step, we have an opportunity to sort the events and dispatch in
a consistent and sensible order. It also ensures that event callbacks will
not be run until all animations (including transitions) have been updated
ensuring they see a consistent view of timing properties.
This patch only affects event handling for CSS animations. Transitions will
be dealt with in a subsequent patch.