2 * Copyright (C) 2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "DocumentTimeline.h"
29 #include "AnimationPlaybackEvent.h"
31 #include "ChromeClient.h"
32 #include "DOMWindow.h"
33 #include "DeclarativeAnimation.h"
34 #include "DisplayRefreshMonitor.h"
35 #include "DisplayRefreshMonitorManager.h"
37 #include "KeyframeEffect.h"
39 #include "RenderElement.h"
41 static const Seconds defaultAnimationInterval { 15_ms };
42 static const Seconds throttledAnimationInterval { 30_ms };
46 Ref<DocumentTimeline> DocumentTimeline::create(Document& document, PlatformDisplayID displayID)
48 return adoptRef(*new DocumentTimeline(document, displayID));
51 DocumentTimeline::DocumentTimeline(Document& document, PlatformDisplayID displayID)
52 : AnimationTimeline(DocumentTimelineClass)
53 , m_document(&document)
54 , m_animationScheduleTimer(*this, &DocumentTimeline::animationScheduleTimerFired)
55 #if !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
56 , m_animationResolutionTimer(*this, &DocumentTimeline::animationResolutionTimerFired)
59 windowScreenDidChange(displayID);
62 DocumentTimeline::~DocumentTimeline()
64 m_invalidationTaskQueue.close();
65 m_eventDispatchTaskQueue.close();
68 void DocumentTimeline::detachFromDocument()
73 void DocumentTimeline::updateThrottlingState()
75 m_needsUpdateAnimationSchedule = false;
76 timingModelDidChange();
79 Seconds DocumentTimeline::animationInterval() const
81 if (!m_document || !m_document->page())
82 return Seconds::infinity();
83 return m_document->page()->isLowPowerModeEnabled() ? throttledAnimationInterval : defaultAnimationInterval;
86 void DocumentTimeline::suspendAnimations()
88 if (animationsAreSuspended())
93 m_invalidationTaskQueue.cancelAllTasks();
94 if (m_animationScheduleTimer.isActive())
95 m_animationScheduleTimer.stop();
97 for (const auto& animation : animations())
98 animation->setSuspended(true);
100 applyPendingAcceleratedAnimations();
103 void DocumentTimeline::resumeAnimations()
105 if (!animationsAreSuspended())
108 m_isSuspended = false;
110 for (const auto& animation : animations())
111 animation->setSuspended(false);
113 m_needsUpdateAnimationSchedule = false;
114 timingModelDidChange();
117 bool DocumentTimeline::animationsAreSuspended()
119 return m_isSuspended;
122 unsigned DocumentTimeline::numberOfActiveAnimationsForTesting() const
125 for (const auto& animation : animations()) {
126 if (!animation->isSuspended())
132 std::optional<Seconds> DocumentTimeline::currentTime()
134 if (m_paused || m_isSuspended || !m_document || !m_document->domWindow())
135 return AnimationTimeline::currentTime();
137 if (!m_cachedCurrentTime) {
138 m_cachedCurrentTime = Seconds(m_document->domWindow()->nowTimestamp());
139 scheduleInvalidationTaskIfNeeded();
141 return m_cachedCurrentTime;
144 void DocumentTimeline::pause()
149 void DocumentTimeline::timingModelDidChange()
151 if (m_needsUpdateAnimationSchedule || m_isSuspended)
154 m_needsUpdateAnimationSchedule = true;
156 // We know that we will resolve animations again, so we can cancel the timer right away.
157 if (m_animationScheduleTimer.isActive())
158 m_animationScheduleTimer.stop();
160 scheduleInvalidationTaskIfNeeded();
163 void DocumentTimeline::scheduleInvalidationTaskIfNeeded()
165 if (m_invalidationTaskQueue.hasPendingTasks())
168 m_invalidationTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performInvalidationTask, this));
171 void DocumentTimeline::performInvalidationTask()
173 // Now that the timing model has changed we can see if there are DOM events to dispatch for declarative animations.
174 for (auto& animation : animations()) {
175 if (is<DeclarativeAnimation>(animation))
176 downcast<DeclarativeAnimation>(*animation).invalidateDOMEvents();
179 updateAnimationSchedule();
180 m_cachedCurrentTime = std::nullopt;
183 void DocumentTimeline::updateAnimationSchedule()
185 if (!m_needsUpdateAnimationSchedule)
188 m_needsUpdateAnimationSchedule = false;
190 if (!m_acceleratedAnimationsPendingRunningStateChange.isEmpty()) {
191 scheduleAnimationResolution();
195 Seconds scheduleDelay = Seconds::infinity();
197 for (const auto& animation : animations()) {
198 auto animationTimeToNextRequiredTick = animation->timeToNextRequiredTick();
199 if (animationTimeToNextRequiredTick < animationInterval()) {
200 scheduleAnimationResolution();
203 scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
206 if (scheduleDelay < Seconds::infinity())
207 m_animationScheduleTimer.startOneShot(scheduleDelay);
210 void DocumentTimeline::animationScheduleTimerFired()
212 scheduleAnimationResolution();
215 void DocumentTimeline::scheduleAnimationResolution()
217 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
218 DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
220 // FIXME: We need to use the same logic as ScriptedAnimationController here,
221 // which will be addressed by the refactor tracked by webkit.org/b/179293.
222 m_animationResolutionTimer.startOneShot(animationInterval());
226 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
227 void DocumentTimeline::displayRefreshFired()
229 void DocumentTimeline::animationResolutionTimerFired()
235 void DocumentTimeline::updateAnimations()
237 if (m_document && hasElementAnimations()) {
238 for (const auto& elementToAnimationsMapItem : elementToAnimationsMap())
239 elementToAnimationsMapItem.key->invalidateStyleAndLayerComposition();
240 for (const auto& elementToCSSAnimationsMapItem : elementToCSSAnimationsMap())
241 elementToCSSAnimationsMapItem.key->invalidateStyleAndLayerComposition();
242 for (const auto& elementToCSSTransitionsMapItem : elementToCSSTransitionsMap())
243 elementToCSSTransitionsMapItem.key->invalidateStyleAndLayerComposition();
244 m_document->updateStyleIfNeeded();
247 applyPendingAcceleratedAnimations();
249 // Time has advanced, the timing model requires invalidation now.
250 timingModelDidChange();
253 bool DocumentTimeline::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const
255 if (!renderer.element())
258 KeyframeEffectReadOnly* matchingEffect = nullptr;
259 for (const auto& animation : animationsForElement(*renderer.element())) {
260 auto* effect = animation->effect();
261 if (is<KeyframeEffectReadOnly>(effect)) {
262 auto* keyframeEffect = downcast<KeyframeEffectReadOnly>(effect);
263 if (keyframeEffect->animatedProperties().contains(CSSPropertyTransform))
264 matchingEffect = downcast<KeyframeEffectReadOnly>(effect);
269 return matchingEffect->computeExtentOfTransformAnimation(bounds);
274 bool DocumentTimeline::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
276 if (!renderer.element())
279 for (const auto& animation : animationsForElement(*renderer.element())) {
280 auto playState = animation->playState();
281 if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
283 auto* effect = animation->effect();
284 if (is<KeyframeEffectReadOnly>(effect) && downcast<KeyframeEffectReadOnly>(effect)->animatedProperties().contains(property))
291 bool DocumentTimeline::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
293 if (!renderer.element())
296 for (const auto& animation : animationsForElement(*renderer.element())) {
297 auto playState = animation->playState();
298 if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
300 auto* effect = animation->effect();
301 if (is<KeyframeEffectReadOnly>(effect)) {
302 auto* keyframeEffect = downcast<KeyframeEffectReadOnly>(effect);
303 if (keyframeEffect->isRunningAccelerated() && keyframeEffect->animatedProperties().contains(property))
311 std::unique_ptr<RenderStyle> DocumentTimeline::animatedStyleForRenderer(RenderElement& renderer)
313 std::unique_ptr<RenderStyle> result;
315 if (auto* element = renderer.element()) {
316 for (const auto& animation : animationsForElement(*element)) {
317 if (is<KeyframeEffectReadOnly>(animation->effect()))
318 downcast<KeyframeEffectReadOnly>(animation->effect())->getAnimatedStyle(result);
323 result = RenderStyle::clonePtr(renderer.style());
328 void DocumentTimeline::animationAcceleratedRunningStateDidChange(WebAnimation& animation)
330 m_acceleratedAnimationsPendingRunningStateChange.add(&animation);
333 void DocumentTimeline::applyPendingAcceleratedAnimations()
335 for (auto& animation : m_acceleratedAnimationsPendingRunningStateChange)
336 animation->applyPendingAcceleratedActions();
337 m_acceleratedAnimationsPendingRunningStateChange.clear();
340 bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element)
342 // FIXME: This will let animations run using hardware compositing even if later in the active
343 // span of the current animations a new animation should require hardware compositing to be
344 // disabled (webkit.org/b/179974).
345 auto animations = animationsForElement(element);
346 for (const auto& animation : animations) {
347 if (is<KeyframeEffectReadOnly>(animation->effect()) && !downcast<KeyframeEffectReadOnly>(animation->effect())->isRunningAccelerated())
350 return !animations.isEmpty();
353 void DocumentTimeline::enqueueAnimationPlaybackEvent(AnimationPlaybackEvent& event)
355 m_pendingAnimationEvents.append(event);
357 if (!m_eventDispatchTaskQueue.hasPendingTasks())
358 m_eventDispatchTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performEventDispatchTask, this));
361 static inline bool compareAnimationPlaybackEvents(const Ref<WebCore::AnimationPlaybackEvent>& lhs, const Ref<WebCore::AnimationPlaybackEvent>& rhs)
363 // Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort before events scheduled to occur later
364 // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
365 if (lhs->timelineTime() && !rhs->timelineTime())
367 if (!lhs->timelineTime() && rhs->timelineTime())
369 if (!lhs->timelineTime() && !rhs->timelineTime())
371 return lhs->timelineTime().value() < rhs->timelineTime().value();
374 void DocumentTimeline::performEventDispatchTask()
376 if (m_pendingAnimationEvents.isEmpty())
379 auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
381 std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), compareAnimationPlaybackEvents);
382 for (auto& pendingEvent : pendingAnimationEvents)
383 pendingEvent->target()->dispatchEvent(pendingEvent);
386 void DocumentTimeline::windowScreenDidChange(PlatformDisplayID displayID)
388 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
389 DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
391 UNUSED_PARAM(displayID);
395 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
396 RefPtr<DisplayRefreshMonitor> DocumentTimeline::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
398 if (!m_document || !m_document->page())
401 if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
404 return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);
408 } // namespace WebCore