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