d5a5bb28782ef2c8ffced6b9586cb7db9c7caca4
[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. Remove replaced animations for doc.
382     removeReplacedAnimations();
383
384     // 3. Perform a microtask checkpoint.
385     MicrotaskQueue::mainThreadQueue().performMicrotaskCheckpoint();
386
387     // 4. Let events to dispatch be a copy of doc's pending animation event queue.
388     // 5. Clear doc's pending animation event queue.
389     auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
390
391     // 6. Perform a stable sort of the animation events in events to dispatch as follows.
392     std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), [] (const Ref<AnimationPlaybackEvent>& lhs, const Ref<AnimationPlaybackEvent>& rhs) {
393         // 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
394         // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
395         // 2. Within events with equal scheduled event times, sort by their composite order. FIXME: We don't do this.
396         if (lhs->timelineTime() && !rhs->timelineTime())
397             return false;
398         if (!lhs->timelineTime() && rhs->timelineTime())
399             return true;
400         if (!lhs->timelineTime() && !rhs->timelineTime())
401             return true;
402         return lhs->timelineTime().value() < rhs->timelineTime().value();
403     });
404
405     // 7. Dispatch each of the events in events to dispatch at their corresponding target using the order established in the previous step.
406     for (auto& pendingEvent : pendingAnimationEvents)
407         pendingEvent->target()->dispatchEvent(pendingEvent);
408
409     // This will cancel any scheduled invalidation if we end up removing all animations.
410     for (auto& animation : animationsToRemove) {
411         // An animation that was initially marked as irrelevant may have changed while we were sending events, so we run the same
412         // check that we ran to add it to animationsToRemove in the first place.
413         if (!animation->isRelevant() && !animation->needsTick())
414             removeAnimation(*animation);
415     }
416
417     // Now that animations that needed removal have been removed, let's update the list of completed transitions.
418     // This needs to happen after dealing with the list of animations to remove as the animation may have been
419     // removed from the list of completed transitions otherwise.
420     for (auto& completedTransition : completedTransitions)
421         transitionDidComplete(completedTransition);
422 }
423
424 bool DocumentTimeline::animationCanBeRemoved(WebAnimation& animation)
425 {
426     // https://drafts.csswg.org/web-animations/#removing-replaced-animations
427
428     ASSERT(m_document);
429
430     // - is replaceable, and
431     if (!animation.isReplaceable())
432         return false;
433
434     // - has a replace state of active, and
435     if (animation.replaceState() != WebAnimation::ReplaceState::Active)
436         return false;
437
438     // - has an associated animation effect whose target element is a descendant of doc, and
439     auto* effect = animation.effect();
440     if (!is<KeyframeEffect>(effect))
441         return false;
442
443     auto* keyframeEffect = downcast<KeyframeEffect>(effect);
444     auto* target = keyframeEffect->target();
445     if (!target || !target->isDescendantOf(*m_document))
446         return false;
447
448     HashSet<CSSPropertyID> propertiesToMatch = keyframeEffect->animatedProperties();
449     auto animations = animationsForElement(*target, AnimationTimeline::Ordering::Sorted);
450     for (auto& animationWithHigherCompositeOrder : WTF::makeReversedRange(animations)) {
451         if (&animation == animationWithHigherCompositeOrder)
452             break;
453
454         if (animationWithHigherCompositeOrder && animationWithHigherCompositeOrder->isReplaceable()) {
455             auto* effectWithHigherCompositeOrder = animationWithHigherCompositeOrder->effect();
456             if (is<KeyframeEffect>(effectWithHigherCompositeOrder)) {
457                 auto* keyframeEffectWithHigherCompositeOrder = downcast<KeyframeEffect>(effectWithHigherCompositeOrder);
458                 for (auto cssPropertyId : keyframeEffectWithHigherCompositeOrder->animatedProperties()) {
459                     if (propertiesToMatch.remove(cssPropertyId) && propertiesToMatch.isEmpty())
460                         break;
461                 }
462             }
463         }
464     }
465
466     return propertiesToMatch.isEmpty();
467 }
468
469 void DocumentTimeline::removeReplacedAnimations()
470 {
471     // https://drafts.csswg.org/web-animations/#removing-replaced-animations
472
473     Vector<RefPtr<WebAnimation>> animationsToRemove;
474
475     // When asked to remove replaced animations for a Document, doc, then for every animation, animation
476     for (auto& animation : m_allAnimations) {
477         if (animation && animationCanBeRemoved(*animation)) {
478             // perform the following steps:
479             // 1. Set animation's replace state to removed.
480             animation->setReplaceState(WebAnimation::ReplaceState::Removed);
481             // 2. Create an AnimationPlaybackEvent, removeEvent.
482             // 3. Set removeEvent's type attribute to remove.
483             // 4. Set removeEvent's currentTime attribute to the current time of animation.
484             // 5. Set removeEvent's timelineTime attribute to the current time of the timeline with which animation is associated.
485             // 6. If animation has a document for timing, then append removeEvent to its document for timing's pending animation
486             //    event queue along with its target, animation. For the scheduled event time, use the result of applying the procedure
487             //    to convert timeline time to origin-relative time to the current time of the timeline with which animation is associated.
488             //    Otherwise, queue a task to dispatch removeEvent at animation. The task source for this task is the DOM manipulation task source.
489             animation->enqueueAnimationPlaybackEvent(eventNames().removeEvent, animation->currentTime(), currentTime());
490
491             animationsToRemove.append(animation.get());
492         }
493     }
494
495     for (auto& animation : animationsToRemove)
496         removeAnimation(*animation);
497 }
498
499 void DocumentTimeline::transitionDidComplete(RefPtr<CSSTransition> transition)
500 {
501     ASSERT(transition);
502     removeAnimation(*transition);
503     if (is<KeyframeEffect>(transition->effect())) {
504         if (auto* target = downcast<KeyframeEffect>(transition->effect())->target()) {
505             m_elementToCompletedCSSTransitionByCSSPropertyID.ensure(target, [] {
506                 return HashMap<CSSPropertyID, RefPtr<CSSTransition>> { };
507             }).iterator->value.set(transition->property(), transition);
508         }
509     }
510 }
511
512 void DocumentTimeline::scheduleNextTick()
513 {
514     // There is no tick to schedule if we don't have any relevant animations.
515     if (m_animations.isEmpty())
516         return;
517
518     for (const auto& animation : m_animations) {
519         if (!animation->isRunningAccelerated()) {
520             scheduleAnimationResolution();
521             return;
522         }
523     }
524
525     Seconds scheduleDelay = Seconds::infinity();
526
527     for (const auto& animation : m_animations) {
528         auto animationTimeToNextRequiredTick = animation->timeToNextTick();
529         if (animationTimeToNextRequiredTick < animationInterval()) {
530             scheduleAnimationResolution();
531             return;
532         }
533         scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
534     }
535
536     if (scheduleDelay < Seconds::infinity())
537         m_tickScheduleTimer.startOneShot(scheduleDelay);
538 }
539
540 bool DocumentTimeline::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const
541 {
542     if (!renderer.element())
543         return true;
544
545     KeyframeEffect* matchingEffect = nullptr;
546     for (const auto& animation : animationsForElement(*renderer.element())) {
547         auto* effect = animation->effect();
548         if (is<KeyframeEffect>(effect)) {
549             auto* keyframeEffect = downcast<KeyframeEffect>(effect);
550             if (keyframeEffect->animatedProperties().contains(CSSPropertyTransform))
551                 matchingEffect = downcast<KeyframeEffect>(effect);
552         }
553     }
554
555     if (matchingEffect)
556         return matchingEffect->computeExtentOfTransformAnimation(bounds);
557
558     return true;
559 }
560
561 bool DocumentTimeline::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
562 {
563     if (!renderer.element())
564         return false;
565
566     for (const auto& animation : animationsForElement(*renderer.element())) {
567         auto playState = animation->playState();
568         if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
569             continue;
570         auto* effect = animation->effect();
571         if (is<KeyframeEffect>(effect) && downcast<KeyframeEffect>(effect)->animatedProperties().contains(property))
572             return true;
573     }
574
575     return false;
576 }
577
578 bool DocumentTimeline::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
579 {
580     if (!renderer.element())
581         return false;
582
583     for (const auto& animation : animationsForElement(*renderer.element())) {
584         auto playState = animation->playState();
585         if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
586             continue;
587         auto* effect = animation->effect();
588         if (is<KeyframeEffect>(effect)) {
589             auto* keyframeEffect = downcast<KeyframeEffect>(effect);
590             if (keyframeEffect->isRunningAccelerated() && keyframeEffect->animatedProperties().contains(property))
591                 return true;
592         }
593     }
594
595     return false;
596 }
597
598 std::unique_ptr<RenderStyle> DocumentTimeline::animatedStyleForRenderer(RenderElement& renderer)
599 {
600     std::unique_ptr<RenderStyle> result;
601
602     if (auto* element = renderer.element()) {
603         for (const auto& animation : animationsForElement(*element)) {
604             if (is<KeyframeEffect>(animation->effect()))
605                 downcast<KeyframeEffect>(animation->effect())->getAnimatedStyle(result);
606         }
607     }
608
609     if (!result)
610         result = RenderStyle::clonePtr(renderer.style());
611
612     return result;
613 }
614
615 void DocumentTimeline::animationWasAddedToElement(WebAnimation& animation, Element& element)
616 {
617     AnimationTimeline::animationWasAddedToElement(animation, element);
618     updateListOfElementsWithRunningAcceleratedAnimationsForElement(element);
619 }
620
621 void DocumentTimeline::animationWasRemovedFromElement(WebAnimation& animation, Element& element)
622 {
623     AnimationTimeline::animationWasRemovedFromElement(animation, element);
624     updateListOfElementsWithRunningAcceleratedAnimationsForElement(element);
625 }
626
627 void DocumentTimeline::animationAcceleratedRunningStateDidChange(WebAnimation& animation)
628 {
629     m_acceleratedAnimationsPendingRunningStateChange.add(&animation);
630
631     if (is<KeyframeEffect>(animation.effect())) {
632         if (auto* target = downcast<KeyframeEffect>(animation.effect())->target())
633             updateListOfElementsWithRunningAcceleratedAnimationsForElement(*target);
634     }
635 }
636
637 void DocumentTimeline::updateListOfElementsWithRunningAcceleratedAnimationsForElement(Element& element)
638 {
639     auto animations = animationsForElement(element);
640
641     if (animations.isEmpty()) {
642         m_elementsWithRunningAcceleratedAnimations.remove(&element);
643         return;
644     }
645
646     for (const auto& animation : animations) {
647         if (!animation->isRunningAccelerated()) {
648             m_elementsWithRunningAcceleratedAnimations.remove(&element);
649             return;
650         }
651     }
652
653     m_elementsWithRunningAcceleratedAnimations.add(&element);
654 }
655
656 void DocumentTimeline::applyPendingAcceleratedAnimations()
657 {
658     auto acceleratedAnimationsPendingRunningStateChange = m_acceleratedAnimationsPendingRunningStateChange;
659     m_acceleratedAnimationsPendingRunningStateChange.clear();
660
661     bool hasForcedLayout = false;
662     for (auto& animation : acceleratedAnimationsPendingRunningStateChange) {
663         if (!hasForcedLayout) {
664             auto* effect = animation->effect();
665             if (is<KeyframeEffect>(effect))
666                 hasForcedLayout |= downcast<KeyframeEffect>(effect)->forceLayoutIfNeeded();
667         }
668         animation->applyPendingAcceleratedActions();
669     }
670 }
671
672 bool DocumentTimeline::resolveAnimationsForElement(Element& element, RenderStyle& targetStyle)
673 {
674     bool hasNonAcceleratedAnimationProperty = false;
675
676     for (const auto& animation : animationsForElement(element)) {
677         animation->resolve(targetStyle);
678
679         if (hasNonAcceleratedAnimationProperty)
680             continue;
681
682         auto* effect = animation->effect();
683         if (!effect || !is<KeyframeEffect>(effect))
684             continue;
685
686         auto* keyframeEffect = downcast<KeyframeEffect>(effect);
687         for (auto cssPropertyId : keyframeEffect->animatedProperties()) {
688             if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(cssPropertyId)) {
689                 hasNonAcceleratedAnimationProperty = true;
690                 break;
691             }
692         }
693     }
694
695     return !hasNonAcceleratedAnimationProperty;
696 }
697
698 bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element) const
699 {
700     return m_elementsWithRunningAcceleratedAnimations.contains(&element);
701 }
702
703 void DocumentTimeline::enqueueAnimationPlaybackEvent(AnimationPlaybackEvent& event)
704 {
705     m_pendingAnimationEvents.append(event);
706 }
707
708 Vector<std::pair<String, double>> DocumentTimeline::acceleratedAnimationsForElement(Element& element) const
709 {
710     auto* renderer = element.renderer();
711     if (renderer && renderer->isComposited()) {
712         auto* compositedRenderer = downcast<RenderBoxModelObject>(renderer);
713         if (auto* graphicsLayer = compositedRenderer->layer()->backing()->graphicsLayer())
714             return graphicsLayer->acceleratedAnimationsForTesting();
715     }
716     return { };
717 }
718
719 unsigned DocumentTimeline::numberOfAnimationTimelineInvalidationsForTesting() const
720 {
721     return m_numberOfAnimationTimelineInvalidationsForTesting;
722 }
723
724 } // namespace WebCore