3018cbb2ccd2af8f77abb60bc6c239238cfaf4e2
[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 "CSSPropertyAnimation.h"
32 #include "CSSTransition.h"
33 #include "DOMWindow.h"
34 #include "DeclarativeAnimation.h"
35 #include "Document.h"
36 #include "DocumentAnimationScheduler.h"
37 #include "GraphicsLayer.h"
38 #include "KeyframeEffect.h"
39 #include "Microtasks.h"
40 #include "Node.h"
41 #include "Page.h"
42 #include "PseudoElement.h"
43 #include "RenderElement.h"
44 #include "RenderLayer.h"
45 #include "RenderLayerBacking.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 #if !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
65     , m_animationResolutionTimer(*this, &DocumentTimeline::animationResolutionTimerFired)
66 #endif
67     , m_tickScheduleTimer(*this, &DocumentTimeline::scheduleAnimationResolutionIfNeeded)
68     , m_document(&document)
69     , m_originTime(originTime)
70 {
71     if (m_document && m_document->page() && !m_document->page()->isVisible())
72         suspendAnimations();
73 }
74
75 DocumentTimeline::~DocumentTimeline() = default;
76
77 void DocumentTimeline::detachFromDocument()
78 {
79     m_currentTimeClearingTaskQueue.close();
80     m_elementsWithRunningAcceleratedAnimations.clear();
81
82     auto& animationsToRemove = m_animations;
83     while (!animationsToRemove.isEmpty())
84         animationsToRemove.first()->remove();
85
86     unscheduleAnimationResolution();
87     m_document = nullptr;
88 }
89
90 static inline bool compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(Element* lhsOwningElement, Element* rhsOwningElement)
91 {
92     // With regard to pseudo-elements, the sort order is as follows:
93     //     - element
94     //     - ::before
95     //     - ::after
96     //     - element children
97     
98     // We could be comparing two pseudo-elements that are hosted on the same element.
99     if (is<PseudoElement>(lhsOwningElement) && is<PseudoElement>(rhsOwningElement)) {
100         auto* lhsPseudoElement = downcast<PseudoElement>(lhsOwningElement);
101         auto* rhsPseudoElement = downcast<PseudoElement>(rhsOwningElement);
102         if (lhsPseudoElement->hostElement() == rhsPseudoElement->hostElement())
103             return lhsPseudoElement->isBeforePseudoElement();
104     }
105
106     // Or comparing a pseudo-element that is compared to another non-pseudo element, in which case
107     // we want to see if it's hosted on that other element, and if not use its host element to compare.
108     if (is<PseudoElement>(lhsOwningElement)) {
109         auto* lhsHostElement = downcast<PseudoElement>(lhsOwningElement)->hostElement();
110         if (rhsOwningElement == lhsHostElement)
111             return false;
112         lhsOwningElement = lhsHostElement;
113     }
114
115     if (is<PseudoElement>(rhsOwningElement)) {
116         auto* rhsHostElement = downcast<PseudoElement>(rhsOwningElement)->hostElement();
117         if (lhsOwningElement == rhsHostElement)
118             return true;
119         rhsOwningElement = rhsHostElement;
120     }
121
122     return lhsOwningElement->compareDocumentPosition(*rhsOwningElement) & Node::DOCUMENT_POSITION_FOLLOWING;
123 }
124
125 Vector<RefPtr<WebAnimation>> DocumentTimeline::getAnimations() const
126 {
127     ASSERT(m_document);
128
129     Vector<RefPtr<WebAnimation>> cssTransitions;
130     Vector<RefPtr<WebAnimation>> cssAnimations;
131     Vector<RefPtr<WebAnimation>> webAnimations;
132
133     // First, let's get all qualifying animations in their right group.
134     for (const auto& animation : m_allAnimations) {
135         if (!animation->isRelevant() || animation->timeline() != this || !is<KeyframeEffect>(animation->effect()))
136             continue;
137
138         auto* target = downcast<KeyframeEffect>(animation->effect())->target();
139         if (!target || !target->isDescendantOf(*m_document))
140             continue;
141
142         if (is<CSSTransition>(animation) && downcast<CSSTransition>(animation)->owningElement())
143             cssTransitions.append(animation);
144         else if (is<CSSAnimation>(animation) && downcast<CSSAnimation>(animation)->owningElement())
145             cssAnimations.append(animation);
146         else
147             webAnimations.append(animation);
148     }
149
150     // Now sort CSS Transitions by their composite order.
151     std::sort(cssTransitions.begin(), cssTransitions.end(), [](auto& lhs, auto& rhs) {
152         // https://drafts.csswg.org/css-transitions-2/#animation-composite-order
153         auto* lhsTransition = downcast<CSSTransition>(lhs.get());
154         auto* rhsTransition = downcast<CSSTransition>(rhs.get());
155
156         auto* lhsOwningElement = lhsTransition->owningElement();
157         auto* rhsOwningElement = rhsTransition->owningElement();
158
159         // If the owning element of A and B differs, sort A and B by tree order of their corresponding owning elements.
160         if (lhsOwningElement != rhsOwningElement)
161             return compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(lhsOwningElement, rhsOwningElement);
162
163         // Otherwise, if A and B have different transition generation values, sort by their corresponding transition generation in ascending order.
164         if (lhsTransition->generationTime() != rhsTransition->generationTime())
165             return lhsTransition->generationTime() < rhsTransition->generationTime();
166
167         // Otherwise, sort A and B in ascending order by the Unicode codepoints that make up the expanded transition property name of each transition
168         // (i.e. without attempting case conversion and such that ‘-moz-column-width’ sorts before ‘column-width’).
169         return lhsTransition->transitionProperty().utf8() < rhsTransition->transitionProperty().utf8();
170     });
171
172     // Now sort CSS Animations by their composite order.
173     std::sort(cssAnimations.begin(), cssAnimations.end(), [](auto& lhs, auto& rhs) {
174         // https://drafts.csswg.org/css-animations-2/#animation-composite-order
175         auto* lhsOwningElement = downcast<CSSAnimation>(lhs.get())->owningElement();
176         auto* rhsOwningElement = downcast<CSSAnimation>(rhs.get())->owningElement();
177
178         // If the owning element of A and B differs, sort A and B by tree order of their corresponding owning elements.
179         if (lhsOwningElement != rhsOwningElement)
180             return compareDeclarativeAnimationOwningElementPositionsInDocumentTreeOrder(lhsOwningElement, rhsOwningElement);
181
182         // Otherwise, sort A and B based on their position in the computed value of the animation-name property of the (common) owning element.
183         // In our case, this matches the time at which the animations were created and thus their relative position in m_allAnimations.
184         return false;
185     });
186
187     // Finally, we can concatenate the sorted CSS Transitions, CSS Animations and Web Animations in their relative composite order.
188     Vector<RefPtr<WebAnimation>> animations;
189     animations.appendRange(cssTransitions.begin(), cssTransitions.end());
190     animations.appendRange(cssAnimations.begin(), cssAnimations.end());
191     animations.appendRange(webAnimations.begin(), webAnimations.end());
192     return animations;
193 }
194
195 void DocumentTimeline::updateThrottlingState()
196 {
197     scheduleAnimationResolutionIfNeeded();
198 }
199
200 Seconds DocumentTimeline::animationInterval() const
201 {
202     if (!m_document || !m_document->page())
203         return Seconds::infinity();
204     return m_document->page()->isLowPowerModeEnabled() ? throttledAnimationInterval : defaultAnimationInterval;
205 }
206
207 void DocumentTimeline::suspendAnimations()
208 {
209     if (animationsAreSuspended())
210         return;
211
212     if (!m_cachedCurrentTime)
213         m_cachedCurrentTime = liveCurrentTime();
214
215     for (const auto& animation : m_animations)
216         animation->setSuspended(true);
217
218     m_isSuspended = true;
219
220     applyPendingAcceleratedAnimations();
221
222     unscheduleAnimationResolution();
223 }
224
225 void DocumentTimeline::resumeAnimations()
226 {
227     if (!animationsAreSuspended())
228         return;
229
230     m_cachedCurrentTime = WTF::nullopt;
231
232     m_isSuspended = false;
233
234     for (const auto& animation : m_animations)
235         animation->setSuspended(false);
236
237     scheduleAnimationResolutionIfNeeded();
238 }
239
240 bool DocumentTimeline::animationsAreSuspended()
241 {
242     return m_isSuspended;
243 }
244
245 unsigned DocumentTimeline::numberOfActiveAnimationsForTesting() const
246 {
247     unsigned count = 0;
248     for (const auto& animation : m_animations) {
249         if (!animation->isSuspended())
250             ++count;
251     }
252     return count;
253 }
254
255 Seconds DocumentTimeline::liveCurrentTime() const
256 {
257 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
258     return m_document->animationScheduler().lastTimestamp();
259 #else
260     return Seconds(m_document->domWindow()->nowTimestamp());
261 #endif
262 }
263
264 Optional<Seconds> DocumentTimeline::currentTime()
265 {
266     if (!m_document || !m_document->domWindow())
267         return AnimationTimeline::currentTime();
268
269     if (auto* mainDocumentTimeline = m_document->existingTimeline()) {
270         if (mainDocumentTimeline != this) {
271             if (auto mainDocumentTimelineCurrentTime = mainDocumentTimeline->currentTime())
272                 return mainDocumentTimelineCurrentTime.value() - m_originTime;
273             return WTF::nullopt;
274         }
275     }
276
277     auto currentTime = liveCurrentTime();
278
279 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
280     // If we're in the middle of firing a frame, either due to a requestAnimationFrame callback
281     // or scheduling an animation update, we want to ensure we use the same time we're using as
282     // the timestamp for requestAnimationFrame() callbacks.
283     if (m_document->animationScheduler().isFiring())
284         cacheCurrentTime(currentTime);
285 #endif
286
287     if (!m_cachedCurrentTime) {
288 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
289         // If we're not in the middle of firing a frame, let's make our best guess at what the currentTime should
290         // be since the last time a frame fired by increment of our update interval. This way code using something
291         // like setTimeout() or handling events will get a time that's only updating at around 60fps, or less if
292         // we're throttled.
293         auto lastAnimationSchedulerTimestamp = currentTime;
294         auto delta = Seconds(m_document->domWindow()->nowTimestamp()) - lastAnimationSchedulerTimestamp;
295         int frames = std::floor(delta.seconds() / animationInterval().seconds());
296         cacheCurrentTime(lastAnimationSchedulerTimestamp + Seconds(frames * animationInterval().seconds()));
297 #else
298         cacheCurrentTime(currentTime);
299 #endif
300     }
301     return m_cachedCurrentTime.value() - m_originTime;
302 }
303
304 void DocumentTimeline::cacheCurrentTime(Seconds newCurrentTime)
305 {
306     m_cachedCurrentTime = newCurrentTime;
307     // We want to be sure to keep this time cached until we've both finished running JS and finished updating
308     // animations, so we schedule the invalidation task and register a whenIdle callback on the VM, which will
309     // fire syncronously if no JS is running.
310     m_waitingOnVMIdle = true;
311     if (!m_currentTimeClearingTaskQueue.hasPendingTasks())
312         m_currentTimeClearingTaskQueue.enqueueTask(std::bind(&DocumentTimeline::maybeClearCachedCurrentTime, this));
313     m_document->vm().whenIdle([this, protectedThis = makeRefPtr(this)]() {
314         m_waitingOnVMIdle = false;
315         maybeClearCachedCurrentTime();
316     });
317 }
318
319 void DocumentTimeline::maybeClearCachedCurrentTime()
320 {
321     // We want to make sure we only clear the cached current time if we're not currently running
322     // JS or waiting on all current animation updating code to have completed. This is so that
323     // we're guaranteed to have a consistent current time reported for all work happening in a given
324     // JS frame or throughout updating animations in WebCore.
325     if (!m_isSuspended && !m_waitingOnVMIdle && !m_currentTimeClearingTaskQueue.hasPendingTasks())
326         m_cachedCurrentTime = WTF::nullopt;
327 }
328
329 void DocumentTimeline::scheduleAnimationResolutionIfNeeded()
330 {
331     if (!m_isUpdatingAnimations && !m_isSuspended && !m_animations.isEmpty())
332         scheduleAnimationResolution();
333 }
334
335 void DocumentTimeline::animationTimingDidChange(WebAnimation& animation)
336 {
337     AnimationTimeline::animationTimingDidChange(animation);
338     scheduleAnimationResolutionIfNeeded();
339 }
340
341 void DocumentTimeline::removeAnimation(WebAnimation& animation)
342 {
343     AnimationTimeline::removeAnimation(animation);
344
345     if (m_animations.isEmpty())
346         unscheduleAnimationResolution();
347 }
348
349 void DocumentTimeline::scheduleAnimationResolution()
350 {
351 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
352     m_document->animationScheduler().scheduleWebAnimationsResolution();
353 #else
354     // FIXME: We need to use the same logic as ScriptedAnimationController here,
355     // which will be addressed by the refactor tracked by webkit.org/b/179293.
356     m_animationResolutionTimer.startOneShot(animationInterval());
357 #endif
358 }
359
360 void DocumentTimeline::unscheduleAnimationResolution()
361 {
362     m_tickScheduleTimer.stop();
363 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
364     m_document->animationScheduler().unscheduleWebAnimationsResolution();
365 #else
366     // FIXME: We need to use the same logic as ScriptedAnimationController here,
367     // which will be addressed by the refactor tracked by webkit.org/b/179293.
368     m_animationResolutionTimer.stop();
369 #endif
370 }
371
372 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
373 void DocumentTimeline::documentAnimationSchedulerDidFire()
374 #else
375 void DocumentTimeline::animationResolutionTimerFired()
376 #endif
377 {
378     updateAnimationsAndSendEvents();
379     applyPendingAcceleratedAnimations();
380     scheduleNextTick();
381 }
382
383 void DocumentTimeline::updateAnimationsAndSendEvents()
384 {
385     m_numberOfAnimationTimelineInvalidationsForTesting++;
386
387     m_isUpdatingAnimations = true;
388
389     // https://drafts.csswg.org/web-animations/#update-animations-and-send-events
390
391     // 1. Update the current time of all timelines associated with doc passing now as the timestamp.
392
393     Vector<RefPtr<WebAnimation>> animationsToRemove;
394     Vector<RefPtr<CSSTransition>> completedTransitions;
395
396     for (auto& animation : m_animations) {
397         if (animation->timeline() != this) {
398             ASSERT(!animation->timeline());
399             animationsToRemove.append(animation);
400             continue;
401         }
402
403         // This will notify the animation that timing has changed and will call automatically
404         // schedule invalidation if required for this animation.
405         animation->tick();
406
407         if (!animation->isRelevant() && !animation->needsTick())
408             animationsToRemove.append(animation);
409
410         if (!animation->needsTick() && is<CSSTransition>(animation) && animation->playState() == WebAnimation::PlayState::Finished) {
411             auto* transition = downcast<CSSTransition>(animation.get());
412             if (transition->owningElement())
413                 completedTransitions.append(transition);
414         }
415     }
416
417     // 2. Perform a microtask checkpoint.
418     MicrotaskQueue::mainThreadQueue().performMicrotaskCheckpoint();
419
420     // 3. Let events to dispatch be a copy of doc's pending animation event queue.
421     // 4. Clear doc's pending animation event queue.
422     auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
423
424     // 5. Perform a stable sort of the animation events in events to dispatch as follows.
425     std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), [] (const Ref<AnimationPlaybackEvent>& lhs, const Ref<AnimationPlaybackEvent>& rhs) {
426         // 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
427         // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
428         // 2. Within events with equal scheduled event times, sort by their composite order. FIXME: We don't do this.
429         if (lhs->timelineTime() && !rhs->timelineTime())
430             return false;
431         if (!lhs->timelineTime() && rhs->timelineTime())
432             return true;
433         if (!lhs->timelineTime() && !rhs->timelineTime())
434             return true;
435         return lhs->timelineTime().value() < rhs->timelineTime().value();
436     });
437
438     // 6. Dispatch each of the events in events to dispatch at their corresponding target using the order established in the previous step.
439     for (auto& pendingEvent : pendingAnimationEvents)
440         pendingEvent->target()->dispatchEvent(pendingEvent);
441
442     // This will cancel any scheduled invalidation if we end up removing all animations.
443     for (auto& animation : animationsToRemove)
444         removeAnimation(*animation);
445
446     // Now that animations that needed removal have been removed, let's update the list of completed transitions.
447     // This needs to happen after dealing with the list of animations to remove as the animation may have been
448     // removed from the list of completed transitions otherwise.
449     for (auto& completedTransition : completedTransitions)
450         transitionDidComplete(completedTransition);
451
452     m_isUpdatingAnimations = false;
453 }
454
455 void DocumentTimeline::transitionDidComplete(RefPtr<CSSTransition> transition)
456 {
457     ASSERT(transition);
458     removeAnimation(*transition);
459     if (is<KeyframeEffect>(transition->effect())) {
460         if (auto* target = downcast<KeyframeEffect>(transition->effect())->target()) {
461             m_elementToCompletedCSSTransitionByCSSPropertyID.ensure(target, [] {
462                 return HashMap<CSSPropertyID, RefPtr<CSSTransition>> { };
463             }).iterator->value.set(transition->property(), transition);
464         }
465     }
466 }
467
468 void DocumentTimeline::scheduleNextTick()
469 {
470     // There is no tick to schedule if we don't have any relevant animations.
471     if (m_animations.isEmpty())
472         return;
473
474     for (const auto& animation : m_animations) {
475         if (!animation->isRunningAccelerated()) {
476             scheduleAnimationResolutionIfNeeded();
477             return;
478         }
479     }
480
481     Seconds scheduleDelay = Seconds::infinity();
482
483     for (const auto& animation : m_animations) {
484         auto animationTimeToNextRequiredTick = animation->timeToNextTick();
485         if (animationTimeToNextRequiredTick < animationInterval()) {
486             scheduleAnimationResolutionIfNeeded();
487             return;
488         }
489         scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
490     }
491
492     if (scheduleDelay < Seconds::infinity())
493         m_tickScheduleTimer.startOneShot(scheduleDelay);
494 }
495
496 bool DocumentTimeline::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const
497 {
498     if (!renderer.element())
499         return true;
500
501     KeyframeEffect* matchingEffect = nullptr;
502     for (const auto& animation : animationsForElement(*renderer.element())) {
503         auto* effect = animation->effect();
504         if (is<KeyframeEffect>(effect)) {
505             auto* keyframeEffect = downcast<KeyframeEffect>(effect);
506             if (keyframeEffect->animatedProperties().contains(CSSPropertyTransform))
507                 matchingEffect = downcast<KeyframeEffect>(effect);
508         }
509     }
510
511     if (matchingEffect)
512         return matchingEffect->computeExtentOfTransformAnimation(bounds);
513
514     return true;
515 }
516
517 bool DocumentTimeline::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
518 {
519     if (!renderer.element())
520         return false;
521
522     for (const auto& animation : animationsForElement(*renderer.element())) {
523         auto playState = animation->playState();
524         if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
525             continue;
526         auto* effect = animation->effect();
527         if (is<KeyframeEffect>(effect) && downcast<KeyframeEffect>(effect)->animatedProperties().contains(property))
528             return true;
529     }
530
531     return false;
532 }
533
534 bool DocumentTimeline::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
535 {
536     if (!renderer.element())
537         return false;
538
539     for (const auto& animation : animationsForElement(*renderer.element())) {
540         auto playState = animation->playState();
541         if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
542             continue;
543         auto* effect = animation->effect();
544         if (is<KeyframeEffect>(effect)) {
545             auto* keyframeEffect = downcast<KeyframeEffect>(effect);
546             if (keyframeEffect->isRunningAccelerated() && keyframeEffect->animatedProperties().contains(property))
547                 return true;
548         }
549     }
550
551     return false;
552 }
553
554 std::unique_ptr<RenderStyle> DocumentTimeline::animatedStyleForRenderer(RenderElement& renderer)
555 {
556     std::unique_ptr<RenderStyle> result;
557
558     if (auto* element = renderer.element()) {
559         for (const auto& animation : animationsForElement(*element)) {
560             if (is<KeyframeEffect>(animation->effect()))
561                 downcast<KeyframeEffect>(animation->effect())->getAnimatedStyle(result);
562         }
563     }
564
565     if (!result)
566         result = RenderStyle::clonePtr(renderer.style());
567
568     return result;
569 }
570
571 void DocumentTimeline::animationWasAddedToElement(WebAnimation& animation, Element& element)
572 {
573     AnimationTimeline::animationWasAddedToElement(animation, element);
574     updateListOfElementsWithRunningAcceleratedAnimationsForElement(element);
575 }
576
577 void DocumentTimeline::animationWasRemovedFromElement(WebAnimation& animation, Element& element)
578 {
579     AnimationTimeline::animationWasRemovedFromElement(animation, element);
580     updateListOfElementsWithRunningAcceleratedAnimationsForElement(element);
581 }
582
583 void DocumentTimeline::animationAcceleratedRunningStateDidChange(WebAnimation& animation)
584 {
585     m_acceleratedAnimationsPendingRunningStateChange.add(&animation);
586
587     if (is<KeyframeEffect>(animation.effect())) {
588         if (auto* target = downcast<KeyframeEffect>(animation.effect())->target())
589             updateListOfElementsWithRunningAcceleratedAnimationsForElement(*target);
590     }
591 }
592
593 void DocumentTimeline::updateListOfElementsWithRunningAcceleratedAnimationsForElement(Element& element)
594 {
595     auto animations = animationsForElement(element);
596     bool runningAnimationsForElementAreAllAccelerated = !animations.isEmpty();
597     for (const auto& animation : animations) {
598         if (!animation->isRunningAccelerated()) {
599             runningAnimationsForElementAreAllAccelerated = false;
600             break;
601         }
602     }
603
604     if (runningAnimationsForElementAreAllAccelerated)
605         m_elementsWithRunningAcceleratedAnimations.add(&element);
606     else
607         m_elementsWithRunningAcceleratedAnimations.remove(&element);
608 }
609
610 void DocumentTimeline::applyPendingAcceleratedAnimations()
611 {
612     auto acceleratedAnimationsPendingRunningStateChange = m_acceleratedAnimationsPendingRunningStateChange;
613     m_acceleratedAnimationsPendingRunningStateChange.clear();
614
615     bool hasForcedLayout = false;
616     for (auto& animation : acceleratedAnimationsPendingRunningStateChange) {
617         if (!hasForcedLayout) {
618             auto* effect = animation->effect();
619             if (is<KeyframeEffect>(effect))
620                 hasForcedLayout |= downcast<KeyframeEffect>(effect)->forceLayoutIfNeeded();
621         }
622         animation->applyPendingAcceleratedActions();
623     }
624 }
625
626 bool DocumentTimeline::resolveAnimationsForElement(Element& element, RenderStyle& targetStyle)
627 {
628     bool hasNonAcceleratedAnimations = false;
629     bool hasPendingAcceleratedAnimations = true;
630     for (const auto& animation : animationsForElement(element)) {
631         animation->resolve(targetStyle);
632         if (!hasNonAcceleratedAnimations) {
633             if (auto* effect = animation->effect()) {
634                 if (is<KeyframeEffect>(effect)) {
635                     auto* keyframeEffect = downcast<KeyframeEffect>(effect);
636                     for (auto cssPropertyId : keyframeEffect->animatedProperties()) {
637                         if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(cssPropertyId)) {
638                             hasNonAcceleratedAnimations = true;
639                             continue;
640                         }
641                         if (!hasPendingAcceleratedAnimations)
642                             hasPendingAcceleratedAnimations = keyframeEffect->hasPendingAcceleratedAction();
643                     }
644                 }
645             }
646         }
647     }
648
649     // If there are no non-accelerated animations and we've encountered at least one pending
650     // accelerated animation, we should recomposite this element's layer for animation purposes.
651     return !hasNonAcceleratedAnimations && hasPendingAcceleratedAnimations;
652 }
653
654 bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element) const
655 {
656     return m_elementsWithRunningAcceleratedAnimations.contains(&element);
657 }
658
659 void DocumentTimeline::enqueueAnimationPlaybackEvent(AnimationPlaybackEvent& event)
660 {
661     m_pendingAnimationEvents.append(event);
662 }
663
664 Vector<std::pair<String, double>> DocumentTimeline::acceleratedAnimationsForElement(Element& element) const
665 {
666     auto* renderer = element.renderer();
667     if (renderer && renderer->isComposited()) {
668         auto* compositedRenderer = downcast<RenderBoxModelObject>(renderer);
669         if (auto* graphicsLayer = compositedRenderer->layer()->backing()->graphicsLayer())
670             return graphicsLayer->acceleratedAnimationsForTesting();
671     }
672     return { };
673 }
674
675 unsigned DocumentTimeline::numberOfAnimationTimelineInvalidationsForTesting() const
676 {
677     return m_numberOfAnimationTimelineInvalidationsForTesting;
678 }
679
680 } // namespace WebCore