1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "third_party/blink/renderer/core/animation/animation.h"
32 
33 #include <limits>
34 #include <memory>
35 
36 #include "base/metrics/histogram_macros.h"
37 #include "third_party/blink/public/platform/platform.h"
38 #include "third_party/blink/public/platform/task_type.h"
39 #include "third_party/blink/renderer/core/animation/animation_timeline.h"
40 #include "third_party/blink/renderer/core/animation/css/css_animations.h"
41 #include "third_party/blink/renderer/core/animation/document_timeline.h"
42 #include "third_party/blink/renderer/core/animation/element_animations.h"
43 #include "third_party/blink/renderer/core/animation/keyframe_effect.h"
44 #include "third_party/blink/renderer/core/animation/pending_animations.h"
45 #include "third_party/blink/renderer/core/animation/scroll_timeline.h"
46 #include "third_party/blink/renderer/core/animation/scroll_timeline_util.h"
47 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
48 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
49 #include "third_party/blink/renderer/core/css/style_change_reason.h"
50 #include "third_party/blink/renderer/core/dom/document.h"
51 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
52 #include "third_party/blink/renderer/core/events/animation_playback_event.h"
53 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
54 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
55 #include "third_party/blink/renderer/core/frame/web_feature.h"
56 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
57 #include "third_party/blink/renderer/core/paint/paint_layer.h"
58 #include "third_party/blink/renderer/core/probe/core_probes.h"
59 #include "third_party/blink/renderer/platform/animation/compositor_animation.h"
60 #include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
61 #include "third_party/blink/renderer/platform/bindings/microtask.h"
62 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
63 #include "third_party/blink/renderer/platform/heap/heap.h"
64 #include "third_party/blink/renderer/platform/heap/persistent.h"
65 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
66 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
67 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
68 
69 namespace blink {
70 
71 namespace {
72 
73 enum PseudoPriority { kNone, kMarker, kBefore, kOther, kAfter };
74 
NextSequenceNumber()75 unsigned NextSequenceNumber() {
76   static unsigned next = 0;
77   return ++next;
78 }
79 
SecondsToMilliseconds(double seconds)80 double SecondsToMilliseconds(double seconds) {
81   return seconds * 1000;
82 }
83 
MillisecondsToSeconds(double milliseconds)84 double MillisecondsToSeconds(double milliseconds) {
85   return milliseconds / 1000;
86 }
87 
Max(base::Optional<double> a,double b)88 double Max(base::Optional<double> a, double b) {
89   if (a.has_value())
90     return std::max(a.value(), b);
91   return b;
92 }
93 
Min(base::Optional<double> a,double b)94 double Min(base::Optional<double> a, double b) {
95   if (a.has_value())
96     return std::min(a.value(), b);
97   return b;
98 }
99 
ConvertStringtoPriority(const String & pseudo)100 PseudoPriority ConvertStringtoPriority(const String& pseudo) {
101   if (pseudo.IsNull())
102     return PseudoPriority::kNone;
103   if (pseudo == "::marker")
104     return PseudoPriority::kMarker;
105   if (pseudo == "::before")
106     return PseudoPriority::kBefore;
107   if (pseudo == "::after")
108     return PseudoPriority::kAfter;
109   return PseudoPriority::kOther;
110 }
111 
AnimationPriority(const Animation & animation)112 Animation::AnimationClassPriority AnimationPriority(
113     const Animation& animation) {
114   // According to the spec:
115   // https://drafts.csswg.org/web-animations/#animation-class,
116   // CSS tranisiton has a lower composite order than the CSS animation, and CSS
117   // animation has a lower composite order than other animations. Thus,CSS
118   // transitions are to appear before CSS animations and CSS animations are to
119   // appear before other animations
120   // TODO: When animations are disassociated from their element they are sorted
121   // by their sequence number, i.e. kDefaultPriority. See
122   // https://drafts.csswg.org/css-animations-2/#animation-composite-order and
123   // https://drafts.csswg.org/css-transitions-2/#animation-composite-order
124   Animation::AnimationClassPriority priority;
125   if (animation.IsCSSTransition() && animation.IsOwned())
126     priority = Animation::AnimationClassPriority::kCssTransitionPriority;
127   else if (animation.IsCSSAnimation() && animation.IsOwned())
128     priority = Animation::AnimationClassPriority::kCssAnimationPriority;
129   else
130     priority = Animation::AnimationClassPriority::kDefaultPriority;
131   return priority;
132 }
133 
RecordCompositorAnimationFailureReasons(CompositorAnimations::FailureReasons failure_reasons)134 void RecordCompositorAnimationFailureReasons(
135     CompositorAnimations::FailureReasons failure_reasons) {
136   // UMA_HISTOGRAM_ENUMERATION requires that the enum_max must be strictly
137   // greater than the sample value. kFailureReasonCount doesn't include the
138   // kNoFailure value but the histograms do so adding the +1 is necessary.
139   // TODO(dcheng): Fix https://crbug.com/705169 so this isn't needed.
140   constexpr uint32_t kFailureReasonEnumMax =
141       CompositorAnimations::kFailureReasonCount + 1;
142 
143   if (failure_reasons == CompositorAnimations::kNoFailure) {
144     UMA_HISTOGRAM_ENUMERATION(
145         "Blink.Animation.CompositedAnimationFailureReason",
146         CompositorAnimations::kNoFailure, kFailureReasonEnumMax);
147     return;
148   }
149 
150   for (uint32_t i = 0; i < CompositorAnimations::kFailureReasonCount; i++) {
151     unsigned val = 1 << i;
152     if (failure_reasons & val) {
153       UMA_HISTOGRAM_ENUMERATION(
154           "Blink.Animation.CompositedAnimationFailureReason", i + 1,
155           kFailureReasonEnumMax);
156     }
157   }
158 }
159 }  // namespace
160 
Create(AnimationEffect * effect,AnimationTimeline * timeline,ExceptionState & exception_state)161 Animation* Animation::Create(AnimationEffect* effect,
162                              AnimationTimeline* timeline,
163                              ExceptionState& exception_state) {
164   DCHECK(timeline);
165   if (!IsA<DocumentTimeline>(timeline) && !timeline->IsScrollTimeline()) {
166     exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
167                                       "Invalid timeline. Animation requires a "
168                                       "DocumentTimeline or ScrollTimeline");
169     return nullptr;
170   }
171   DCHECK(IsA<DocumentTimeline>(timeline) || timeline->IsScrollTimeline());
172 
173   auto* context = timeline->GetDocument()->GetExecutionContext();
174   return MakeGarbageCollected<Animation>(context, timeline, effect);
175 }
176 
Create(ExecutionContext * execution_context,AnimationEffect * effect,ExceptionState & exception_state)177 Animation* Animation::Create(ExecutionContext* execution_context,
178                              AnimationEffect* effect,
179                              ExceptionState& exception_state) {
180   Document* document = To<LocalDOMWindow>(execution_context)->document();
181   return Create(effect, &document->Timeline(), exception_state);
182 }
183 
Create(ExecutionContext * execution_context,AnimationEffect * effect,AnimationTimeline * timeline,ExceptionState & exception_state)184 Animation* Animation::Create(ExecutionContext* execution_context,
185                              AnimationEffect* effect,
186                              AnimationTimeline* timeline,
187                              ExceptionState& exception_state) {
188   if (!timeline) {
189     Animation* animation =
190         MakeGarbageCollected<Animation>(execution_context, nullptr, effect);
191     return animation;
192   }
193 
194   return Create(effect, timeline, exception_state);
195 }
196 
Animation(ExecutionContext * execution_context,AnimationTimeline * timeline,AnimationEffect * content)197 Animation::Animation(ExecutionContext* execution_context,
198                      AnimationTimeline* timeline,
199                      AnimationEffect* content)
200     : ExecutionContextLifecycleObserver(execution_context),
201       reported_play_state_(kIdle),
202       playback_rate_(1),
203       start_time_(),
204       hold_time_(),
205       sequence_number_(NextSequenceNumber()),
206       content_(content),
207       timeline_(timeline),
208       replace_state_(kActive),
209       is_paused_for_testing_(false),
210       is_composited_animation_disabled_for_testing_(false),
211       pending_pause_(false),
212       pending_play_(false),
213       pending_finish_notification_(false),
214       has_queued_microtask_(false),
215       outdated_(false),
216       finished_(true),
217       compositor_state_(nullptr),
218       compositor_pending_(false),
219       compositor_group_(0),
220       effect_suppressed_(false) {
221   if (content_) {
222     if (content_->GetAnimation()) {
223       content_->GetAnimation()->cancel();
224       content_->GetAnimation()->setEffect(nullptr);
225     }
226     content_->Attach(this);
227   }
228   document_ = timeline_ ? timeline_->GetDocument()
229                         : To<LocalDOMWindow>(execution_context)->document();
230   DCHECK(document_);
231 
232   if (timeline_)
233     timeline_->AnimationAttached(this);
234   else
235     document_->Timeline().AnimationAttached(this);
236 
237   probe::DidCreateAnimation(document_, sequence_number_);
238 }
239 
~Animation()240 Animation::~Animation() {
241   // Verify that compositor_animation_ has been disposed of.
242   DCHECK(!compositor_animation_);
243 }
244 
Dispose()245 void Animation::Dispose() {
246   if (timeline_)
247     timeline_->AnimationDetached(this);
248   DestroyCompositorAnimation();
249   // If the DocumentTimeline and its Animation objects are
250   // finalized by the same GC, we have to eagerly clear out
251   // this Animation object's compositor animation registration.
252   DCHECK(!compositor_animation_);
253 }
254 
EffectEnd() const255 double Animation::EffectEnd() const {
256   return content_ ? content_->SpecifiedTiming().EndTimeInternal() : 0;
257 }
258 
Limited(base::Optional<double> current_time) const259 bool Animation::Limited(base::Optional<double> current_time) const {
260   return (EffectivePlaybackRate() < 0 && current_time <= 0) ||
261          (EffectivePlaybackRate() > 0 && current_time >= EffectEnd());
262 }
263 
GetDocument() const264 Document* Animation::GetDocument() const {
265   return document_;
266 }
267 
TimelineTime() const268 base::Optional<double> Animation::TimelineTime() const {
269   return timeline_ ? timeline_->CurrentTime() : base::nullopt;
270 }
271 
272 // https://drafts.csswg.org/web-animations/#setting-the-current-time-of-an-animation.
setCurrentTimeForBinding(base::Optional<double> new_current_time,ExceptionState & exception_state)273 void Animation::setCurrentTimeForBinding(
274     base::Optional<double> new_current_time,
275     ExceptionState& exception_state) {
276   // TODO(crbug.com/924159): Update this after we add support for inactive
277   // timelines and unresolved timeline.currentTime
278   if (!new_current_time) {
279     // If the current time is resolved, then throw a TypeError.
280     if (CurrentTimeInternal()) {
281       exception_state.ThrowTypeError(
282           "currentTime may not be changed from resolved to unresolved");
283     }
284     return;
285   }
286 
287   SetCurrentTimeInternal(MillisecondsToSeconds(new_current_time.value()));
288 
289   // Synchronously resolve pending pause task.
290   if (pending_pause_) {
291     hold_time_ = MillisecondsToSeconds(new_current_time.value());
292     ApplyPendingPlaybackRate();
293     start_time_ = base::nullopt;
294     pending_pause_ = false;
295     if (ready_promise_)
296       ResolvePromiseMaybeAsync(ready_promise_.Get());
297   }
298 
299   // Update the finished state.
300   UpdateFinishedState(UpdateType::kDiscontinuous, NotificationType::kAsync);
301 
302   SetCompositorPending(/*effect_changed=*/false);
303 
304   // Notify of potential state change.
305   NotifyProbe();
306 }
307 
setCurrentTimeForBinding(double new_current_time,bool is_null,ExceptionState & exception_state)308 void Animation::setCurrentTimeForBinding(double new_current_time,
309                                          bool is_null,
310                                          ExceptionState& exception_state) {
311   setCurrentTimeForBinding(
312       is_null ? base::nullopt : base::make_optional(new_current_time),
313       exception_state);
314 }
315 
setCurrentTime(double new_current_time,bool is_null,ExceptionState & exception_state)316 void Animation::setCurrentTime(double new_current_time,
317                                bool is_null,
318                                ExceptionState& exception_state) {
319   setCurrentTimeForBinding(
320       is_null ? base::nullopt : base::make_optional(new_current_time),
321       exception_state);
322 }
323 
324 // https://drafts.csswg.org/web-animations/#setting-the-current-time-of-an-animation
325 // See steps for silently setting the current time. The preliminary step of
326 // handling an unresolved time are to be handled by the caller.
SetCurrentTimeInternal(double new_current_time)327 void Animation::SetCurrentTimeInternal(double new_current_time) {
328   DCHECK(std::isfinite(new_current_time));
329 
330   base::Optional<double> previous_start_time = start_time_;
331   base::Optional<double> previous_hold_time = hold_time_;
332 
333   // Update either the hold time or the start time.
334   if (hold_time_ || !start_time_ || !timeline_ || !timeline_->IsActive() ||
335       playback_rate_ == 0)
336     hold_time_ = new_current_time;
337   else
338     start_time_ = CalculateStartTime(new_current_time);
339 
340   // Preserve invariant that we can only set a start time or a hold time in the
341   // absence of an active timeline.
342   if (!timeline_ || !timeline_->IsActive())
343     start_time_ = base::nullopt;
344 
345   // Reset the previous current time.
346   previous_current_time_ = base::nullopt;
347 
348   if (previous_start_time != start_time_ || previous_hold_time != hold_time_)
349     SetOutdated();
350 }
351 
startTime() const352 base::Optional<double> Animation::startTime() const {
353   return start_time_
354              ? base::make_optional(SecondsToMilliseconds(start_time_.value()))
355              : base::nullopt;
356 }
357 
startTime(bool & is_null) const358 double Animation::startTime(bool& is_null) const {
359   base::Optional<double> result = startTime();
360   is_null = !result;
361   return result.value_or(0);
362 }
363 
364 // https://drafts.csswg.org/web-animations/#the-current-time-of-an-animation
currentTimeForBinding() const365 base::Optional<double> Animation::currentTimeForBinding() const {
366   // 1. If the animation’s hold time is resolved,
367   //    The current time is the animation’s hold time.
368   if (hold_time_.has_value())
369     return SecondsToMilliseconds(hold_time_.value());
370 
371   // 2.  If any of the following are true:
372   //    * the animation has no associated timeline, or
373   //    * the associated timeline is inactive, or
374   //    * the animation’s start time is unresolved.
375   // The current time is an unresolved time value.
376   if (!timeline_ || !timeline_->IsActive() || !start_time_)
377     return base::nullopt;
378 
379   // 3. Otherwise,
380   // current time = (timeline time - start time) × playback rate
381   base::Optional<double> timeline_time = timeline_->CurrentTimeSeconds();
382 
383   // An active timeline should always have a value, and since inactive timeline
384   // is handled in step 2 above, make sure that timeline_time has a value.
385   DCHECK(timeline_time.has_value());
386 
387   double current_time =
388       (timeline_time.value() - start_time_.value()) * playback_rate_;
389   return SecondsToMilliseconds(current_time);
390 }
391 
currentTimeForBinding(bool & is_null)392 double Animation::currentTimeForBinding(bool& is_null) {
393   base::Optional<double> result = currentTimeForBinding();
394   is_null = !result;
395   return result.value_or(0);
396 }
397 
currentTime() const398 double Animation::currentTime() const {
399   return currentTimeForBinding().value_or(Timing::NullValue());
400 }
401 
currentTime(bool & is_null)402 double Animation::currentTime(bool& is_null) {
403   base::Optional<double> result = currentTimeForBinding();
404   is_null = !result;
405   return result.value_or(0);
406 }
407 
CurrentTimeInternal() const408 base::Optional<double> Animation::CurrentTimeInternal() const {
409   return hold_time_ ? hold_time_ : CalculateCurrentTime();
410 }
411 
UnlimitedCurrentTime() const412 base::Optional<double> Animation::UnlimitedCurrentTime() const {
413   return CalculateAnimationPlayState() == kPaused || !start_time_
414              ? CurrentTimeInternal()
415              : CalculateCurrentTime();
416 }
417 
playState() const418 String Animation::playState() const {
419   return PlayStateString();
420 }
421 
PreCommit(int compositor_group,const PaintArtifactCompositor * paint_artifact_compositor,bool start_on_compositor)422 bool Animation::PreCommit(
423     int compositor_group,
424     const PaintArtifactCompositor* paint_artifact_compositor,
425     bool start_on_compositor) {
426   // TODO(crbug.com/916117): Revisit this condition as part of handling
427   // inactive timelines work.
428   if (timeline_ && timeline_->IsScrollTimeline() && !timeline_->IsActive())
429     return false;
430 
431   bool soft_change =
432       compositor_state_ &&
433       (Paused() || compositor_state_->playback_rate != EffectivePlaybackRate());
434   bool hard_change =
435       compositor_state_ && (compositor_state_->effect_changed ||
436                             compositor_state_->start_time != start_time_ ||
437                             !compositor_state_->start_time || !start_time_);
438 
439   // FIXME: softChange && !hardChange should generate a Pause/ThenStart,
440   // not a Cancel, but we can't communicate these to the compositor yet.
441 
442   bool changed = soft_change || hard_change;
443   bool should_cancel = (!Playing() && compositor_state_) || changed;
444   bool should_start = Playing() && (!compositor_state_ || changed);
445 
446   if (start_on_compositor && should_cancel && should_start &&
447       compositor_state_ && compositor_state_->pending_action == kStart) {
448     // Restarting but still waiting for a start time.
449     return false;
450   }
451 
452   if (should_cancel) {
453     CancelAnimationOnCompositor();
454     compositor_state_ = nullptr;
455   }
456 
457   DCHECK(!compositor_state_ || compositor_state_->start_time);
458 
459   if (should_start) {
460     compositor_group_ = compositor_group;
461     if (start_on_compositor) {
462       CompositorAnimations::FailureReasons failure_reasons =
463           CheckCanStartAnimationOnCompositor(paint_artifact_compositor);
464       RecordCompositorAnimationFailureReasons(failure_reasons);
465 
466       if (failure_reasons == CompositorAnimations::kNoFailure) {
467         CreateCompositorAnimation();
468         StartAnimationOnCompositor(paint_artifact_compositor);
469         compositor_state_ = std::make_unique<CompositorState>(*this);
470       } else {
471         CancelIncompatibleAnimationsOnCompositor();
472       }
473     }
474   }
475 
476   return true;
477 }
478 
PostCommit(double timeline_time)479 void Animation::PostCommit(double timeline_time) {
480   compositor_pending_ = false;
481 
482   if (!compositor_state_ || compositor_state_->pending_action == kNone)
483     return;
484 
485   DCHECK_EQ(kStart, compositor_state_->pending_action);
486   if (compositor_state_->start_time) {
487     DCHECK_EQ(start_time_.value(), compositor_state_->start_time.value());
488     compositor_state_->pending_action = kNone;
489   }
490 }
491 
HasLowerCompositeOrdering(const Animation * animation1,const Animation * animation2,CompareAnimationsOrdering compare_animation_type)492 bool Animation::HasLowerCompositeOrdering(
493     const Animation* animation1,
494     const Animation* animation2,
495     CompareAnimationsOrdering compare_animation_type) {
496   AnimationClassPriority priority1 = AnimationPriority(*animation1);
497   AnimationClassPriority priority2 = AnimationPriority(*animation2);
498   if (priority1 != priority2)
499     return priority1 < priority2;
500 
501   // If the the animation class is CssAnimation or CssTransition, then first
502   // compare the owning element of animation1 and animation2, sort two of them
503   // by tree order of their conrresponding owning element
504   // The specs:
505   // https://drafts.csswg.org/css-animations-2/#animation-composite-order
506   // https://drafts.csswg.org/css-transitions-2/#animation-composite-order
507   if (priority1 != kDefaultPriority && animation1->effect() &&
508       animation2->effect()) {
509     // TODO(crbug.com/1043778): Implement and use OwningElement on CSSAnimation
510     // and CSSTransition.
511     auto* effect1 = DynamicTo<KeyframeEffect>(animation1->effect());
512     auto* effect2 = DynamicTo<KeyframeEffect>(animation2->effect());
513     Element* target1 = effect1->target();
514     Element* target2 = effect2->target();
515 
516     // The tree position comparison would take a longer time, thus affec the
517     // performance. We only do it when it comes to getAnimation.
518     if (*target1 != *target2) {
519       if (compare_animation_type == CompareAnimationsOrdering::kTreeOrder) {
520         return target1->compareDocumentPosition(target2) &
521                Node::kDocumentPositionFollowing;
522       } else {
523         return target1 < target2;
524       }
525     }
526 
527     // A pseudo-element has a higher composite ordering than its originating
528     // element, but lower than the originating element's children.
529     // Two pseudo-elements sharing the same originating element are sorted
530     // as follows:
531     // ::marker
532     // ::before
533     // other pseudo-elements (ordered by selector)
534     // ::after
535     const String& pseudo1 = effect1->pseudoElement();
536     const String& pseudo2 = effect2->pseudoElement();
537     PseudoPriority priority1 = ConvertStringtoPriority(pseudo1);
538     PseudoPriority priority2 = ConvertStringtoPriority(pseudo2);
539 
540     if (priority1 != priority2)
541       return priority1 < priority2;
542     if (priority1 == kOther && pseudo1 != pseudo2)
543       return CodeUnitCompareLessThan(pseudo1, pseudo2);
544 
545     // For two animatiions with the same target (including the pseudo-element
546     // selector) compare the SequenceNumber for now.
547     // TODO(crbug.com/1045835): Sort animation1 and animation2 based on their
548     // position in the computed value of "animation-name" property for
549     // CSSAnimations and transition property for CSSTransitions.
550     return animation1->SequenceNumber() < animation2->SequenceNumber();
551   }
552   // If the anmiations are not-CSS WebAnimation just compare them via generation
553   // time/ sequence number.
554   return animation1->SequenceNumber() < animation2->SequenceNumber();
555 }
556 
NotifyReady(double ready_time)557 void Animation::NotifyReady(double ready_time) {
558   // Complete the pending updates prior to updating the compositor state in
559   // order to ensure a correct start time for the compositor state without the
560   // need to duplicate the calculations.
561   if (pending_play_)
562     CommitPendingPlay(ready_time);
563   else if (pending_pause_)
564     CommitPendingPause(ready_time);
565 
566   if (compositor_state_ && compositor_state_->pending_action == kStart) {
567     DCHECK(!compositor_state_->start_time);
568     compositor_state_->pending_action = kNone;
569     compositor_state_->start_time = start_time_;
570   }
571 
572   // Notify of change to play state.
573   NotifyProbe();
574 }
575 
576 // Microtask for playing an animation.
577 // Refer to Step 8.3 'pending play task' in
578 // https://drafts.csswg.org/web-animations/#playing-an-animation-section.
CommitPendingPlay(double ready_time)579 void Animation::CommitPendingPlay(double ready_time) {
580   DCHECK(!Timing::IsNull(ready_time));
581   DCHECK(start_time_ || hold_time_);
582   DCHECK(pending_play_);
583   pending_play_ = false;
584 
585   // Update hold and start time.
586   if (hold_time_) {
587     // A: If animation’s hold time is resolved,
588     // A.1. Apply any pending playback rate on animation.
589     // A.2. Let new start time be the result of evaluating:
590     //        ready time - hold time / playback rate for animation.
591     //      If the playback rate is zero, let new start time be simply ready
592     //      time.
593     // A.3. Set the start time of animation to new start time.
594     // A.4. If animation’s playback rate is not 0, make animation’s hold time
595     //      unresolved.
596     ApplyPendingPlaybackRate();
597     if (playback_rate_ == 0) {
598       start_time_ = ready_time;
599     } else {
600       start_time_ = ready_time - hold_time_.value() / playback_rate_;
601       hold_time_ = base::nullopt;
602     }
603   } else if (start_time_ && pending_playback_rate_) {
604     // B: If animation’s start time is resolved and animation has a pending
605     //    playback rate,
606     // B.1. Let current time to match be the result of evaluating:
607     //        (ready time - start time) × playback rate for animation.
608     // B.2 Apply any pending playback rate on animation.
609     // B.3 If animation’s playback rate is zero, let animation’s hold time be
610     //     current time to match.
611     // B.4 Let new start time be the result of evaluating:
612     //       ready time - current time to match / playback rate for animation.
613     //     If the playback rate is zero, let new start time be simply ready
614     //     time.
615     // B.5 Set the start time of animation to new start time.
616     double current_time_to_match =
617         (ready_time - start_time_.value()) * playback_rate_;
618     ApplyPendingPlaybackRate();
619     if (playback_rate_ == 0) {
620       hold_time_ = current_time_to_match;
621       start_time_ = ready_time;
622     } else {
623       start_time_ = ready_time - current_time_to_match / playback_rate_;
624     }
625   }
626 
627   // 8.4 Resolve animation’s current ready promise with animation.
628   if (ready_promise_ &&
629       ready_promise_->GetState() == AnimationPromise::kPending)
630     ResolvePromiseMaybeAsync(ready_promise_.Get());
631 
632   // 8.5 Run the procedure to update an animation’s finished state for
633   //     animation with the did seek flag set to false, and the synchronously
634   //     notify flag set to false.
635   UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
636 }
637 
638 // Microtask for pausing an animation.
639 // Refer to step 7 'pending pause task' in
640 // https://drafts.csswg.org/web-animations-1/#pausing-an-animation-section
CommitPendingPause(double ready_time)641 void Animation::CommitPendingPause(double ready_time) {
642   DCHECK(pending_pause_);
643   pending_pause_ = false;
644 
645   // 1. Let ready time be the time value of the timeline associated with
646   //    animation at the moment when the user agent completed processing
647   //    necessary to suspend playback of animation’s associated effect.
648   // 2. If animation’s start time is resolved and its hold time is not resolved,
649   //    let animation’s hold time be the result of evaluating
650   //    (ready time - start time) × playback rate.
651   if (start_time_ && !hold_time_)
652     hold_time_ = (ready_time - start_time_.value()) * playback_rate_;
653 
654   // 3. Apply any pending playback rate on animation.
655   // 4. Make animation’s start time unresolved.
656   ApplyPendingPlaybackRate();
657   start_time_ = base::nullopt;
658 
659   // 5. Resolve animation’s current ready promise with animation.
660   if (ready_promise_ &&
661       ready_promise_->GetState() == AnimationPromise::kPending)
662     ResolvePromiseMaybeAsync(ready_promise_.Get());
663 
664   // 6. Run the procedure to update an animation’s finished state for animation
665   //    with the did seek flag set to false (continuous), and the synchronously
666   //    notify flag set to false.
667   UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
668 }
669 
Affects(const Element & element,const CSSProperty & property) const670 bool Animation::Affects(const Element& element,
671                         const CSSProperty& property) const {
672   const auto* effect = DynamicTo<KeyframeEffect>(content_.Get());
673   if (!effect)
674     return false;
675 
676   return (effect->EffectTarget() == &element) &&
677          effect->Affects(PropertyHandle(property));
678 }
679 
CalculateStartTime(double current_time) const680 base::Optional<double> Animation::CalculateStartTime(
681     double current_time) const {
682   base::Optional<double> start_time;
683   if (timeline_) {
684     base::Optional<double> timeline_time = timeline_->CurrentTimeSeconds();
685     if (timeline_time)
686       start_time = timeline_time.value() - current_time / playback_rate_;
687     // TODO(crbug.com/916117): Handle NaN time for scroll-linked animations.
688     DCHECK(start_time || timeline_->IsScrollTimeline());
689   }
690   return start_time;
691 }
692 
CalculateCurrentTime() const693 base::Optional<double> Animation::CalculateCurrentTime() const {
694   if (!start_time_ || !timeline_ || !timeline_->IsActive())
695     return base::nullopt;
696   base::Optional<double> timeline_time = timeline_->CurrentTimeSeconds();
697 
698   if (!timeline_time) {
699     // timeline_time can be null only when the timeline is inactive
700     DCHECK(!timeline_->IsActive());
701     return base::nullopt;
702   }
703 
704   return (timeline_time.value() - start_time_.value()) * playback_rate_;
705 }
706 
707 // https://drafts.csswg.org/web-animations/#setting-the-start-time-of-an-animation
setStartTime(base::Optional<double> start_time_ms,ExceptionState & exception_state)708 void Animation::setStartTime(base::Optional<double> start_time_ms,
709                              ExceptionState& exception_state) {
710   // TODO(crbug.com/916117): Implement setting start time for scroll-linked
711   // animations.
712   if (timeline_ && timeline_->IsScrollTimeline()) {
713     exception_state.ThrowDOMException(
714         DOMExceptionCode::kNotSupportedError,
715         "Scroll-linked WebAnimation currently does not support setting start"
716         " time.");
717     return;
718   }
719 
720   bool had_start_time = start_time_.has_value();
721 
722   // 1. Let timeline time be the current time value of the timeline that
723   //    animation is associated with. If there is no timeline associated with
724   //    animation or the associated timeline is inactive, let the timeline time
725   //    be unresolved.
726   base::Optional<double> timeline_time = timeline_ && timeline_->IsActive()
727                                              ? timeline_->CurrentTimeSeconds()
728                                              : base::nullopt;
729 
730   // 2. If timeline time is unresolved and new start time is resolved, make
731   //    animation’s hold time unresolved.
732   // This preserves the invariant that when we don’t have an active timeline it
733   // is only possible to set either the start time or the animation’s current
734   // time.
735   if (!timeline_time && start_time_ms)
736     hold_time_ = base::nullopt;
737 
738   // 3. Let previous current time be animation’s current time.
739   base::Optional<double> previous_current_time = CurrentTimeInternal();
740 
741   // 4. Apply any pending playback rate on animation.
742   ApplyPendingPlaybackRate();
743 
744   // 5. Set animation’s start time to new start time.
745   base::Optional<double> new_start_time;
746   if (start_time_ms)
747     new_start_time = MillisecondsToSeconds(start_time_ms.value());
748   start_time_ = new_start_time;
749 
750   // 6. Update animation’s hold time based on the first matching condition from
751   //    the following,
752   // 6a If new start time is resolved,
753   //      If animation’s playback rate is not zero, make animation’s hold time
754   //      unresolved.
755   // 6b Otherwise (new start time is unresolved),
756   //      Set animation’s hold time to previous current time even if previous
757   //      current time is unresolved.
758   if (start_time_) {
759     if (playback_rate_ != 0)
760       hold_time_ = base::nullopt;
761   } else {
762     hold_time_ = previous_current_time;
763   }
764 
765   // 7. If animation has a pending play task or a pending pause task, cancel
766   //    that task and resolve animation’s current ready promise with animation.
767   if (PendingInternal()) {
768     pending_pause_ = false;
769     pending_play_ = false;
770     if (ready_promise_ &&
771         ready_promise_->GetState() == AnimationPromise::kPending)
772       ResolvePromiseMaybeAsync(ready_promise_.Get());
773   }
774 
775   // 8. Run the procedure to update an animation’s finished state for animation
776   //    with the did seek flag set to true (discontinuous), and the
777   //    synchronously notify flag set to false (async).
778   UpdateFinishedState(UpdateType::kDiscontinuous, NotificationType::kAsync);
779 
780   // Update user agent.
781   base::Optional<double> new_current_time = CurrentTimeInternal();
782   if (previous_current_time != new_current_time) {
783     SetOutdated();
784   } else if (!had_start_time && start_time_) {
785     // Even though this animation is not outdated, time to effect change is
786     // infinity until start time is set.
787     ForceServiceOnNextFrame();
788   }
789   SetCompositorPending(/*effect_changed=*/false);
790 
791   NotifyProbe();
792 }
793 
setStartTime(double start_time_ms,bool is_null,ExceptionState & exception_state)794 void Animation::setStartTime(double start_time_ms,
795                              bool is_null,
796                              ExceptionState& exception_state) {
797   setStartTime(is_null ? base::nullopt : base::make_optional(start_time_ms),
798                exception_state);
799 }
800 
801 // https://drafts.csswg.org/web-animations-1/#setting-the-associated-effect
setEffect(AnimationEffect * new_effect)802 void Animation::setEffect(AnimationEffect* new_effect) {
803   // 1. Let old effect be the current associated effect of animation, if any.
804   AnimationEffect* old_effect = content_;
805 
806   // 2. If new effect is the same object as old effect, abort this procedure.
807   if (new_effect == old_effect)
808     return;
809 
810   // 3. If animation has a pending pause task, reschedule that task to run as
811   //    soon as animation is ready.
812   // 4. If animation has a pending play task, reschedule that task to run as
813   //    soon as animation is ready to play new effect.
814   // No special action required for a reschedule. The pending_pause_ and
815   // pending_play_ flags remain unchanged.
816 
817   // 5. If new effect is not null and if new effect is the associated effect of
818   //    another previous animation, run the procedure to set the associated
819   //    effect of an animation (this procedure) on previous animation passing
820   //    null as new effect.
821   if (new_effect && new_effect->GetAnimation())
822     new_effect->GetAnimation()->setEffect(nullptr);
823 
824   // 6. Let the associated effect of the animation be the new effect.
825   if (old_effect)
826     old_effect->Detach();
827   content_ = new_effect;
828   if (new_effect)
829     new_effect->Attach(this);
830   SetOutdated();
831 
832   // 7. Run the procedure to update an animation’s finished state for animation
833   //    with the did seek flag set to false (continuous), and the synchronously
834   //    notify flag set to false (async).
835   UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
836 
837   SetCompositorPending(/*effect_change=*/true);
838 
839   // Notify of a potential state change.
840   NotifyProbe();
841 
842   // The effect is no longer associated with CSS properties.
843   if (new_effect) {
844     new_effect->SetIgnoreCssTimingProperties();
845     if (KeyframeEffect* keyframe_effect = DynamicTo<KeyframeEffect>(new_effect))
846       keyframe_effect->SetIgnoreCSSKeyframes();
847   }
848 
849   // The remaining steps are for handling CSS animation and transition events.
850   // Both use an event delegate to dispatch events, which must be reattached to
851   // the new effect.
852 
853   // When the animation no longer has an associated effect, calls to
854   // Animation::Update will no longer update the animation timing and,
855   // consequently, do not trigger animation or transition events.
856   // Each transitionrun or transitionstart requires a corresponding
857   // transitionend or transitioncancel.
858   // https://drafts.csswg.org/css-transitions-2/#event-dispatch
859   // Similarly, each animationstart requires a corresponding animationend or
860   // animationcancel.
861   // https://drafts.csswg.org/css-animations-2/#event-dispatch
862   AnimationEffect::EventDelegate* old_event_delegate =
863       old_effect ? old_effect->GetEventDelegate() : nullptr;
864   if (!new_effect && old_effect && old_event_delegate) {
865     // If the animation|transition has no target effect, the timing phase is set
866     // according to the first matching condition from below:
867     //   If the current time is unresolved,
868     //     The timing phase is ‘idle’.
869     //   If current time < 0,
870     //     The timing phase is ‘before’.
871     //   Otherwise,
872     //     The timing phase is ‘after’.
873     base::Optional<double> current_time = CurrentTimeInternal();
874     Timing::Phase phase;
875     if (!current_time)
876       phase = Timing::kPhaseNone;
877     else if (current_time < 0)
878       phase = Timing::kPhaseBefore;
879     else
880       phase = Timing::kPhaseAfter;
881     old_event_delegate->OnEventCondition(*old_effect, phase);
882     return;
883   }
884 
885   if (!new_effect || !old_effect)
886     return;
887 
888   // Use the original target for event targeting.
889   Element* target = To<KeyframeEffect>(old_effect)->target();
890   if (!target)
891     return;
892 
893   // Attach an event delegate to the new effect.
894   AnimationEffect::EventDelegate* new_event_delegate =
895       CreateEventDelegate(target, old_event_delegate);
896   new_effect->SetEventDelegate(new_event_delegate);
897 
898   // Force an update to the timing model to ensure correct ordering of
899   // animation or transition events.
900   Update(kTimingUpdateOnDemand);
901 }
902 
PlayStateString() const903 String Animation::PlayStateString() const {
904   return PlayStateString(CalculateAnimationPlayState());
905 }
906 
PlayStateString(AnimationPlayState play_state)907 const char* Animation::PlayStateString(AnimationPlayState play_state) {
908   switch (play_state) {
909     case kIdle:
910       return "idle";
911     case kPending:
912       return "pending";
913     case kRunning:
914       return "running";
915     case kPaused:
916       return "paused";
917     case kFinished:
918       return "finished";
919     default:
920       NOTREACHED();
921       return "";
922   }
923 }
924 
925 // https://drafts.csswg.org/web-animations/#play-states
CalculateAnimationPlayState() const926 Animation::AnimationPlayState Animation::CalculateAnimationPlayState() const {
927   // 1. All of the following conditions are true:
928   //    * The current time of animation is unresolved, and
929   //    * animation does not have either a pending play task or a pending pause
930   //      task,
931   //    then idle.
932   if (!CurrentTimeInternal() && !PendingInternal())
933     return kIdle;
934 
935   // 2. Either of the following conditions are true:
936   //    * animation has a pending pause task, or
937   //    * both the start time of animation is unresolved and it does not have a
938   //      pending play task,
939   //    then paused.
940   if (pending_pause_ || (!start_time_ && !pending_play_))
941     return kPaused;
942 
943   // 3.  For animation, current time is resolved and either of the following
944   //     conditions are true:
945   //     * animation’s effective playback rate > 0 and current time ≥ target
946   //       effect end; or
947   //     * animation’s effective playback rate < 0 and current time ≤ 0,
948   //    then finished.
949   if (Limited())
950     return kFinished;
951 
952   // 4.  Otherwise
953   return kRunning;
954 }
955 
PendingInternal() const956 bool Animation::PendingInternal() const {
957   return pending_pause_ || pending_play_;
958 }
959 
pending() const960 bool Animation::pending() const {
961   return PendingInternal();
962 }
963 
964 // https://drafts.csswg.org/web-animations-1/#reset-an-animations-pending-tasks.
ResetPendingTasks()965 void Animation::ResetPendingTasks() {
966   // 1. If animation does not have a pending play task or a pending pause task,
967   //    abort this procedure.
968   if (!PendingInternal())
969     return;
970 
971   // 2. If animation has a pending play task, cancel that task.
972   // 3. If animation has a pending pause task, cancel that task.
973   pending_play_ = false;
974   pending_pause_ = false;
975 
976   // 4. Apply any pending playback rate on animation.
977   ApplyPendingPlaybackRate();
978 
979   // 5. Reject animation’s current ready promise with a DOMException named
980   //    "AbortError".
981   // 6. Let animation’s current ready promise be the result of creating a new
982   //    resolved Promise object with value animation in the relevant Realm of
983   //    animation.
984   if (ready_promise_)
985     RejectAndResetPromiseMaybeAsync(ready_promise_.Get());
986 }
987 
988 // ----------------------------------------------
989 // Pause methods.
990 // ----------------------------------------------
991 
992 // https://drafts.csswg.org/web-animations/#pausing-an-animation-section
pause(ExceptionState & exception_state)993 void Animation::pause(ExceptionState& exception_state) {
994   // TODO(crbug.com/916117): Implement pause for scroll-linked animations.
995   if (timeline_ && timeline_->IsScrollTimeline()) {
996     exception_state.ThrowDOMException(
997         DOMExceptionCode::kNotSupportedError,
998         "Scroll-linked WebAnimation currently does not support pause.");
999     return;
1000   }
1001 
1002   // 1. If animation has a pending pause task, abort these steps.
1003   // 2. If the play state of animation is paused, abort these steps.
1004   if (pending_pause_ || CalculateAnimationPlayState() == kPaused)
1005     return;
1006 
1007   // 3.  If the animation’s current time is unresolved, perform the steps
1008   //     according to the first matching condition from below:
1009   // 3a. If animation’s playback rate is ≥ 0,
1010   //       Let animation’s hold time be zero.
1011   // 3b. Otherwise,
1012   //       If associated effect end for animation is positive infinity, throw an
1013   //       "InvalidStateError" DOMException and abort these steps. Otherwise,
1014   //       let animation’s hold time be associated effect end.
1015   base::Optional<double> current_time = CurrentTimeInternal();
1016   if (!current_time) {
1017     if (playback_rate_ >= 0) {
1018       hold_time_ = 0;
1019     } else {
1020       if (EffectEnd() == std::numeric_limits<double>::infinity()) {
1021         exception_state.ThrowDOMException(
1022             DOMExceptionCode::kInvalidStateError,
1023             "Cannot play reversed Animation with infinite target effect end.");
1024         return;
1025       }
1026       hold_time_ = EffectEnd();
1027     }
1028   }
1029 
1030   // 4. Let has pending ready promise be a boolean flag that is initially false.
1031   // 5. If animation has a pending play task, cancel that task and let has
1032   //    pending ready promise be true.
1033   // 6. If has pending ready promise is false, set animation’s current ready
1034   //    promise to a new promise in the relevant Realm of animation.
1035   if (pending_play_)
1036     pending_play_ = false;
1037   else if (ready_promise_)
1038     ready_promise_->Reset();
1039 
1040   // 7. Schedule a task to be executed at the first possible moment after the
1041   //    user agent has performed any processing necessary to suspend the
1042   //    playback of animation’s associated effect, if any.
1043   pending_pause_ = true;
1044   pending_play_ = false;
1045 
1046   SetOutdated();
1047   SetCompositorPending(false);
1048 
1049   // 8. Run the procedure to update an animation’s finished state for animation
1050   //    with the did seek flag set to false (continuous) , and thesynchronously
1051   //    notify flag set to false.
1052   UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
1053 
1054   NotifyProbe();
1055 }
1056 
1057 // ----------------------------------------------
1058 // Play methods.
1059 // ----------------------------------------------
1060 
1061 // Refer to the unpause operation in the following spec:
1062 // https://drafts.csswg.org/css-animations-1/#animation-play-state
Unpause()1063 void Animation::Unpause() {
1064   if (CalculateAnimationPlayState() != kPaused)
1065     return;
1066   PlayInternal(AutoRewind::kDisabled, ASSERT_NO_EXCEPTION);
1067 }
1068 
1069 // https://drafts.csswg.org/web-animations/#programming-interface.
play(ExceptionState & exception_state)1070 void Animation::play(ExceptionState& exception_state) {
1071   // Begin or resume playback of the animation by running the procedure to
1072   // play an animation passing true as the value of the auto-rewind flag.
1073   PlayInternal(AutoRewind::kEnabled, exception_state);
1074 }
1075 
1076 // https://drafts.csswg.org/web-animations/#playing-an-animation-section.
PlayInternal(AutoRewind auto_rewind,ExceptionState & exception_state)1077 void Animation::PlayInternal(AutoRewind auto_rewind,
1078                              ExceptionState& exception_state) {
1079   // 1. Let aborted pause be a boolean flag that is true if animation has a
1080   //    pending pause task, and false otherwise.
1081   // 2. Let has pending ready promise be a boolean flag that is initially false.
1082   bool aborted_pause = pending_pause_;
1083   bool has_pending_ready_promise = false;
1084 
1085   // 3. Perform the steps corresponding to the first matching condition from the
1086   //    following, if any:
1087   //
1088   // 3a If animation’s effective playback rate > 0, the auto-rewind flag is true
1089   //    and either animation’s:
1090   //      current time is unresolved, or
1091   //      current time < zero, or
1092   //      current time ≥ target effect end,
1093   //    Set animation’s hold time to zero.
1094   //
1095   // 3b If animation’s effective playback rate < 0, the auto-rewind flag is true
1096   //    and either animation’s:
1097   //      current time is unresolved, or
1098   //      current time ≤ zero, or
1099   //      current time > target effect end,
1100   //    If target effect end is positive infinity, throw an "InvalidStateError"
1101   //    DOMException and abort these steps. Otherwise, set animation’s hold time
1102   //    to target effect end.
1103   //
1104   // 3c If animation’s effective playback rate = 0 and animation’s current time
1105   //    is unresolved,
1106   //    Set animation’s hold time to zero.
1107   double effective_playback_rate = EffectivePlaybackRate();
1108   base::Optional<double> current_time = CurrentTimeInternal();
1109 
1110   // TODO(crbug.com/1012073): This should be able to be extracted into a
1111   // function in AnimationTimeline that each child class can override for their
1112   // own special behavior.
1113   double initial_hold_time = 0;
1114   if (timeline_ && timeline_->IsScrollTimeline() && timeline_->IsActive()) {
1115     base::Optional<double> timeline_time = timeline_->CurrentTimeSeconds();
1116     if (timeline_time) {
1117       // TODO(crbug.com/924159): Once inactive timelines are supported we need
1118       // to re-evaluate if it is desired behavior to adjust the hold time when
1119       // playback rate is set before play().
1120       initial_hold_time = timeline_time.value() * effective_playback_rate;
1121     }
1122   }
1123 
1124   if (effective_playback_rate > 0 && auto_rewind == AutoRewind::kEnabled &&
1125       (!current_time || current_time < 0 || current_time >= EffectEnd())) {
1126     hold_time_ = initial_hold_time;
1127   } else if (effective_playback_rate < 0 &&
1128              auto_rewind == AutoRewind::kEnabled &&
1129              (!current_time || current_time <= 0 ||
1130               current_time > EffectEnd())) {
1131     if (EffectEnd() == std::numeric_limits<double>::infinity()) {
1132       exception_state.ThrowDOMException(
1133           DOMExceptionCode::kInvalidStateError,
1134           "Cannot play reversed Animation with infinite target effect end.");
1135       return;
1136     }
1137     hold_time_ = initial_hold_time + EffectEnd();
1138   } else if (effective_playback_rate == 0 && !current_time) {
1139     hold_time_ = initial_hold_time;
1140   }
1141 
1142   // 4. If animation has a pending play task or a pending pause task,
1143   //   4.1 Cancel that task.
1144   //   4.2 Set has pending ready promise to true.
1145   if (pending_play_ || pending_pause_) {
1146     pending_play_ = pending_pause_ = false;
1147     has_pending_ready_promise = true;
1148   }
1149 
1150   // 5. If the following three conditions are all satisfied:
1151   //      animation’s hold time is unresolved, and
1152   //      aborted pause is false, and
1153   //      animation does not have a pending playback rate,
1154   //    abort this procedure.
1155   if (!hold_time_ && !aborted_pause && !pending_playback_rate_)
1156     return;
1157 
1158   // 6. If animation’s hold time is resolved, let its start time be unresolved.
1159   if (hold_time_)
1160     start_time_ = base::nullopt;
1161 
1162   // 7. If has pending ready promise is false, let animation’s current ready
1163   //    promise be a new promise in the relevant Realm of animation.
1164   if (ready_promise_ && !has_pending_ready_promise)
1165     ready_promise_->Reset();
1166 
1167   // 8. Schedule a task to run as soon as animation is ready.
1168   pending_play_ = true;
1169   finished_ = false;
1170   SetOutdated();
1171   SetCompositorPending(/*effect_changed=*/false);
1172 
1173   // 9. Run the procedure to update an animation’s finished state for animation
1174   //    with the did seek flag set to false, and the synchronously notify flag
1175   //    set to false.
1176   // Boolean valued arguments replaced with enumerated values for clarity.
1177   UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
1178 
1179   // Notify change to pending play or finished state.
1180   NotifyProbe();
1181 }
1182 
1183 // https://drafts.csswg.org/web-animations/#reversing-an-animation-section
reverse(ExceptionState & exception_state)1184 void Animation::reverse(ExceptionState& exception_state) {
1185   // TODO(crbug.com/916117): Implement reverse for scroll-linked animations.
1186   if (timeline_ && timeline_->IsScrollTimeline()) {
1187     exception_state.ThrowDOMException(
1188         DOMExceptionCode::kNotSupportedError,
1189         "Scroll-linked WebAnimation currently does not support reverse.");
1190     return;
1191   }
1192 
1193   // 1. If there is no timeline associated with animation, or the associated
1194   //    timeline is inactive throw an "InvalidStateError" DOMException and abort
1195   //    these steps.
1196   if (!timeline_ || !timeline_->IsActive()) {
1197     exception_state.ThrowDOMException(
1198         DOMExceptionCode::kInvalidStateError,
1199         "Cannot reverse an animation with no active timeline");
1200     return;
1201   }
1202 
1203   // 2. Let original pending playback rate be animation’s pending playback rate.
1204   // 3. Let animation’s pending playback rate be the additive inverse of its
1205   //    effective playback rate (i.e. -effective playback rate).
1206   base::Optional<double> original_pending_playback_rate =
1207       pending_playback_rate_;
1208   pending_playback_rate_ = -EffectivePlaybackRate();
1209 
1210   // Resolve precision issue at zero.
1211   if (pending_playback_rate_.value() == -0)
1212     pending_playback_rate_ = 0;
1213 
1214   // 4. Run the steps to play an animation for animation with the auto-rewind
1215   //    flag set to true.
1216   //    If the steps to play an animation throw an exception, set animation’s
1217   //    pending playback rate to original pending playback rate and propagate
1218   //    the exception.
1219   PlayInternal(AutoRewind::kEnabled, exception_state);
1220   if (exception_state.HadException())
1221     pending_playback_rate_ = original_pending_playback_rate;
1222 }
1223 
1224 // ----------------------------------------------
1225 // Finish methods.
1226 // ----------------------------------------------
1227 
1228 // https://drafts.csswg.org/web-animations/#finishing-an-animation-section
finish(ExceptionState & exception_state)1229 void Animation::finish(ExceptionState& exception_state) {
1230   if (!EffectivePlaybackRate()) {
1231     exception_state.ThrowDOMException(
1232         DOMExceptionCode::kInvalidStateError,
1233         "Cannot finish Animation with a playbackRate of 0.");
1234     return;
1235   }
1236   if (EffectivePlaybackRate() > 0 &&
1237       EffectEnd() == std::numeric_limits<double>::infinity()) {
1238     exception_state.ThrowDOMException(
1239         DOMExceptionCode::kInvalidStateError,
1240         "Cannot finish Animation with an infinite target effect end.");
1241     return;
1242   }
1243 
1244   ApplyPendingPlaybackRate();
1245 
1246   double new_current_time = playback_rate_ < 0 ? 0 : EffectEnd();
1247   SetCurrentTimeInternal(new_current_time);
1248 
1249   if (!start_time_ && timeline_ && timeline_->IsActive())
1250     start_time_ = CalculateStartTime(new_current_time);
1251 
1252   if (pending_pause_ && start_time_) {
1253     hold_time_ = base::nullopt;
1254     pending_pause_ = false;
1255     if (ready_promise_)
1256       ResolvePromiseMaybeAsync(ready_promise_.Get());
1257   }
1258   if (pending_play_ && start_time_) {
1259     pending_play_ = false;
1260     if (ready_promise_)
1261       ResolvePromiseMaybeAsync(ready_promise_.Get());
1262   }
1263 
1264   SetOutdated();
1265   UpdateFinishedState(UpdateType::kDiscontinuous, NotificationType::kSync);
1266 
1267   // Notify of change to finished state.
1268   NotifyProbe();
1269 }
1270 
UpdateFinishedState(UpdateType update_type,NotificationType notification_type)1271 void Animation::UpdateFinishedState(UpdateType update_type,
1272                                     NotificationType notification_type) {
1273   bool did_seek = update_type == UpdateType::kDiscontinuous;
1274   // 1. Calculate the unconstrained current time. The dependency on did_seek is
1275   // required to accommodate timelines that may change direction. Without this
1276   // distinction, a once-finished animation would remain finished even when its
1277   // timeline progresses in the opposite direction.
1278   base::Optional<double> unconstrained_current_time =
1279       did_seek ? CurrentTimeInternal() : CalculateCurrentTime();
1280 
1281   // 2. Conditionally update the hold time.
1282   if (unconstrained_current_time && start_time_ && !pending_play_ &&
1283       !pending_pause_) {
1284     // Can seek outside the bounds of the active effect. Set the hold time to
1285     // the unconstrained value of the current time in the event that this update
1286     // is the result of explicitly setting the current time and the new time
1287     // is out of bounds. An update due to a time tick should not snap the hold
1288     // value back to the boundary if previously set outside the normal effect
1289     // boundary. The value of previous current time is used to retain this
1290     // value.
1291     double playback_rate = EffectivePlaybackRate();
1292     if (playback_rate > 0 && unconstrained_current_time >= EffectEnd()) {
1293       hold_time_ = did_seek ? unconstrained_current_time
1294                             : Max(previous_current_time_, EffectEnd());
1295     } else if (playback_rate < 0 && unconstrained_current_time <= 0) {
1296       hold_time_ = did_seek ? unconstrained_current_time
1297                             : Min(previous_current_time_, 0);
1298       // Hack for resolving precision issue at zero.
1299       if (hold_time_.value() == -0)
1300         hold_time_ = 0;
1301     } else if (playback_rate != 0) {
1302       // Update start time and reset hold time.
1303       if (did_seek && hold_time_)
1304         start_time_ = CalculateStartTime(hold_time_.value());
1305       hold_time_ = base::nullopt;
1306     }
1307   }
1308 
1309   // 3. Set the previous current time.
1310   previous_current_time_ = CurrentTimeInternal();
1311 
1312   // 4. Set the current finished state.
1313   AnimationPlayState play_state = CalculateAnimationPlayState();
1314   if (play_state == kFinished) {
1315     // 5. Setup finished notification.
1316     if (notification_type == NotificationType::kSync)
1317       CommitFinishNotification();
1318     else
1319       ScheduleAsyncFinish();
1320   } else {
1321     // 6. If not finished but the current finished promise is already resolved,
1322     //    create a new promise.
1323     finished_ = pending_finish_notification_ = false;
1324     if (finished_promise_ &&
1325         finished_promise_->GetState() == AnimationPromise::kResolved) {
1326       finished_promise_->Reset();
1327     }
1328   }
1329 }
1330 
ScheduleAsyncFinish()1331 void Animation::ScheduleAsyncFinish() {
1332   // Run a task to handle the finished promise and event as a microtask. With
1333   // the exception of an explicit call to Animation::finish, it is important to
1334   // apply these updates asynchronously as it is possible to enter the finished
1335   // state temporarily.
1336   pending_finish_notification_ = true;
1337   if (!has_queued_microtask_) {
1338     Microtask::EnqueueMicrotask(
1339         WTF::Bind(&Animation::AsyncFinishMicrotask, WrapWeakPersistent(this)));
1340     has_queued_microtask_ = true;
1341   }
1342 }
1343 
AsyncFinishMicrotask()1344 void Animation::AsyncFinishMicrotask() {
1345   // Resolve the finished promise and queue the finished event only if the
1346   // animation is still in a pending finished state. It is possible that the
1347   // transition was only temporary.
1348   if (pending_finish_notification_) {
1349     // A pending play or pause must resolve before the finish promise.
1350     if (PendingInternal() && timeline_)
1351       NotifyReady(timeline_->CurrentTimeSeconds().value_or(0));
1352     CommitFinishNotification();
1353   }
1354 
1355   // This is a once callback and needs to be re-armed.
1356   has_queued_microtask_ = false;
1357 }
1358 
1359 // Refer to 'finished notification steps' in
1360 // https://drafts.csswg.org/web-animations-1/#updating-the-finished-state
CommitFinishNotification()1361 void Animation::CommitFinishNotification() {
1362   pending_finish_notification_ = false;
1363 
1364   // 1. If animation’s play state is not equal to finished, abort these steps.
1365   if (CalculateAnimationPlayState() != kFinished)
1366     return;
1367 
1368   // 2. Resolve animation’s current finished promise object with animation.
1369   if (finished_promise_ &&
1370       finished_promise_->GetState() == AnimationPromise::kPending) {
1371     ResolvePromiseMaybeAsync(finished_promise_.Get());
1372   }
1373 
1374   // 3. Create an AnimationPlaybackEvent, finishEvent.
1375   QueueFinishedEvent();
1376 }
1377 
1378 // https://drafts.csswg.org/web-animations/#setting-the-playback-rate-of-an-animation
updatePlaybackRate(double playback_rate,ExceptionState & exception_state)1379 void Animation::updatePlaybackRate(double playback_rate,
1380                                    ExceptionState& exception_state) {
1381   // TODO(crbug.com/916117): Implement updatePlaybackRate for scroll-linked
1382   // animations.
1383   if (timeline_ && timeline_->IsScrollTimeline()) {
1384     exception_state.ThrowDOMException(
1385         DOMExceptionCode::kNotSupportedError,
1386         "Scroll-linked WebAnimation currently does not support"
1387         " updatePlaybackRate.");
1388     return;
1389   }
1390 
1391   // 1. Let previous play state be animation’s play state.
1392   // 2. Let animation’s pending playback rate be new playback rate.
1393   AnimationPlayState play_state = CalculateAnimationPlayState();
1394   pending_playback_rate_ = playback_rate;
1395 
1396   // 3. Perform the steps corresponding to the first matching condition from
1397   //    below:
1398   //
1399   // 3a If animation has a pending play task or a pending pause task,
1400   //    Abort these steps.
1401   if (PendingInternal())
1402     return;
1403 
1404   switch (play_state) {
1405     // 3b If previous play state is idle or paused,
1406     //    Apply any pending playback rate on animation.
1407     case kIdle:
1408     case kPaused:
1409       ApplyPendingPlaybackRate();
1410       break;
1411 
1412     // 3c If previous play state is finished,
1413     //    3c.1 Let the unconstrained current time be the result of calculating
1414     //         the current time of animation substituting an unresolved time
1415     //          value for the hold time.
1416     //    3c.2 Let animation’s start time be the result of evaluating the
1417     //         following expression:
1418     //    timeline time - (unconstrained current time / pending playback rate)
1419     // Where timeline time is the current time value of the timeline associated
1420     // with animation.
1421     //    3c.3 If pending playback rate is zero, let animation’s start time be
1422     //         timeline time.
1423     //    3c.4 Apply any pending playback rate on animation.
1424     //    3c.5 Run the procedure to update an animation’s finished state for
1425     //         animation with the did seek flag set to false, and the
1426     //         synchronously notify flag set to false.
1427     case kFinished: {
1428       base::Optional<double> unconstrained_current_time =
1429           CalculateCurrentTime();
1430       base::Optional<double> timeline_time =
1431           timeline_ ? timeline_->CurrentTimeSeconds() : base::nullopt;
1432       if (playback_rate) {
1433         if (timeline_time) {
1434           start_time_ = (timeline_time && unconstrained_current_time)
1435                             ? base::make_optional<double>(
1436                                   (timeline_time.value() -
1437                                    unconstrained_current_time.value()) /
1438                                   playback_rate)
1439                             : base::nullopt;
1440         }
1441       } else {
1442         start_time_ = timeline_time;
1443       }
1444       ApplyPendingPlaybackRate();
1445       UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
1446       SetCompositorPending(false);
1447       SetOutdated();
1448       NotifyProbe();
1449       break;
1450     }
1451 
1452     // 3d Otherwise,
1453     // Run the procedure to play an animation for animation with the
1454     // auto-rewind flag set to false.
1455     case kRunning:
1456       PlayInternal(AutoRewind::kDisabled, exception_state);
1457       break;
1458 
1459     case kUnset:
1460     case kPending:
1461       NOTREACHED();
1462   }
1463 }
1464 
finished(ScriptState * script_state)1465 ScriptPromise Animation::finished(ScriptState* script_state) {
1466   if (!finished_promise_) {
1467     finished_promise_ = MakeGarbageCollected<AnimationPromise>(
1468         ExecutionContext::From(script_state));
1469     // Do not report unhandled rejections of the finished promise.
1470     finished_promise_->MarkAsHandled();
1471 
1472     // Defer resolving the finished promise if the finish notification task is
1473     // pending. The finished state could change before the next microtask
1474     // checkpoint.
1475     if (CalculateAnimationPlayState() == kFinished &&
1476         !pending_finish_notification_)
1477       finished_promise_->Resolve(this);
1478   }
1479   return finished_promise_->Promise(script_state->World());
1480 }
1481 
ready(ScriptState * script_state)1482 ScriptPromise Animation::ready(ScriptState* script_state) {
1483   // Check for a pending state change prior to checking the ready promise, since
1484   // the pending check may force a style flush, which in turn could trigger a
1485   // reset of the ready promise when resolving a change to the
1486   // animationPlayState style.
1487   bool is_pending = pending();
1488   if (!ready_promise_) {
1489     ready_promise_ = MakeGarbageCollected<AnimationPromise>(
1490         ExecutionContext::From(script_state));
1491     // Do not report unhandled rejections of the ready promise.
1492     ready_promise_->MarkAsHandled();
1493     if (!is_pending)
1494       ready_promise_->Resolve(this);
1495   }
1496   return ready_promise_->Promise(script_state->World());
1497 }
1498 
InterfaceName() const1499 const AtomicString& Animation::InterfaceName() const {
1500   return event_target_names::kAnimation;
1501 }
1502 
GetExecutionContext() const1503 ExecutionContext* Animation::GetExecutionContext() const {
1504   return ExecutionContextLifecycleObserver::GetExecutionContext();
1505 }
1506 
HasPendingActivity() const1507 bool Animation::HasPendingActivity() const {
1508   bool has_pending_promise =
1509       finished_promise_ &&
1510       finished_promise_->GetState() == AnimationPromise::kPending;
1511 
1512   return pending_finished_event_ || pending_cancelled_event_ ||
1513          pending_remove_event_ || has_pending_promise ||
1514          (!finished_ && HasEventListeners(event_type_names::kFinish));
1515 }
1516 
ContextDestroyed()1517 void Animation::ContextDestroyed() {
1518   finished_ = true;
1519   pending_finished_event_ = nullptr;
1520   pending_cancelled_event_ = nullptr;
1521   pending_remove_event_ = nullptr;
1522 }
1523 
DispatchEventInternal(Event & event)1524 DispatchEventResult Animation::DispatchEventInternal(Event& event) {
1525   if (pending_finished_event_ == &event)
1526     pending_finished_event_ = nullptr;
1527   if (pending_cancelled_event_ == &event)
1528     pending_cancelled_event_ = nullptr;
1529   if (pending_remove_event_ == &event)
1530     pending_remove_event_ = nullptr;
1531   return EventTargetWithInlineData::DispatchEventInternal(event);
1532 }
1533 
playbackRate() const1534 double Animation::playbackRate() const {
1535   return playback_rate_;
1536 }
1537 
EffectivePlaybackRate() const1538 double Animation::EffectivePlaybackRate() const {
1539   return pending_playback_rate_.value_or(playback_rate_);
1540 }
1541 
ApplyPendingPlaybackRate()1542 void Animation::ApplyPendingPlaybackRate() {
1543   if (pending_playback_rate_) {
1544     playback_rate_ = pending_playback_rate_.value();
1545     pending_playback_rate_ = base::nullopt;
1546   }
1547 }
1548 
setPlaybackRate(double playback_rate,ExceptionState & exception_state)1549 void Animation::setPlaybackRate(double playback_rate,
1550                                 ExceptionState& exception_state) {
1551   // TODO(crbug.com/924159): Update this after we add support for inactive
1552   // timelines and unresolved timeline.currentTime
1553 
1554   base::Optional<double> start_time_before = start_time_;
1555 
1556   // 1. Clear any pending playback rate on animation.
1557   // 2. Let previous time be the value of the current time of animation before
1558   //    changing the playback rate.
1559   // 3. Set the playback rate to new playback rate.
1560   // 4. If previous time is resolved, set the current time of animation to
1561   //    previous time
1562   pending_playback_rate_ = base::nullopt;
1563   double previous_current_time = currentTime();
1564   playback_rate_ = playback_rate;
1565   if (!Timing::IsNull(previous_current_time)) {
1566     setCurrentTime(previous_current_time, false, exception_state);
1567   }
1568 
1569   // Adds a UseCounter to check if setting playbackRate causes a compensatory
1570   // seek forcing a change in start_time_
1571   if (start_time_before && start_time_ != start_time_before &&
1572       CalculateAnimationPlayState() != kFinished) {
1573     UseCounter::Count(GetExecutionContext(),
1574                       WebFeature::kAnimationSetPlaybackRateCompensatorySeek);
1575   }
1576 
1577   SetCompositorPending(false);
1578   SetOutdated();
1579   NotifyProbe();
1580 }
1581 
ClearOutdated()1582 void Animation::ClearOutdated() {
1583   if (!outdated_)
1584     return;
1585   outdated_ = false;
1586   if (timeline_)
1587     timeline_->ClearOutdatedAnimation(this);
1588 }
1589 
SetOutdated()1590 void Animation::SetOutdated() {
1591   if (outdated_)
1592     return;
1593   outdated_ = true;
1594   if (timeline_)
1595     timeline_->SetOutdatedAnimation(this);
1596 }
1597 
ForceServiceOnNextFrame()1598 void Animation::ForceServiceOnNextFrame() {
1599   if (timeline_)
1600     timeline_->ScheduleServiceOnNextFrame();
1601 }
1602 
1603 CompositorAnimations::FailureReasons
CheckCanStartAnimationOnCompositor(const PaintArtifactCompositor * paint_artifact_compositor) const1604 Animation::CheckCanStartAnimationOnCompositor(
1605     const PaintArtifactCompositor* paint_artifact_compositor) const {
1606   CompositorAnimations::FailureReasons reasons =
1607       CheckCanStartAnimationOnCompositorInternal();
1608 
1609   if (auto* keyframe_effect = DynamicTo<KeyframeEffect>(content_.Get())) {
1610     reasons |= keyframe_effect->CheckCanStartAnimationOnCompositor(
1611         paint_artifact_compositor, playback_rate_);
1612   }
1613   return reasons;
1614 }
1615 
1616 CompositorAnimations::FailureReasons
CheckCanStartAnimationOnCompositorInternal() const1617 Animation::CheckCanStartAnimationOnCompositorInternal() const {
1618   CompositorAnimations::FailureReasons reasons =
1619       CompositorAnimations::kNoFailure;
1620 
1621   if (is_composited_animation_disabled_for_testing_)
1622     reasons |= CompositorAnimations::kAcceleratedAnimationsDisabled;
1623 
1624   if (EffectSuppressed())
1625     reasons |= CompositorAnimations::kEffectSuppressedByDevtools;
1626 
1627   // An Animation with zero playback rate will produce no visual output, so
1628   // there is no reason to composite it.
1629   if (EffectivePlaybackRate() == 0)
1630     reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
1631 
1632   if (!CurrentTimeInternal())
1633     reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
1634 
1635   // Cannot composite an infinite duration animation with a negative playback
1636   // rate. TODO(crbug.com/1029167): Fix calculation of compositor timing to
1637   // enable compositing provided the iteration duration is finite. Having an
1638   // infinite number of iterations in the animation should not impede the
1639   // ability to composite the animation.
1640   if (std::isinf(EffectEnd()) && EffectivePlaybackRate() < 0)
1641     reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
1642 
1643   // An Animation without a timeline effectively isn't playing, so there is no
1644   // reason to composite it. Additionally, mutating the timeline playback rate
1645   // is a debug feature available via devtools; we don't support this on the
1646   // compositor currently and there is no reason to do so.
1647   auto* document_timeline = DynamicTo<DocumentTimeline>(*timeline_);
1648   if (!document_timeline || document_timeline->PlaybackRate() != 1)
1649     reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
1650 
1651   // An Animation without an effect cannot produce a visual, so there is no
1652   // reason to composite it.
1653   if (!IsA<KeyframeEffect>(content_.Get()))
1654     reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
1655 
1656   // An Animation that is not playing will not produce a visual, so there is no
1657   // reason to composite it.
1658   if (!Playing())
1659     reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
1660 
1661   // TODO(crbug.com/916117): Support accelerated scroll linked animations.
1662   if (timeline_->IsScrollTimeline())
1663     reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
1664 
1665   return reasons;
1666 }
1667 
StartAnimationOnCompositor(const PaintArtifactCompositor * paint_artifact_compositor)1668 void Animation::StartAnimationOnCompositor(
1669     const PaintArtifactCompositor* paint_artifact_compositor) {
1670   DCHECK_EQ(CheckCanStartAnimationOnCompositor(paint_artifact_compositor),
1671             CompositorAnimations::kNoFailure);
1672   DCHECK(IsA<DocumentTimeline>(*timeline_));
1673 
1674   bool reversed = EffectivePlaybackRate() < 0;
1675 
1676   base::Optional<double> start_time = base::nullopt;
1677   double time_offset = 0;
1678   // Start the animation on the compositor with either a start time or time
1679   // offset. The start time is used for synchronous updates where the
1680   // compositor start time must be in precise alignment with the specified time
1681   // (e.g. after calling setStartTime). Asynchronous updates such as updating
1682   // the playback rate preserve current time even if the start time is set.
1683   // Asynchronous updates have an associated pending play or pending pause
1684   // task associated with them.
1685   if (start_time_ && !PendingInternal()) {
1686     start_time = To<DocumentTimeline>(*timeline_)
1687                      .ZeroTime()
1688                      .since_origin()
1689                      .InSecondsF() +
1690                  start_time_.value();
1691     if (reversed) {
1692       start_time =
1693           start_time.value() - (EffectEnd() / fabs(EffectivePlaybackRate()));
1694     }
1695   } else {
1696     base::Optional<double> current_time = CurrentTimeInternal();
1697     DCHECK(current_time);
1698     time_offset =
1699         reversed ? EffectEnd() - current_time.value() : current_time.value();
1700     time_offset = time_offset / fabs(EffectivePlaybackRate());
1701   }
1702 
1703   DCHECK(!start_time || !Timing::IsNull(start_time.value()));
1704   DCHECK_NE(compositor_group_, 0);
1705   DCHECK(To<KeyframeEffect>(content_.Get()));
1706   DCHECK(std::isfinite(time_offset));
1707   To<KeyframeEffect>(content_.Get())
1708       ->StartAnimationOnCompositor(compositor_group_, start_time,
1709                                    base::TimeDelta::FromSecondsD(time_offset),
1710                                    EffectivePlaybackRate());
1711 }
1712 
1713 // TODO(crbug.com/960944): Rename to SetPendingCommit. This method handles both
1714 // composited and non-composited animations. The use of 'compositor' in the name
1715 // is confusing.
SetCompositorPending(bool effect_changed)1716 void Animation::SetCompositorPending(bool effect_changed) {
1717   // Cannot play an animation with a null timeline.
1718   // TODO(crbug.com/827626) Revisit once timelines are mutable as there will be
1719   // work to do if the timeline is reset.
1720   if (!timeline_)
1721     return;
1722 
1723   // FIXME: KeyframeEffect could notify this directly?
1724   if (!HasActiveAnimationsOnCompositor()) {
1725     DestroyCompositorAnimation();
1726     compositor_state_.reset();
1727   }
1728   if (effect_changed && compositor_state_) {
1729     compositor_state_->effect_changed = true;
1730   }
1731   if (compositor_pending_ || is_paused_for_testing_) {
1732     return;
1733   }
1734   // In general, we need to update the compositor-side if anything has changed
1735   // on the blink version of the animation. There is also an edge case; if
1736   // neither the compositor nor blink side have a start time we still have to
1737   // sync them. This can happen if the blink side animation was started, the
1738   // compositor side hadn't started on its side yet, and then the blink side
1739   // start time was cleared (e.g. by setting current time).
1740   if (PendingInternal() || !compositor_state_ ||
1741       compositor_state_->effect_changed ||
1742       compositor_state_->playback_rate != EffectivePlaybackRate() ||
1743       compositor_state_->start_time != start_time_ ||
1744       !compositor_state_->start_time || !start_time_) {
1745     compositor_pending_ = true;
1746     document_->GetPendingAnimations().Add(this);
1747   }
1748 }
1749 
CancelAnimationOnCompositor()1750 void Animation::CancelAnimationOnCompositor() {
1751   if (HasActiveAnimationsOnCompositor()) {
1752     To<KeyframeEffect>(content_.Get())
1753         ->CancelAnimationOnCompositor(GetCompositorAnimation());
1754   }
1755 
1756   DestroyCompositorAnimation();
1757 }
1758 
RestartAnimationOnCompositor()1759 void Animation::RestartAnimationOnCompositor() {
1760   if (!HasActiveAnimationsOnCompositor())
1761     return;
1762   if (To<KeyframeEffect>(content_.Get())
1763           ->CancelAnimationOnCompositor(GetCompositorAnimation()))
1764     SetCompositorPending(true);
1765 }
1766 
CancelIncompatibleAnimationsOnCompositor()1767 void Animation::CancelIncompatibleAnimationsOnCompositor() {
1768   if (auto* keyframe_effect = DynamicTo<KeyframeEffect>(content_.Get()))
1769     keyframe_effect->CancelIncompatibleAnimationsOnCompositor();
1770 }
1771 
HasActiveAnimationsOnCompositor()1772 bool Animation::HasActiveAnimationsOnCompositor() {
1773   auto* keyframe_effect = DynamicTo<KeyframeEffect>(content_.Get());
1774   if (!keyframe_effect)
1775     return false;
1776 
1777   return keyframe_effect->HasActiveAnimationsOnCompositor();
1778 }
1779 
1780 // Update current time of the animation. Refer to step 1 in:
1781 // https://drafts.csswg.org/web-animations/#update-animations-and-send-events
Update(TimingUpdateReason reason)1782 bool Animation::Update(TimingUpdateReason reason) {
1783   // Due to the hierarchical nature of the timing model, updating the current
1784   // time of an animation also involves:
1785   //   * Running the update an animation’s finished state procedure.
1786   //   * Queueing animation events.
1787   if (!timeline_)
1788     return false;
1789 
1790   ClearOutdated();
1791   bool idle = CalculateAnimationPlayState() == kIdle;
1792   if (!idle)
1793     UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
1794 
1795   if (content_) {
1796     base::Optional<double> inherited_time = idle || !timeline_->CurrentTime()
1797                                                 ? base::nullopt
1798                                                 : CurrentTimeInternal();
1799 
1800     // Special case for end-exclusivity when playing backwards.
1801     if (inherited_time == 0 && EffectivePlaybackRate() < 0)
1802       inherited_time = -1;
1803 
1804     content_->UpdateInheritedTime(inherited_time, reason);
1805     // After updating the animation time if the animation is no longer current
1806     // blink will no longer composite the element (see
1807     // CompositingReasonFinder::RequiresCompositingFor*Animation). We cancel any
1808     // running compositor animation so that we don't try to animate the
1809     // non-existent element on the compositor.
1810     if (!content_->IsCurrent())
1811       CancelAnimationOnCompositor();
1812   }
1813 
1814   if (reason == kTimingUpdateForAnimationFrame) {
1815     if (idle || CalculateAnimationPlayState() == kFinished) {
1816       // TODO(crbug.com/1029348): Per spec, we should have a microtask
1817       // checkpoint right after the update cycle. Once this is fixed we should
1818       // no longer need to force a synchronous resolution here.
1819       AsyncFinishMicrotask();
1820       finished_ = true;
1821     }
1822   }
1823 
1824   DCHECK(!outdated_);
1825   NotifyProbe();
1826 
1827   return !finished_ || TimeToEffectChange();
1828 }
1829 
QueueFinishedEvent()1830 void Animation::QueueFinishedEvent() {
1831   const AtomicString& event_type = event_type_names::kFinish;
1832   if (GetExecutionContext() && HasEventListeners(event_type)) {
1833     base::Optional<double> event_current_time = CurrentTimeInternal();
1834     if (event_current_time)
1835       event_current_time = SecondsToMilliseconds(event_current_time.value());
1836     // TODO(crbug.com/916117): Handle NaN values for scroll-linked animations.
1837     pending_finished_event_ = MakeGarbageCollected<AnimationPlaybackEvent>(
1838         event_type, event_current_time, TimelineTime());
1839     pending_finished_event_->SetTarget(this);
1840     pending_finished_event_->SetCurrentTarget(this);
1841     document_->EnqueueAnimationFrameEvent(pending_finished_event_);
1842   }
1843 }
1844 
UpdateIfNecessary()1845 void Animation::UpdateIfNecessary() {
1846   // Update is a no-op if there is no timeline_, and will not reset the outdated
1847   // state in this case.
1848   if (!timeline_)
1849     return;
1850 
1851   if (Outdated())
1852     Update(kTimingUpdateOnDemand);
1853   DCHECK(!Outdated());
1854 }
1855 
EffectInvalidated()1856 void Animation::EffectInvalidated() {
1857   SetOutdated();
1858   UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
1859   // FIXME: Needs to consider groups when added.
1860   SetCompositorPending(true);
1861 }
1862 
IsEventDispatchAllowed() const1863 bool Animation::IsEventDispatchAllowed() const {
1864   return Paused() || start_time_;
1865 }
1866 
TimeToEffectChange()1867 base::Optional<AnimationTimeDelta> Animation::TimeToEffectChange() {
1868   DCHECK(!outdated_);
1869   if (!start_time_ || hold_time_ || !playback_rate_)
1870     return base::nullopt;
1871 
1872   if (!content_) {
1873     base::Optional<double> current_time = CurrentTimeInternal();
1874     DCHECK(current_time);
1875     return AnimationTimeDelta::FromSecondsD(-current_time.value() /
1876                                             playback_rate_);
1877   }
1878 
1879   double result =
1880       playback_rate_ > 0
1881           ? content_->TimeToForwardsEffectChange().InSecondsF() / playback_rate_
1882           : content_->TimeToReverseEffectChange().InSecondsF() /
1883                 -playback_rate_;
1884 
1885   return !HasActiveAnimationsOnCompositor() &&
1886                  content_->GetPhase() == Timing::kPhaseActive
1887              ? AnimationTimeDelta()
1888              : AnimationTimeDelta::FromSecondsD(result);
1889 }
1890 
cancel()1891 void Animation::cancel() {
1892   double current_time_before_cancel = CurrentTimeInternal().value_or(0);
1893   AnimationPlayState initial_play_state = CalculateAnimationPlayState();
1894   if (initial_play_state != kIdle) {
1895     ResetPendingTasks();
1896 
1897     if (finished_promise_) {
1898       if (finished_promise_->GetState() == AnimationPromise::kPending)
1899         RejectAndResetPromiseMaybeAsync(finished_promise_.Get());
1900       else
1901         finished_promise_->Reset();
1902     }
1903 
1904     const AtomicString& event_type = event_type_names::kCancel;
1905     if (GetExecutionContext() && HasEventListeners(event_type)) {
1906       base::Optional<double> event_current_time = base::nullopt;
1907       // TODO(crbug.com/916117): Handle NaN values for scroll-linked
1908       // animations.
1909       pending_cancelled_event_ = MakeGarbageCollected<AnimationPlaybackEvent>(
1910           event_type, event_current_time, TimelineTime());
1911       pending_cancelled_event_->SetTarget(this);
1912       pending_cancelled_event_->SetCurrentTarget(this);
1913       document_->EnqueueAnimationFrameEvent(pending_cancelled_event_);
1914     }
1915   } else {
1916     // Quietly reset without rejecting promises.
1917     pending_playback_rate_ = base::nullopt;
1918     pending_pause_ = pending_play_ = false;
1919   }
1920 
1921   hold_time_ = base::nullopt;
1922   start_time_ = base::nullopt;
1923 
1924   // Apply changes synchronously.
1925   SetCompositorPending(/*effect_changed=*/false);
1926   SetOutdated();
1927 
1928   // Force dispatch of canceled event.
1929   if (content_)
1930     content_->SetCancelTime(current_time_before_cancel);
1931   Update(kTimingUpdateOnDemand);
1932 
1933   // Notify of change to canceled state.
1934   NotifyProbe();
1935 }
1936 
CreateCompositorAnimation()1937 void Animation::CreateCompositorAnimation() {
1938   if (Platform::Current()->IsThreadedAnimationEnabled() &&
1939       !compositor_animation_) {
1940     compositor_animation_ = CompositorAnimationHolder::Create(this);
1941     AttachCompositorTimeline();
1942   }
1943 
1944   AttachCompositedLayers();
1945 }
1946 
DestroyCompositorAnimation()1947 void Animation::DestroyCompositorAnimation() {
1948   DetachCompositedLayers();
1949 
1950   if (compositor_animation_) {
1951     DetachCompositorTimeline();
1952     compositor_animation_->Detach();
1953     compositor_animation_ = nullptr;
1954   }
1955 }
1956 
AttachCompositorTimeline()1957 void Animation::AttachCompositorTimeline() {
1958   DCHECK(compositor_animation_);
1959 
1960   // Register ourselves on the compositor timeline. This will cause our cc-side
1961   // animation animation to be registered.
1962   CompositorAnimationTimeline* compositor_timeline =
1963       timeline_ ? timeline_->EnsureCompositorTimeline() : nullptr;
1964   if (!compositor_timeline)
1965     return;
1966 
1967   compositor_timeline->AnimationAttached(*this);
1968   if (compositor_timeline->GetAnimationTimeline()->IsScrollTimeline())
1969     document_->AttachCompositorTimeline(compositor_timeline);
1970 }
1971 
DetachCompositorTimeline()1972 void Animation::DetachCompositorTimeline() {
1973   DCHECK(compositor_animation_);
1974 
1975   CompositorAnimationTimeline* compositor_timeline =
1976       timeline_ ? timeline_->CompositorTimeline() : nullptr;
1977   if (!compositor_timeline)
1978     return;
1979 
1980   compositor_timeline->AnimationDestroyed(*this);
1981 
1982   if (compositor_timeline->GetAnimationTimeline()->IsScrollTimeline())
1983     document_->DetachCompositorTimeline(compositor_timeline);
1984 }
1985 
UpdateCompositorScrollTimeline()1986 void Animation::UpdateCompositorScrollTimeline() {
1987   if (!compositor_animation_ || !timeline_)
1988     return;
1989   Node* scroll_source = To<ScrollTimeline>(*timeline_).ResolvedScrollSource();
1990   LayoutBox* box = scroll_source ? scroll_source->GetLayoutBox() : nullptr;
1991 
1992   base::Optional<double> start_scroll_offset;
1993   base::Optional<double> end_scroll_offset;
1994   if (box) {
1995     double current_offset;
1996     double max_offset;
1997     To<ScrollTimeline>(*timeline_)
1998         .GetCurrentAndMaxOffset(box, current_offset, max_offset);
1999 
2000     double resolved_start_scroll_offset = 0;
2001     double resolved_end_scroll_offset = max_offset;
2002     To<ScrollTimeline>(*timeline_)
2003         .ResolveScrollStartAndEnd(box, max_offset, resolved_start_scroll_offset,
2004                                   resolved_end_scroll_offset);
2005     start_scroll_offset = resolved_start_scroll_offset;
2006     end_scroll_offset = resolved_end_scroll_offset;
2007   }
2008   compositor_animation_->GetAnimation()->UpdateScrollTimeline(
2009       scroll_timeline_util::GetCompositorScrollElementId(scroll_source),
2010       start_scroll_offset, end_scroll_offset);
2011 }
2012 
AttachCompositedLayers()2013 void Animation::AttachCompositedLayers() {
2014   if (!compositor_animation_)
2015     return;
2016 
2017   DCHECK(content_);
2018   DCHECK(IsA<KeyframeEffect>(*content_));
2019 
2020   To<KeyframeEffect>(content_.Get())->AttachCompositedLayers();
2021 }
2022 
DetachCompositedLayers()2023 void Animation::DetachCompositedLayers() {
2024   if (compositor_animation_ &&
2025       compositor_animation_->GetAnimation()->IsElementAttached())
2026     compositor_animation_->GetAnimation()->DetachElement();
2027 }
2028 
NotifyAnimationStarted(double monotonic_time,int group)2029 void Animation::NotifyAnimationStarted(double monotonic_time, int group) {
2030   document_->GetPendingAnimations().NotifyCompositorAnimationStarted(
2031       monotonic_time, group);
2032 }
2033 
AddedEventListener(const AtomicString & event_type,RegisteredEventListener & registered_listener)2034 void Animation::AddedEventListener(
2035     const AtomicString& event_type,
2036     RegisteredEventListener& registered_listener) {
2037   EventTargetWithInlineData::AddedEventListener(event_type,
2038                                                 registered_listener);
2039   if (event_type == event_type_names::kFinish)
2040     UseCounter::Count(GetExecutionContext(), WebFeature::kAnimationFinishEvent);
2041 }
2042 
PauseForTesting(double pause_time)2043 void Animation::PauseForTesting(double pause_time) {
2044   // Do not restart a canceled animation.
2045   if (CalculateAnimationPlayState() == kIdle)
2046     return;
2047 
2048   // Pause a running animation, or update the hold time of a previously paused
2049   // animation.
2050   SetCurrentTimeInternal(pause_time);
2051   if (HasActiveAnimationsOnCompositor()) {
2052     base::Optional<double> current_time = CurrentTimeInternal();
2053     DCHECK(current_time);
2054     To<KeyframeEffect>(content_.Get())
2055         ->PauseAnimationForTestingOnCompositor(
2056             base::TimeDelta::FromSecondsD(current_time.value()));
2057   }
2058 
2059   // Do not wait for animation ready to lock in the hold time. Otherwise,
2060   // the pause won't take effect until the next frame and the hold time will
2061   // potentially drift.
2062   is_paused_for_testing_ = true;
2063   pending_pause_ = false;
2064   pending_play_ = false;
2065   hold_time_ = pause_time;
2066   start_time_ = base::nullopt;
2067 }
2068 
SetEffectSuppressed(bool suppressed)2069 void Animation::SetEffectSuppressed(bool suppressed) {
2070   effect_suppressed_ = suppressed;
2071   if (suppressed)
2072     CancelAnimationOnCompositor();
2073 }
2074 
DisableCompositedAnimationForTesting()2075 void Animation::DisableCompositedAnimationForTesting() {
2076   is_composited_animation_disabled_for_testing_ = true;
2077   CancelAnimationOnCompositor();
2078 }
2079 
InvalidateKeyframeEffect(const TreeScope & tree_scope)2080 void Animation::InvalidateKeyframeEffect(const TreeScope& tree_scope) {
2081   auto* keyframe_effect = DynamicTo<KeyframeEffect>(content_.Get());
2082   if (!keyframe_effect)
2083     return;
2084 
2085   Element* target = keyframe_effect->EffectTarget();
2086 
2087   // TODO(alancutter): Remove dependency of this function on CSSAnimations.
2088   // This function makes the incorrect assumption that the animation uses
2089   // @keyframes for its effect model when it may instead be using JS provided
2090   // keyframes.
2091   if (target &&
2092       CSSAnimations::IsAffectedByKeyframesFromScope(*target, tree_scope)) {
2093     target->SetNeedsStyleRecalc(kLocalStyleChange,
2094                                 StyleChangeReasonForTracing::Create(
2095                                     style_change_reason::kStyleSheetChange));
2096   }
2097 }
2098 
ResolvePromiseMaybeAsync(AnimationPromise * promise)2099 void Animation::ResolvePromiseMaybeAsync(AnimationPromise* promise) {
2100   if (ScriptForbiddenScope::IsScriptForbidden()) {
2101     GetExecutionContext()
2102         ->GetTaskRunner(TaskType::kDOMManipulation)
2103         ->PostTask(FROM_HERE,
2104                    WTF::Bind(&AnimationPromise::Resolve<Animation*>,
2105                              WrapPersistent(promise), WrapPersistent(this)));
2106   } else {
2107     promise->Resolve(this);
2108   }
2109 }
2110 
RejectAndResetPromise(AnimationPromise * promise)2111 void Animation::RejectAndResetPromise(AnimationPromise* promise) {
2112   promise->Reject(
2113       MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError));
2114   promise->Reset();
2115 }
2116 
RejectAndResetPromiseMaybeAsync(AnimationPromise * promise)2117 void Animation::RejectAndResetPromiseMaybeAsync(AnimationPromise* promise) {
2118   if (ScriptForbiddenScope::IsScriptForbidden()) {
2119     GetExecutionContext()
2120         ->GetTaskRunner(TaskType::kDOMManipulation)
2121         ->PostTask(FROM_HERE,
2122                    WTF::Bind(&Animation::RejectAndResetPromise,
2123                              WrapPersistent(this), WrapPersistent(promise)));
2124   } else {
2125     RejectAndResetPromise(promise);
2126   }
2127 }
2128 
NotifyProbe()2129 void Animation::NotifyProbe() {
2130   AnimationPlayState old_play_state = reported_play_state_;
2131   AnimationPlayState new_play_state =
2132       PendingInternal() ? kPending : CalculateAnimationPlayState();
2133 
2134   if (old_play_state != new_play_state) {
2135     if (!PendingInternal()) {
2136       probe::AnimationPlayStateChanged(document_, this, old_play_state,
2137                                        new_play_state);
2138     }
2139     reported_play_state_ = new_play_state;
2140 
2141     bool was_active = old_play_state == kPending || old_play_state == kRunning;
2142     bool is_active = new_play_state == kPending || new_play_state == kRunning;
2143 
2144     if (!was_active && is_active) {
2145       TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
2146           "blink.animations,devtools.timeline,benchmark,rail", "Animation",
2147           this, "data", inspector_animation_event::Data(*this));
2148     } else if (was_active && !is_active) {
2149       TRACE_EVENT_NESTABLE_ASYNC_END1(
2150           "blink.animations,devtools.timeline,benchmark,rail", "Animation",
2151           this, "endData", inspector_animation_state_event::Data(*this));
2152     } else {
2153       TRACE_EVENT_NESTABLE_ASYNC_INSTANT1(
2154           "blink.animations,devtools.timeline,benchmark,rail", "Animation",
2155           this, "data", inspector_animation_state_event::Data(*this));
2156     }
2157   }
2158 }
2159 
2160 // -------------------------------------
2161 // Replacement of animations
2162 // -------------------------------------
2163 
2164 // https://drafts.csswg.org/web-animations-1/#removing-replaced-animations
IsReplaceable()2165 bool Animation::IsReplaceable() {
2166   // An animation is replaceable if all of the following conditions are true:
2167 
2168   // 1. The existence of the animation is not prescribed by markup. That is, it
2169   //    is not a CSS animation with an owning element, nor a CSS transition with
2170   //    an owning element.
2171   if (IsCSSAnimation() || IsCSSTransition()) {
2172     // TODO(crbug.com/981905): Add OwningElement method to Animation and
2173     // override in CssAnimations and CssTransitions. Only bail here if the
2174     // animation has an owning element.
2175     return false;
2176   }
2177 
2178   // 2. The animation's play state is finished.
2179   if (CalculateAnimationPlayState() != kFinished)
2180     return false;
2181 
2182   // 3. The animation's replace state is not removed.
2183   if (replace_state_ == kRemoved)
2184     return false;
2185 
2186   // 4. The animation is associated with a monotonically increasing timeline.
2187   if (!timeline_ || timeline_->IsScrollTimeline())
2188     return false;
2189 
2190   // 5. The animation has an associated effect.
2191   if (!content_ || !content_->IsKeyframeEffect())
2192     return false;
2193 
2194   // 6. The animation's associated effect is in effect.
2195   if (!content_->IsInEffect())
2196     return false;
2197 
2198   // 7. The animation's associated effect has an effect target.
2199   Element* target = To<KeyframeEffect>(content_.Get())->target();
2200   if (!target)
2201     return false;
2202 
2203   return true;
2204 }
2205 
2206 // https://drafts.csswg.org/web-animations-1/#removing-replaced-animations
RemoveReplacedAnimation()2207 void Animation::RemoveReplacedAnimation() {
2208   DCHECK(IsReplaceable());
2209 
2210   // To remove a replaced animation, perform the following steps:
2211   // 1. Set animation’s replace state to removed.
2212   // 2. Create an AnimationPlaybackEvent, removeEvent.
2213   // 3. Set removeEvent’s type attribute to remove.
2214   // 4. Set removeEvent’s currentTime attribute to the current time of
2215   //    animation.
2216   // 5. Set removeEvent’s timelineTime attribute to the current time of the
2217   //    timeline with which animation is associated.
2218   //
2219   // If animation has a document for timing, then append removeEvent to its
2220   // document for timing's pending animation event queue along with its target,
2221   // animation. For the scheduled event time, use the result of applying the
2222   // procedure to convert timeline time to origin-relative time to the current
2223   // time of the timeline with which animation is associated.
2224   replace_state_ = kRemoved;
2225   const AtomicString& event_type = event_type_names::kRemove;
2226   if (GetExecutionContext() && HasEventListeners(event_type)) {
2227     base::Optional<double> event_current_time = CurrentTimeInternal();
2228     if (event_current_time)
2229       event_current_time = SecondsToMilliseconds(event_current_time.value());
2230     pending_remove_event_ = MakeGarbageCollected<AnimationPlaybackEvent>(
2231         event_type, event_current_time, TimelineTime());
2232     pending_remove_event_->SetTarget(this);
2233     pending_remove_event_->SetCurrentTarget(this);
2234     document_->EnqueueAnimationFrameEvent(pending_remove_event_);
2235   }
2236 
2237   // Force timing update to clear the effect.
2238   if (content_)
2239     content_->Invalidate();
2240   Update(kTimingUpdateOnDemand);
2241 }
2242 
persist()2243 void Animation::persist() {
2244   if (replace_state_ == kPersisted)
2245     return;
2246 
2247   replace_state_ = kPersisted;
2248 
2249   // Force timing update to reapply the effect.
2250   if (content_)
2251     content_->Invalidate();
2252   Update(kTimingUpdateOnDemand);
2253 }
2254 
replaceState()2255 String Animation::replaceState() {
2256   switch (replace_state_) {
2257     case kActive:
2258       return "active";
2259 
2260     case kRemoved:
2261       return "removed";
2262 
2263     case kPersisted:
2264       return "persisted";
2265 
2266     default:
2267       NOTREACHED();
2268       return "";
2269   }
2270 }
2271 
2272 // https://drafts.csswg.org/web-animations-1/#dom-animation-commitstyles
commitStyles(ExceptionState & exception_state)2273 void Animation::commitStyles(ExceptionState& exception_state) {
2274   Element* target = content_ && content_->IsKeyframeEffect()
2275                         ? To<KeyframeEffect>(effect())->target()
2276                         : nullptr;
2277 
2278   // 1. If target is not an element capable of having a style attribute
2279   //    (for example, it is a pseudo-element or is an element in a document
2280   //    format for which style attributes are not defined) throw a
2281   //    "NoModificationAllowedError" DOMException and abort these steps.
2282   if (!target || !target->IsStyledElement() ||
2283       !To<KeyframeEffect>(effect())->pseudoElement().IsEmpty()) {
2284     exception_state.ThrowDOMException(
2285         DOMExceptionCode::kNoModificationAllowedError,
2286         "Animation not associated with a styled target element");
2287     return;
2288   }
2289   // 2. If, after applying any pending style changes, target is not being
2290   //    rendered, throw an "InvalidStateError" DOMException and abort these
2291   //    steps.
2292   target->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kJavaScript);
2293   if (!target->GetLayoutObject()) {
2294     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
2295                                       "Target element is not rendered.");
2296     return;
2297   }
2298 
2299   // 3. Let inline style be the result of getting the CSS declaration block
2300   //    corresponding to target’s style attribute. If target does not have a
2301   //    style attribute, let inline style be a new empty CSS declaration block
2302   //    with the readonly flag unset and owner node set to target.
2303   CSSStyleDeclaration* inline_style = target->style();
2304 
2305   // 4. Let targeted properties be the set of physical longhand properties
2306   //    that are a target property for at least one animation effect
2307   //    associated with animation whose effect target is target.
2308   PropertyHandleSet animation_properties =
2309       To<KeyframeEffect>(effect())->Model()->Properties();
2310 
2311   // 5. For each property, property, in targeted properties:
2312   //   5.1 Let partialEffectStack be a copy of the effect stack for property
2313   //       on target.
2314   //   5.2 If animation’s replace state is removed, add all animation effects
2315   //       associated with animation whose effect target is target and which
2316   //       include property as a target property to partialEffectStack.
2317   //   5.3 Remove from partialEffectStack any animation effects whose
2318   //       associated animation has a higher composite order than animation.
2319   //   5.4 Let effect value be the result of calculating the result of
2320   //       partialEffectStack for property using target’s computed style
2321   //       (see § 5.4.3 Calculating the result of an effect stack).
2322   //   5.5 Set a CSS declaration property for effect value in inline style.
2323   // 6. Update style attribute for inline style.
2324   ActiveInterpolationsMap interpolations_map =
2325       To<KeyframeEffect>(effect())->InterpolationsForCommitStyles();
2326   StyleResolver& resolver = target->GetDocument().EnsureStyleResolver();
2327   scoped_refptr<ComputedStyle> style =
2328       resolver.StyleForInterpolations(*target, interpolations_map);
2329 
2330   for (const auto& property : animation_properties) {
2331     if (!property.IsCSSProperty())
2332       continue;
2333 
2334     CSSPropertyRef ref(property.GetCSSPropertyName(), target->GetDocument());
2335     const CSSValue* value = ref.GetProperty().CSSValueFromComputedStyle(
2336         *style, target->GetLayoutObject(), false);
2337     inline_style->setProperty(target->GetExecutionContext(),
2338                               property.GetCSSPropertyName().ToAtomicString(),
2339                               value->CssText(), "", ASSERT_NO_EXCEPTION);
2340   }
2341 }
2342 
Trace(Visitor * visitor)2343 void Animation::Trace(Visitor* visitor) {
2344   visitor->Trace(content_);
2345   visitor->Trace(document_);
2346   visitor->Trace(timeline_);
2347   visitor->Trace(pending_finished_event_);
2348   visitor->Trace(pending_cancelled_event_);
2349   visitor->Trace(pending_remove_event_);
2350   visitor->Trace(finished_promise_);
2351   visitor->Trace(ready_promise_);
2352   visitor->Trace(compositor_animation_);
2353   EventTargetWithInlineData::Trace(visitor);
2354   ExecutionContextLifecycleObserver::Trace(visitor);
2355 }
2356 
2357 Animation::CompositorAnimationHolder*
Create(Animation * animation)2358 Animation::CompositorAnimationHolder::Create(Animation* animation) {
2359   return MakeGarbageCollected<CompositorAnimationHolder>(animation);
2360 }
2361 
CompositorAnimationHolder(Animation * animation)2362 Animation::CompositorAnimationHolder::CompositorAnimationHolder(
2363     Animation* animation)
2364     : animation_(animation) {
2365   compositor_animation_ = CompositorAnimation::Create();
2366   compositor_animation_->SetAnimationDelegate(animation_);
2367 }
2368 
Dispose()2369 void Animation::CompositorAnimationHolder::Dispose() {
2370   if (!animation_)
2371     return;
2372   animation_->Dispose();
2373   DCHECK(!animation_);
2374   DCHECK(!compositor_animation_);
2375 }
2376 
Detach()2377 void Animation::CompositorAnimationHolder::Detach() {
2378   DCHECK(compositor_animation_);
2379   compositor_animation_->SetAnimationDelegate(nullptr);
2380   animation_ = nullptr;
2381   compositor_animation_.reset();
2382 }
2383 }  // namespace blink
2384