[Web Animations] Use a keyframe effect stack to resolve animations on an element
[WebKit-https.git] / Source / WebCore / animation / DocumentTimeline.cpp
1 /*
2  * Copyright (C) 2017 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "DocumentTimeline.h"
28
29 #include "AnimationPlaybackEvent.h"
30 #include "CSSAnimation.h"
31 #include "CSSTransition.h"
32 #include "DOMWindow.h"
33 #include "DeclarativeAnimation.h"
34 #include "Document.h"
35 #include "EventNames.h"
36 #include "GraphicsLayer.h"
37 #include "KeyframeEffect.h"
38 #include "Microtasks.h"
39 #include "Node.h"
40 #include "Page.h"
41 #include "PseudoElement.h"
42 #include "RenderElement.h"
43 #include "RenderLayer.h"
44 #include "RenderLayerBacking.h"
45 #include <JavaScriptCore/VM.h>
46
47 static const Seconds defaultAnimationInterval { 15_ms };
48 static const Seconds throttledAnimationInterval { 30_ms };
49
50 namespace WebCore {
51
52 Ref<DocumentTimeline> DocumentTimeline::create(Document& document)
53 {
54     return adoptRef(*new DocumentTimeline(document, 0_s));
55 }
56
57 Ref<DocumentTimeline> DocumentTimeline::create(Document& document, DocumentTimelineOptions&& options)
58 {
59     return adoptRef(*new DocumentTimeline(document, Seconds::fromMilliseconds(options.originTime)));
60 }
61
62 DocumentTimeline::DocumentTimeline(Document& document, Seconds originTime)
63     : AnimationTimeline()
64     , m_tickScheduleTimer(*this, &DocumentTimeline::scheduleAnimationResolution)
65     , m_document(&document)
66     , m_originTime(originTime)
67 {
68     if (m_document && m_document->page() && !m_document->page()->isVisible())
69         suspendAnimations();
70 }
71
72 DocumentTimeline::~DocumentTimeline() = default;
73
74 void DocumentTimeline::detachFromDocument()
75 {
76     m_currentTimeClearingTaskQueue.close();
77     m_elementsWithRunningAcceleratedAnimations.clear();
78
79     auto& animationsToRemove = m_animations;
80     while (!animationsToRemove.isEmpty())
81         animationsToRemove.first()->remove();
82
83     unscheduleAnimationResolution();
84     m_document = nullptr;
85 }
86
87 static inline bool compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(Element* lhsOwningElement, Element* rhsOwningElement)
88 {
89     // With regard to pseudo-elements, the sort order is as follows:
90     //     - element
91     //     - ::before
92     //     - ::after
93     //     - element children
94     
95     // We could be comparing two pseudo-elements that are hosted on the same element.
96     if (is<PseudoElement>(lhsOwningElement) && is<PseudoElement>(rhsOwningElement)) {
97         auto* lhsPseudoElement = downcast<PseudoElement>(lhsOwningElement);
98         auto* rhsPseudoElement = downcast<PseudoElement>(rhsOwningElement);
99         if (lhsPseudoElement->hostElement() == rhsPseudoElement->hostElement())
100             return lhsPseudoElement->isBeforePseudoElement();
101     }
102
103     // Or comparing a pseudo-element that is compared to another non-pseudo element, in which case
104     // we want to see if it's hosted on that other element, and if not use its host element to compare.
105     if (is<PseudoElement>(lhsOwningElement)) {
106         auto* lhsHostElement = downcast<PseudoElement>(lhsOwningElement)->hostElement();
107         if (rhsOwningElement == lhsHostElement)
108             return false;
109         lhsOwningElement = lhsHostElement;
110     }
111
112     if (is<PseudoElement>(rhsOwningElement)) {
113         auto* rhsHostElement = downcast<PseudoElement>(rhsOwningElement)->hostElement();
114         if (lhsOwningElement == rhsHostElement)
115             return true;
116         rhsOwningElement = rhsHostElement;
117     }
118
119     return lhsOwningElement->compareDocumentPosition(*rhsOwningElement) & Node::DOCUMENT_POSITION_FOLLOWING;
120 }
121
122 Vector<RefPtr<WebAnimation>> DocumentTimeline::getAnimations() const
123 {
124     ASSERT(m_document);
125
126     Vector<RefPtr<WebAnimation>> cssTransitions;
127     Vector<RefPtr<WebAnimation>> cssAnimations;
128     Vector<RefPtr<WebAnimation>> webAnimations;
129
130     // First, let's get all qualifying animations in their right group.
131     for (const auto& animation : m_allAnimations) {
132         if (!animation || !animation->isRelevant() || animation->timeline() != this || !is<KeyframeEffect>(animation->effect()))
133             continue;
134
135         auto* target = downcast<KeyframeEffect>(animation->effect())->target();
136         if (!target || !target->isDescendantOf(*m_document))
137             continue;
138
139         if (is<CSSTransition>(animation.get()) && downcast<CSSTransition>(animation.get())->owningElement())
140             cssTransitions.append(animation.get());
141         else if (is<CSSAnimation>(animation.get()) && downcast<CSSAnimation>(animation.get())->owningElement())
142             cssAnimations.append(animation.get());
143         else
144             webAnimations.append(animation.get());
145     }
146
147     // Now sort CSS Transitions by their composite order.
148     std::sort(cssTransitions.begin(), cssTransitions.end(), [](auto& lhs, auto& rhs) {
149         // https://drafts.csswg.org/css-transitions-2/#animation-composite-order
150         auto* lhsTransition = downcast<CSSTransition>(lhs.get());
151         auto* rhsTransition = downcast<CSSTransition>(rhs.get());
152
153         auto* lhsOwningElement = lhsTransition->owningElement();
154         auto* rhsOwningElement = rhsTransition->owningElement();
155
156         // If the owning element of A and B differs, sort A and B by tree order of their corresponding owning elements.
157         if (lhsOwningElement != rhsOwningElement)
158             return compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(lhsOwningElement, rhsOwningElement);
159
160         // Otherwise, if A and B have different transition generation values, sort by their corresponding transition generation in ascending order.
161         if (lhsTransition->generationTime() != rhsTransition->generationTime())
162             return lhsTransition->generationTime() < rhsTransition->generationTime();
163
164         // Otherwise, sort A and B in ascending order by the Unicode codepoints that make up the expanded transition property name of each transition
165         // (i.e. without attempting case conversion and such that ‘-moz-column-width’ sorts before ‘column-width’).
166         return lhsTransition->transitionProperty().utf8() < rhsTransition->transitionProperty().utf8();
167     });
168
169     // Now sort CSS Animations by their composite order.
170     std::sort(cssAnimations.begin(), cssAnimations.end(), [](auto& lhs, auto& rhs) {
171         // https://drafts.csswg.org/css-animations-2/#animation-composite-order
172         auto* lhsOwningElement = downcast<CSSAnimation>(lhs.get())->owningElement();
173         auto* rhsOwningElement = downcast<CSSAnimation>(rhs.get())->owningElement();
174
175         // If the owning element of A and B differs, sort A and B by tree order of their corresponding owning elements.
176         if (lhsOwningElement != rhsOwningElement)
177             return compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(lhsOwningElement, rhsOwningElement);
178
179         // Otherwise, sort A and B based on their position in the computed value of the animation-name property of the (common) owning element.
180         // In our case, this matches the time at which the animations were created and thus their relative position in m_allAnimations.
181         return false;
182     });
183
184     // Finally, we can concatenate the sorted CSS Transitions, CSS Animations and Web Animations in their relative composite order.
185     Vector<RefPtr<WebAnimation>> animations;
186     animations.appendRange(cssTransitions.begin(), cssTransitions.end());
187     animations.appendRange(cssAnimations.begin(), cssAnimations.end());
188     animations.appendRange(webAnimations.begin(), webAnimations.end());
189     return animations;
190 }
191
192 void DocumentTimeline::updateThrottlingState()
193 {
194     scheduleAnimationResolution();
195 }
196
197 Seconds DocumentTimeline::animationInterval() const
198 {
199     if (!m_document || !m_document->page())
200         return Seconds::infinity();
201     return m_document->page()->isLowPowerModeEnabled() ? throttledAnimationInterval : defaultAnimationInterval;
202 }
203
204 void DocumentTimeline::suspendAnimations()
205 {
206     if (animationsAreSuspended())
207         return;
208
209     if (!m_cachedCurrentTime)
210         m_cachedCurrentTime = Seconds(liveCurrentTime());
211
212     for (const auto& animation : m_animations)
213         animation->setSuspended(true);
214
215     m_isSuspended = true;
216
217     applyPendingAcceleratedAnimations();
218
219     unscheduleAnimationResolution();
220 }
221
222 void DocumentTimeline::resumeAnimations()
223 {
224     if (!animationsAreSuspended())
225         return;
226
227     m_cachedCurrentTime = WTF::nullopt;
228
229     m_isSuspended = false;
230
231     for (const auto& animation : m_animations)
232         animation->setSuspended(false);
233
234     scheduleAnimationResolution();
235 }
236
237 bool DocumentTimeline::animationsAreSuspended()
238 {
239     return m_isSuspended;
240 }
241
242 unsigned DocumentTimeline::numberOfActiveAnimationsForTesting() const
243 {
244     unsigned count = 0;
245     for (const auto& animation : m_animations) {
246         if (!animation->isSuspended())
247             ++count;
248     }
249     return count;
250 }
251
252 DOMHighResTimeStamp DocumentTimeline::liveCurrentTime() const
253 {
254     return m_document->domWindow()->nowTimestamp();
255 }
256
257 Optional<Seconds> DocumentTimeline::currentTime()
258 {
259     if (!m_document || !m_document->domWindow())
260         return AnimationTimeline::currentTime();
261
262     auto& mainDocumentTimeline = m_document->timeline();
263     if (&mainDocumentTimeline != this) {
264         if (auto mainDocumentTimelineCurrentTime = mainDocumentTimeline.currentTime())
265             return *mainDocumentTimelineCurrentTime - m_originTime;
266         return WTF::nullopt;
267     }
268
269     if (!m_cachedCurrentTime)
270         cacheCurrentTime(liveCurrentTime());
271     
272     return m_cachedCurrentTime.value() - m_originTime;
273 }
274
275 void DocumentTimeline::cacheCurrentTime(DOMHighResTimeStamp newCurrentTime)
276 {
277     m_cachedCurrentTime = Seconds(newCurrentTime);
278     // We want to be sure to keep this time cached until we've both finished running JS and finished updating
279     // animations, so we schedule the invalidation task and register a whenIdle callback on the VM, which will
280     // fire syncronously if no JS is running.
281     m_waitingOnVMIdle = true;
282     if (!m_currentTimeClearingTaskQueue.hasPendingTasks())
283         m_currentTimeClearingTaskQueue.enqueueTask(std::bind(&DocumentTimeline::maybeClearCachedCurrentTime, this));
284     m_document->vm().whenIdle([this, protectedThis = makeRefPtr(this)]() {
285         m_waitingOnVMIdle = false;
286         maybeClearCachedCurrentTime();
287     });
288 }
289
290 void DocumentTimeline::maybeClearCachedCurrentTime()
291 {
292     // We want to make sure we only clear the cached current time if we're not currently running
293     // JS or waiting on all current animation updating code to have completed. This is so that
294     // we're guaranteed to have a consistent current time reported for all work happening in a given
295     // JS frame or throughout updating animations in WebCore.
296     if (!m_isSuspended && !m_waitingOnVMIdle && !m_currentTimeClearingTaskQueue.hasPendingTasks())
297         m_cachedCurrentTime = WTF::nullopt;
298 }
299
300 void DocumentTimeline::animationTimingDidChange(WebAnimation& animation)
301 {
302     AnimationTimeline::animationTimingDidChange(animation);
303     scheduleAnimationResolution();
304 }
305
306 void DocumentTimeline::removeAnimation(WebAnimation& animation)
307 {
308     AnimationTimeline::removeAnimation(animation);
309
310     if (m_animations.isEmpty())
311         unscheduleAnimationResolution();
312 }
313
314 void DocumentTimeline::scheduleAnimationResolution()
315 {
316     if (m_isSuspended || m_animations.isEmpty() || m_animationResolutionScheduled)
317         return;
318
319     if (!m_document || !m_document->page())
320         return;
321     
322     m_document->page()->renderingUpdateScheduler().scheduleTimedRenderingUpdate();
323     m_animationResolutionScheduled = true;
324 }
325
326 void DocumentTimeline::unscheduleAnimationResolution()
327 {
328     m_tickScheduleTimer.stop();
329     m_animationResolutionScheduled = false;
330 }
331
332 void DocumentTimeline::updateAnimationsAndSendEvents(DOMHighResTimeStamp timestamp)
333 {
334     // We need to freeze the current time even if no animation is running.
335     // document.timeline.currentTime may be called from a rAF callback and
336     // it has to match the rAF timestamp.
337     if (!m_isSuspended)
338         cacheCurrentTime(timestamp);
339
340     if (m_isSuspended || m_animations.isEmpty() || !m_animationResolutionScheduled)
341         return;
342
343     internalUpdateAnimationsAndSendEvents();
344     applyPendingAcceleratedAnimations();
345
346     m_animationResolutionScheduled = false;
347     scheduleNextTick();
348 }
349
350 void DocumentTimeline::internalUpdateAnimationsAndSendEvents()
351 {
352     m_numberOfAnimationTimelineInvalidationsForTesting++;
353
354     // https://drafts.csswg.org/web-animations/#update-animations-and-send-events
355
356     // 1. Update the current time of all timelines associated with doc passing now as the timestamp.
357
358     Vector<RefPtr<WebAnimation>> animationsToRemove;
359     Vector<RefPtr<CSSTransition>> completedTransitions;
360
361     for (auto& animation : m_animations) {
362         if (animation->timeline() != this) {
363             ASSERT(!animation->timeline());
364             animationsToRemove.append(animation);
365             continue;
366         }
367
368         // This will notify the animation that timing has changed and will call automatically
369         // schedule invalidation if required for this animation.
370         animation->tick();
371
372         if (!animation->isRelevant() && !animation->needsTick())
373             animationsToRemove.append(animation);
374
375         if (!animation->needsTick() && is<CSSTransition>(animation) && animation->playState() == WebAnimation::PlayState::Finished) {
376             auto* transition = downcast<CSSTransition>(animation.get());
377             if (transition->owningElement())
378                 completedTransitions.append(transition);
379         }
380     }
381
382     // 2. Remove replaced animations for doc.
383     removeReplacedAnimations();
384
385     // 3. Perform a microtask checkpoint.
386     MicrotaskQueue::mainThreadQueue().performMicrotaskCheckpoint();
387
388     // 4. Let events to dispatch be a copy of doc's pending animation event queue.
389     // 5. Clear doc's pending animation event queue.
390     auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
391
392     // 6. Perform a stable sort of the animation events in events to dispatch as follows.
393     std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), [] (const Ref<AnimationPlaybackEvent>& lhs, const Ref<AnimationPlaybackEvent>& rhs) {
394         // 1. Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort before events scheduled to occur later
395         // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
396         // 2. Within events with equal scheduled event times, sort by their composite order. FIXME: We don't do this.
397         if (lhs->timelineTime() && !rhs->timelineTime())
398             return false;
399         if (!lhs->timelineTime() && rhs->timelineTime())
400             return true;
401         if (!lhs->timelineTime() && !rhs->timelineTime())
402             return true;
403         return lhs->timelineTime().value() < rhs->timelineTime().value();
404     });
405
406     // 7. Dispatch each of the events in events to dispatch at their corresponding target using the order established in the previous step.
407     for (auto& pendingEvent : pendingAnimationEvents)
408         pendingEvent->target()->dispatchEvent(pendingEvent);
409
410     // This will cancel any scheduled invalidation if we end up removing all animations.
411     for (auto& animation : animationsToRemove) {
412         // An animation that was initially marked as irrelevant may have changed while we were sending events, so we run the same
413         // check that we ran to add it to animationsToRemove in the first place.
414         if (!animation->isRelevant() && !animation->needsTick())
415             removeAnimation(*animation);
416     }
417
418     // Now that animations that needed removal have been removed, let's update the list of completed transitions.
419     // This needs to happen after dealing with the list of animations to remove as the animation may have been
420     // removed from the list of completed transitions otherwise.
421     for (auto& completedTransition : completedTransitions)
422         transitionDidComplete(completedTransition);
423 }
424
425 bool DocumentTimeline::animationCanBeRemoved(WebAnimation& animation)
426 {
427     // https://drafts.csswg.org/web-animations/#removing-replaced-animations
428
429     ASSERT(m_document);
430
431     // - is replaceable, and
432     if (!animation.isReplaceable())
433         return false;
434
435     // - has a replace state of active, and
436     if (animation.replaceState() != WebAnimation::ReplaceState::Active)
437         return false;
438
439     // - has an associated animation effect whose target element is a descendant of doc, and
440     auto* effect = animation.effect();
441     if (!is<KeyframeEffect>(effect))
442         return false;
443
444     auto* keyframeEffect = downcast<KeyframeEffect>(effect);
445     auto* target = keyframeEffect->target();
446     if (!target || !target->isDescendantOf(*m_document))
447         return false;
448
449     HashSet<CSSPropertyID> propertiesToMatch = keyframeEffect->animatedProperties();
450     auto animations = animationsForElement(*target, AnimationTimeline::Ordering::Sorted);
451     for (auto& animationWithHigherCompositeOrder : WTF::makeReversedRange(animations)) {
452         if (&animation == animationWithHigherCompositeOrder)
453             break;
454
455         if (animationWithHigherCompositeOrder && animationWithHigherCompositeOrder->isReplaceable()) {
456             auto* effectWithHigherCompositeOrder = animationWithHigherCompositeOrder->effect();
457             if (is<KeyframeEffect>(effectWithHigherCompositeOrder)) {
458                 auto* keyframeEffectWithHigherCompositeOrder = downcast<KeyframeEffect>(effectWithHigherCompositeOrder);
459                 for (auto cssPropertyId : keyframeEffectWithHigherCompositeOrder->animatedProperties()) {
460                     if (propertiesToMatch.remove(cssPropertyId) && propertiesToMatch.isEmpty())
461                         break;
462                 }
463             }
464         }
465     }
466
467     return propertiesToMatch.isEmpty();
468 }
469
470 void DocumentTimeline::removeReplacedAnimations()
471 {
472     // https://drafts.csswg.org/web-animations/#removing-replaced-animations
473
474     Vector<RefPtr<WebAnimation>> animationsToRemove;
475
476     // When asked to remove replaced animations for a Document, doc, then for every animation, animation
477     for (auto& animation : m_allAnimations) {
478         if (animation && animationCanBeRemoved(*animation)) {
479             // perform the following steps:
480             // 1. Set animation's replace state to removed.
481             animation->setReplaceState(WebAnimation::ReplaceState::Removed);
482             // 2. Create an AnimationPlaybackEvent, removeEvent.
483             // 3. Set removeEvent's type attribute to remove.
484             // 4. Set removeEvent's currentTime attribute to the current time of animation.
485             // 5. Set removeEvent's timelineTime attribute to the current time of the timeline with which animation is associated.
486             // 6. If animation has a document for timing, then append removeEvent to its document for timing's pending animation
487             //    event queue along with its target, animation. For the scheduled event time, use the result of applying the procedure
488             //    to convert timeline time to origin-relative time to the current time of the timeline with which animation is associated.
489             //    Otherwise, queue a task to dispatch removeEvent at animation. The task source for this task is the DOM manipulation task source.
490             animation->enqueueAnimationPlaybackEvent(eventNames().removeEvent, animation->currentTime(), currentTime());
491
492             animationsToRemove.append(animation.get());
493         }
494     }
495
496     for (auto& animation : animationsToRemove) {
497         if (auto* timeline = animation->timeline())
498             timeline->removeAnimation(*animation);
499     }
500 }
501
502 void DocumentTimeline::transitionDidComplete(RefPtr<CSSTransition> transition)
503 {
504     ASSERT(transition);
505     removeAnimation(*transition);
506     if (is<KeyframeEffect>(transition->effect())) {
507         if (auto* target = downcast<KeyframeEffect>(transition->effect())->target()) {
508             m_elementToCompletedCSSTransitionByCSSPropertyID.ensure(target, [] {
509                 return HashMap<CSSPropertyID, RefPtr<CSSTransition>> { };
510             }).iterator->value.set(transition->property(), transition);
511         }
512     }
513 }
514
515 void DocumentTimeline::scheduleNextTick()
516 {
517     // There is no tick to schedule if we don't have any relevant animations.
518     if (m_animations.isEmpty())
519         return;
520
521     for (const auto& animation : m_animations) {
522         if (!animation->isRunningAccelerated()) {
523             scheduleAnimationResolution();
524             return;
525         }
526     }
527
528     Seconds scheduleDelay = Seconds::infinity();
529
530     for (const auto& animation : m_animations) {
531         auto animationTimeToNextRequiredTick = animation->timeToNextTick();
532         if (animationTimeToNextRequiredTick < animationInterval()) {
533             scheduleAnimationResolution();
534             return;
535         }
536         scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
537     }
538
539     if (scheduleDelay < Seconds::infinity())
540         m_tickScheduleTimer.startOneShot(scheduleDelay);
541 }
542
543 bool DocumentTimeline::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const
544 {
545     if (!renderer.element())
546         return true;
547
548     KeyframeEffect* matchingEffect = nullptr;
549     for (const auto& animation : animationsForElement(*renderer.element())) {
550         auto* effect = animation->effect();
551         if (is<KeyframeEffect>(effect)) {
552             auto* keyframeEffect = downcast<KeyframeEffect>(effect);
553             if (keyframeEffect->animatedProperties().contains(CSSPropertyTransform))
554                 matchingEffect = downcast<KeyframeEffect>(effect);
555         }
556     }
557
558     if (matchingEffect)
559         return matchingEffect->computeExtentOfTransformAnimation(bounds);
560
561     return true;
562 }
563
564 bool DocumentTimeline::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
565 {
566     if (!renderer.element())
567         return false;
568
569     for (const auto& animation : animationsForElement(*renderer.element())) {
570         auto playState = animation->playState();
571         if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
572             continue;
573         auto* effect = animation->effect();
574         if (is<KeyframeEffect>(effect) && downcast<KeyframeEffect>(effect)->animatedProperties().contains(property))
575             return true;
576     }
577
578     return false;
579 }
580
581 bool DocumentTimeline::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
582 {
583     if (!renderer.element())
584         return false;
585
586     for (const auto& animation : animationsForElement(*renderer.element())) {
587         auto playState = animation->playState();
588         if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
589             continue;
590         auto* effect = animation->effect();
591         if (is<KeyframeEffect>(effect)) {
592             auto* keyframeEffect = downcast<KeyframeEffect>(effect);
593             if (keyframeEffect->isRunningAccelerated() && keyframeEffect->animatedProperties().contains(property))
594                 return true;
595         }
596     }
597
598     return false;
599 }
600
601 std::unique_ptr<RenderStyle> DocumentTimeline::animatedStyleForRenderer(RenderElement& renderer)
602 {
603     std::unique_ptr<RenderStyle> result;
604
605     if (auto* element = renderer.element()) {
606         for (const auto& animation : animationsForElement(*element)) {
607             if (is<KeyframeEffect>(animation->effect()))
608                 downcast<KeyframeEffect>(animation->effect())->getAnimatedStyle(result);
609         }
610     }
611
612     if (!result)
613         result = RenderStyle::clonePtr(renderer.style());
614
615     return result;
616 }
617
618 void DocumentTimeline::animationWasAddedToElement(WebAnimation& animation, Element& element)
619 {
620     AnimationTimeline::animationWasAddedToElement(animation, element);
621     updateListOfElementsWithRunningAcceleratedAnimationsForElement(element);
622 }
623
624 void DocumentTimeline::animationWasRemovedFromElement(WebAnimation& animation, Element& element)
625 {
626     AnimationTimeline::animationWasRemovedFromElement(animation, element);
627     updateListOfElementsWithRunningAcceleratedAnimationsForElement(element);
628 }
629
630 void DocumentTimeline::animationAcceleratedRunningStateDidChange(WebAnimation& animation)
631 {
632     m_acceleratedAnimationsPendingRunningStateChange.add(&animation);
633
634     if (is<KeyframeEffect>(animation.effect())) {
635         if (auto* target = downcast<KeyframeEffect>(animation.effect())->target())
636             updateListOfElementsWithRunningAcceleratedAnimationsForElement(*target);
637     }
638 }
639
640 void DocumentTimeline::updateListOfElementsWithRunningAcceleratedAnimationsForElement(Element& element)
641 {
642     auto animations = animationsForElement(element);
643
644     if (animations.isEmpty()) {
645         m_elementsWithRunningAcceleratedAnimations.remove(&element);
646         return;
647     }
648
649     for (const auto& animation : animations) {
650         if (!animation->isRunningAccelerated()) {
651             m_elementsWithRunningAcceleratedAnimations.remove(&element);
652             return;
653         }
654     }
655
656     m_elementsWithRunningAcceleratedAnimations.add(&element);
657 }
658
659 void DocumentTimeline::applyPendingAcceleratedAnimations()
660 {
661     auto acceleratedAnimationsPendingRunningStateChange = m_acceleratedAnimationsPendingRunningStateChange;
662     m_acceleratedAnimationsPendingRunningStateChange.clear();
663
664     bool hasForcedLayout = false;
665     for (auto& animation : acceleratedAnimationsPendingRunningStateChange) {
666         if (!hasForcedLayout) {
667             auto* effect = animation->effect();
668             if (is<KeyframeEffect>(effect))
669                 hasForcedLayout |= downcast<KeyframeEffect>(effect)->forceLayoutIfNeeded();
670         }
671         animation->applyPendingAcceleratedActions();
672     }
673 }
674
675 bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element) const
676 {
677     return m_elementsWithRunningAcceleratedAnimations.contains(&element);
678 }
679
680 void DocumentTimeline::enqueueAnimationPlaybackEvent(AnimationPlaybackEvent& event)
681 {
682     m_pendingAnimationEvents.append(event);
683 }
684
685 Vector<std::pair<String, double>> DocumentTimeline::acceleratedAnimationsForElement(Element& element) const
686 {
687     auto* renderer = element.renderer();
688     if (renderer && renderer->isComposited()) {
689         auto* compositedRenderer = downcast<RenderBoxModelObject>(renderer);
690         if (auto* graphicsLayer = compositedRenderer->layer()->backing()->graphicsLayer())
691             return graphicsLayer->acceleratedAnimationsForTesting();
692     }
693     return { };
694 }
695
696 unsigned DocumentTimeline::numberOfAnimationTimelineInvalidationsForTesting() const
697 {
698     return m_numberOfAnimationTimelineInvalidationsForTesting;
699 }
700
701 } // namespace WebCore