diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/page/animation/CSSAnimationController.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/page/animation/CSSAnimationController.cpp')
-rw-r--r-- | Source/WebCore/page/animation/CSSAnimationController.cpp | 805 |
1 files changed, 805 insertions, 0 deletions
diff --git a/Source/WebCore/page/animation/CSSAnimationController.cpp b/Source/WebCore/page/animation/CSSAnimationController.cpp new file mode 100644 index 000000000..b71ed2e43 --- /dev/null +++ b/Source/WebCore/page/animation/CSSAnimationController.cpp @@ -0,0 +1,805 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CSSAnimationController.h" + +#include "AnimationBase.h" +#include "AnimationEvent.h" +#include "CSSAnimationControllerPrivate.h" +#include "CSSPropertyAnimation.h" +#include "CSSPropertyParser.h" +#include "CompositeAnimation.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "Logging.h" +#include "PseudoElement.h" +#include "RenderView.h" +#include "TransitionEvent.h" +#include "WebKitAnimationEvent.h" +#include "WebKitTransitionEvent.h" +#include <wtf/CurrentTime.h> + +namespace WebCore { + +// Allow a little more than 60fps to make sure we can at least hit that frame rate. +static const double cAnimationTimerDelay = 0.015; +static const double cBeginAnimationUpdateTimeNotSet = -1; + +class AnimationPrivateUpdateBlock { +public: + AnimationPrivateUpdateBlock(CSSAnimationControllerPrivate& animationController) + : m_animationController(animationController) + { + m_animationController.beginAnimationUpdate(); + } + + ~AnimationPrivateUpdateBlock() + { + m_animationController.endAnimationUpdate(); + } + + CSSAnimationControllerPrivate& m_animationController; +}; + +CSSAnimationControllerPrivate::CSSAnimationControllerPrivate(Frame& frame) + : m_animationTimer(*this, &CSSAnimationControllerPrivate::animationTimerFired) + , m_updateStyleIfNeededDispatcher(*this, &CSSAnimationControllerPrivate::updateStyleIfNeededDispatcherFired) + , m_frame(frame) + , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) + , m_beginAnimationUpdateCount(0) + , m_waitingForAsyncStartNotification(false) + , m_isSuspended(false) + , m_allowsNewAnimationsWhileSuspended(false) +{ +} + +CSSAnimationControllerPrivate::~CSSAnimationControllerPrivate() +{ +} + +CompositeAnimation& CSSAnimationControllerPrivate::ensureCompositeAnimation(RenderElement& renderer) +{ + auto result = m_compositeAnimations.add(&renderer, nullptr); + if (result.isNewEntry) { + result.iterator->value = CompositeAnimation::create(*this); + renderer.setIsCSSAnimating(true); + } + + if (animationsAreSuspendedForDocument(&renderer.document())) + result.iterator->value->suspendAnimations(); + + return *result.iterator->value; +} + +bool CSSAnimationControllerPrivate::clear(RenderElement& renderer) +{ + LOG(Animations, "CSSAnimationControllerPrivate %p clear: %p", this, &renderer); + + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + + Element* element = renderer.element(); + + m_eventsToDispatch.removeAllMatching([element] (const EventToDispatch& info) { + return info.element.ptr() == element; + }); + + m_elementChangesToDispatch.removeAllMatching([element](auto& currentElement) { + return currentElement.ptr() == element; + }); + + // Return false if we didn't do anything OR we are suspended (so we don't try to + // do a invalidateStyleForSubtree() when suspended). + RefPtr<CompositeAnimation> animation = m_compositeAnimations.take(&renderer); + ASSERT(animation); + renderer.setIsCSSAnimating(false); + animation->clearRenderer(); + return animation->isSuspended(); +} + +double CSSAnimationControllerPrivate::updateAnimations(SetChanged callSetChanged/* = DoNotCallSetChanged*/) +{ + AnimationPrivateUpdateBlock updateBlock(*this); + double timeToNextService = -1; + bool calledSetChanged = false; + + for (auto& compositeAnimation : m_compositeAnimations) { + CompositeAnimation& animation = *compositeAnimation.value; + if (!animation.isSuspended() && animation.hasAnimations()) { + double t = animation.timeToNextService(); + if (t != -1 && (t < timeToNextService || timeToNextService == -1)) + timeToNextService = t; + if (!timeToNextService) { + if (callSetChanged != CallSetChanged) + break; + Element* element = compositeAnimation.key->element(); + ASSERT(element); + ASSERT(element->document().pageCacheState() == Document::NotInPageCache); + element->invalidateStyleAndLayerComposition(); + calledSetChanged = true; + } + } + } + + if (calledSetChanged) + m_frame.document()->updateStyleIfNeeded(); + + return timeToNextService; +} + +void CSSAnimationControllerPrivate::updateAnimationTimerForRenderer(RenderElement& renderer) +{ + double timeToNextService = 0; + + const CompositeAnimation* compositeAnimation = m_compositeAnimations.get(&renderer); + if (!compositeAnimation->isSuspended() && compositeAnimation->hasAnimations()) + timeToNextService = compositeAnimation->timeToNextService(); + + if (m_animationTimer.isActive() && (m_animationTimer.repeatInterval() || m_animationTimer.nextFireInterval() <= timeToNextService)) + return; + + m_animationTimer.startOneShot(timeToNextService); +} + +void CSSAnimationControllerPrivate::updateAnimationTimer(SetChanged callSetChanged/* = DoNotCallSetChanged*/) +{ + double timeToNextService = updateAnimations(callSetChanged); + + LOG(Animations, "updateAnimationTimer: timeToNextService is %.2f", timeToNextService); + + // If we want service immediately, we start a repeating timer to reduce the overhead of starting + if (!timeToNextService) { + if (!m_animationTimer.isActive() || !m_animationTimer.repeatInterval()) + m_animationTimer.startRepeating(cAnimationTimerDelay); + return; + } + + // If we don't need service, we want to make sure the timer is no longer running + if (timeToNextService < 0) { + if (m_animationTimer.isActive()) + m_animationTimer.stop(); + return; + } + + // Otherwise, we want to start a one-shot timer so we get here again + m_animationTimer.startOneShot(timeToNextService); +} + +void CSSAnimationControllerPrivate::updateStyleIfNeededDispatcherFired() +{ + fireEventsAndUpdateStyle(); +} + +void CSSAnimationControllerPrivate::fireEventsAndUpdateStyle() +{ + // Protect the frame from getting destroyed in the event handler + Ref<Frame> protector(m_frame); + + bool updateStyle = !m_eventsToDispatch.isEmpty() || !m_elementChangesToDispatch.isEmpty(); + + // fire all the events + Vector<EventToDispatch> eventsToDispatch = WTFMove(m_eventsToDispatch); + for (auto& event : eventsToDispatch) { + Element& element = event.element; + if (event.eventType == eventNames().transitionendEvent) + element.dispatchEvent(TransitionEvent::create(event.eventType, event.name, event.elapsedTime, PseudoElement::pseudoElementNameForEvents(element.pseudoId()))); + else + element.dispatchEvent(AnimationEvent::create(event.eventType, event.name, event.elapsedTime)); + } + + for (auto& change : m_elementChangesToDispatch) + change->invalidateStyleAndLayerComposition(); + + m_elementChangesToDispatch.clear(); + + if (updateStyle) + m_frame.document()->updateStyleIfNeeded(); +} + +void CSSAnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() +{ + if (!m_updateStyleIfNeededDispatcher.isActive()) + m_updateStyleIfNeededDispatcher.startOneShot(0); +} + +void CSSAnimationControllerPrivate::addEventToDispatch(Element& element, const AtomicString& eventType, const String& name, double elapsedTime) +{ + m_eventsToDispatch.append({ element, eventType, name, elapsedTime }); + startUpdateStyleIfNeededDispatcher(); +} + +void CSSAnimationControllerPrivate::addElementChangeToDispatch(Element& element) +{ + m_elementChangesToDispatch.append(element); + ASSERT(m_elementChangesToDispatch.last()->document().pageCacheState() == Document::NotInPageCache); + startUpdateStyleIfNeededDispatcher(); +} + +void CSSAnimationControllerPrivate::animationFrameCallbackFired() +{ + double timeToNextService = updateAnimations(CallSetChanged); + + if (timeToNextService >= 0) + m_frame.document()->view()->scheduleAnimation(); +} + +void CSSAnimationControllerPrivate::animationTimerFired() +{ + // We need to keep the frame alive, since it owns us. + Ref<Frame> protector(m_frame); + + // The animation timer might fire before the layout timer, in + // which case we might create some animations with incorrect + // values if we don't layout first. + if (m_requiresLayout) { + if (auto* frameView = m_frame.document()->view()) { + if (frameView->needsLayout()) + frameView->forceLayout(); + } + m_requiresLayout = false; + } + + // Make sure animationUpdateTime is updated, so that it is current even if no + // styleChange has happened (e.g. accelerated animations) + AnimationPrivateUpdateBlock updateBlock(*this); + + // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate + // updateStyleIfNeeded. It will then call back to us with new information. + updateAnimationTimer(CallSetChanged); + + // Fire events right away, to avoid a flash of unanimated style after an animation completes, and before + // the 'end' event fires. + fireEventsAndUpdateStyle(); +} + +bool CSSAnimationControllerPrivate::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + const CompositeAnimation& animation = *m_compositeAnimations.get(&renderer); + return animation.isAnimatingProperty(property, false, runningState); +} + +bool CSSAnimationControllerPrivate::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + const CompositeAnimation& animation = *m_compositeAnimations.get(&renderer); + return animation.isAnimatingProperty(property, true, runningState); +} + +void CSSAnimationControllerPrivate::suspendAnimations() +{ + if (isSuspended()) + return; + + suspendAnimationsForDocument(m_frame.document()); + + // Traverse subframes + for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) + child->animation().suspendAnimations(); + + m_isSuspended = true; +} + +void CSSAnimationControllerPrivate::resumeAnimations() +{ + if (!isSuspended()) + return; + + resumeAnimationsForDocument(m_frame.document()); + + // Traverse subframes + for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) + child->animation().resumeAnimations(); + + m_isSuspended = false; +} + +bool CSSAnimationControllerPrivate::animationsAreSuspendedForDocument(Document* document) +{ + return isSuspended() || m_suspendedDocuments.contains(document); +} + +void CSSAnimationControllerPrivate::detachFromDocument(Document* document) +{ + m_suspendedDocuments.remove(document); +} + +void CSSAnimationControllerPrivate::suspendAnimationsForDocument(Document* document) +{ + if (animationsAreSuspendedForDocument(document)) + return; + + m_suspendedDocuments.add(document); + + AnimationPrivateUpdateBlock updateBlock(*this); + + for (auto& animation : m_compositeAnimations) { + if (&animation.key->document() == document) + animation.value->suspendAnimations(); + } + + updateAnimationTimer(); +} + +void CSSAnimationControllerPrivate::resumeAnimationsForDocument(Document* document) +{ + if (!animationsAreSuspendedForDocument(document)) + return; + + detachFromDocument(document); + + AnimationPrivateUpdateBlock updateBlock(*this); + + for (auto& animation : m_compositeAnimations) { + if (&animation.key->document() == document) + animation.value->resumeAnimations(); + } + + updateAnimationTimer(); +} + +void CSSAnimationControllerPrivate::startAnimationsIfNotSuspended(Document* document) +{ + if (!animationsAreSuspendedForDocument(document) || allowsNewAnimationsWhileSuspended()) + resumeAnimationsForDocument(document); +} + +void CSSAnimationControllerPrivate::setAllowsNewAnimationsWhileSuspended(bool allowed) +{ + m_allowsNewAnimationsWhileSuspended = allowed; +} + +bool CSSAnimationControllerPrivate::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) +{ + if (!renderer) + return false; + + CompositeAnimation& compositeAnimation = ensureCompositeAnimation(*renderer); + if (compositeAnimation.pauseAnimationAtTime(name, t)) { + renderer->element()->invalidateStyleAndLayerComposition(); + startUpdateStyleIfNeededDispatcher(); + return true; + } + + return false; +} + +bool CSSAnimationControllerPrivate::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) +{ + if (!renderer) + return false; + + CompositeAnimation& compositeAnimation = ensureCompositeAnimation(*renderer); + if (compositeAnimation.pauseTransitionAtTime(cssPropertyID(property), t)) { + renderer->element()->invalidateStyleAndLayerComposition(); + startUpdateStyleIfNeededDispatcher(); + return true; + } + + return false; +} + +double CSSAnimationControllerPrivate::beginAnimationUpdateTime() +{ + ASSERT(m_beginAnimationUpdateCount); + if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) + m_beginAnimationUpdateTime = monotonicallyIncreasingTime(); + + return m_beginAnimationUpdateTime; +} + +void CSSAnimationControllerPrivate::beginAnimationUpdate() +{ + if (!m_beginAnimationUpdateCount) + setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); + ++m_beginAnimationUpdateCount; +} + +void CSSAnimationControllerPrivate::endAnimationUpdate() +{ + ASSERT(m_beginAnimationUpdateCount > 0); + if (m_beginAnimationUpdateCount == 1) { + styleAvailable(); + if (!m_waitingForAsyncStartNotification) + startTimeResponse(beginAnimationUpdateTime()); + } + --m_beginAnimationUpdateCount; +} + +void CSSAnimationControllerPrivate::receivedStartTimeResponse(double time) +{ + LOG(Animations, "CSSAnimationControllerPrivate %p receivedStartTimeResponse %f", this, time); + + m_waitingForAsyncStartNotification = false; + startTimeResponse(time); +} + +std::unique_ptr<RenderStyle> CSSAnimationControllerPrivate::getAnimatedStyleForRenderer(RenderElement& renderer) +{ + AnimationPrivateUpdateBlock animationUpdateBlock(*this); + + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + const CompositeAnimation& rendererAnimations = *m_compositeAnimations.get(&renderer); + std::unique_ptr<RenderStyle> animatingStyle = rendererAnimations.getAnimatedStyle(); + if (!animatingStyle) + animatingStyle = RenderStyle::clonePtr(renderer.style()); + + return animatingStyle; +} + +bool CSSAnimationControllerPrivate::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const +{ + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + + const CompositeAnimation& rendererAnimations = *m_compositeAnimations.get(&renderer); + if (!rendererAnimations.isAnimatingProperty(CSSPropertyTransform, false, AnimationBase::Running | AnimationBase::Paused)) + return true; + + return rendererAnimations.computeExtentOfTransformAnimation(bounds); +} + +unsigned CSSAnimationControllerPrivate::numberOfActiveAnimations(Document* document) const +{ + unsigned count = 0; + + for (auto& animation : m_compositeAnimations) { + if (&animation.key->document() == document) + count += animation.value->numberOfActiveAnimations(); + } + + return count; +} + +void CSSAnimationControllerPrivate::addToAnimationsWaitingForStyle(AnimationBase* animation) +{ + // Make sure this animation is not in the start time waiters + m_animationsWaitingForStartTimeResponse.remove(animation); + + m_animationsWaitingForStyle.add(animation); +} + +void CSSAnimationControllerPrivate::removeFromAnimationsWaitingForStyle(AnimationBase* animationToRemove) +{ + m_animationsWaitingForStyle.remove(animationToRemove); +} + +void CSSAnimationControllerPrivate::styleAvailable() +{ + // Go through list of waiters and send them on their way + for (const auto& waitingAnimation : m_animationsWaitingForStyle) + waitingAnimation->styleAvailable(); + + m_animationsWaitingForStyle.clear(); +} + +void CSSAnimationControllerPrivate::addToAnimationsWaitingForStartTimeResponse(AnimationBase* animation, bool willGetResponse) +{ + // If willGetResponse is true, it means this animation is actually waiting for a response + // (which will come in as a call to notifyAnimationStarted()). + // In that case we don't need to add it to this list. We just set a waitingForAResponse flag + // which says we are waiting for the response. If willGetResponse is false, this animation + // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for + // another animation to which it will sync. + // + // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is + // true. If so, we just return and will do our work when the first notifyXXXStarted() call + // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will + // do our work right away. In both cases we call the onAnimationStartResponse() method + // on each animation. In the first case we send in the time we got from notifyXXXStarted(). + // In the second case, we just pass in the beginAnimationUpdateTime(). + // + // This will synchronize all software and accelerated animations started in the same + // updateStyleIfNeeded cycle. + // + + if (willGetResponse) + m_waitingForAsyncStartNotification = true; + + m_animationsWaitingForStartTimeResponse.add(animation); +} + +void CSSAnimationControllerPrivate::removeFromAnimationsWaitingForStartTimeResponse(AnimationBase* animationToRemove) +{ + m_animationsWaitingForStartTimeResponse.remove(animationToRemove); + + if (m_animationsWaitingForStartTimeResponse.isEmpty()) + m_waitingForAsyncStartNotification = false; +} + +void CSSAnimationControllerPrivate::startTimeResponse(double time) +{ + // Go through list of waiters and send them on their way + + for (const auto& animation : m_animationsWaitingForStartTimeResponse) + animation->onAnimationStartResponse(time); + + m_animationsWaitingForStartTimeResponse.clear(); + m_waitingForAsyncStartNotification = false; +} + +void CSSAnimationControllerPrivate::animationWillBeRemoved(AnimationBase* animation) +{ + LOG(Animations, "CSSAnimationControllerPrivate %p animationWillBeRemoved: %p", this, animation); + + removeFromAnimationsWaitingForStyle(animation); + removeFromAnimationsWaitingForStartTimeResponse(animation); +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + removeFromAnimationsDependentOnScroll(animation); +#endif + + bool anyAnimationsWaitingForAsyncStart = false; + for (auto& animation : m_animationsWaitingForStartTimeResponse) { + if (animation->waitingForStartTime() && animation->isAccelerated()) { + anyAnimationsWaitingForAsyncStart = true; + break; + } + } + + if (!anyAnimationsWaitingForAsyncStart) + m_waitingForAsyncStartNotification = false; +} + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) +void CSSAnimationControllerPrivate::addToAnimationsDependentOnScroll(AnimationBase* animation) +{ + m_animationsDependentOnScroll.add(animation); +} + +void CSSAnimationControllerPrivate::removeFromAnimationsDependentOnScroll(AnimationBase* animation) +{ + m_animationsDependentOnScroll.remove(animation); +} + +void CSSAnimationControllerPrivate::scrollWasUpdated() +{ + auto* view = m_frame.view(); + if (!view || !wantsScrollUpdates()) + return; + + m_scrollPosition = view->scrollPositionForFixedPosition().y().toFloat(); + + // FIXME: This is updating all the animations, rather than just the ones + // that are dependent on scroll. We to go from our AnimationBase to its CompositeAnimation + // so we can execute code similar to updateAnimations. + // https://bugs.webkit.org/show_bug.cgi?id=144170 + updateAnimations(CallSetChanged); +} +#endif + +CSSAnimationController::CSSAnimationController(Frame& frame) + : m_data(std::make_unique<CSSAnimationControllerPrivate>(frame)) +{ +} + +CSSAnimationController::~CSSAnimationController() +{ +} + +void CSSAnimationController::cancelAnimations(RenderElement& renderer) +{ + if (!renderer.isCSSAnimating()) + return; + + if (!m_data->clear(renderer)) + return; + + Element* element = renderer.element(); + if (!element || element->document().renderTreeBeingDestroyed()) + return; + ASSERT(element->document().pageCacheState() == Document::NotInPageCache); + element->invalidateStyleAndLayerComposition(); +} + +bool CSSAnimationController::updateAnimations(RenderElement& renderer, const RenderStyle& newStyle, std::unique_ptr<RenderStyle>& animatedStyle) +{ + auto* oldStyle = renderer.hasInitializedStyle() ? &renderer.style() : nullptr; + if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle.animations() && !newStyle.transitions())) + return false; + + if (renderer.document().pageCacheState() != Document::NotInPageCache) + return false; + + // Don't run transitions when printing. + if (renderer.view().printing()) + return false; + + // Fetch our current set of implicit animations from a hashtable. We then compare them + // against the animations in the style and make sure we're in sync. If destination values + // have changed, we reset the animation. We then do a blend to get new values and we return + // a new style. + + // We don't support anonymous pseudo elements like :first-line or :first-letter. + ASSERT(renderer.element()); + + CompositeAnimation& rendererAnimations = m_data->ensureCompositeAnimation(renderer); + bool animationStateChanged = rendererAnimations.animate(renderer, oldStyle, newStyle, animatedStyle); + + if (renderer.parent() || newStyle.animations() || (oldStyle && oldStyle->animations())) { + auto& frameView = renderer.view().frameView(); + if (rendererAnimations.hasAnimationThatDependsOnLayout()) + m_data->setRequiresLayout(); + m_data->updateAnimationTimerForRenderer(renderer); + frameView.scheduleAnimation(); + } + + return animationStateChanged; +} + +std::unique_ptr<RenderStyle> CSSAnimationController::getAnimatedStyleForRenderer(RenderElement& renderer) +{ + if (!renderer.isCSSAnimating()) + return RenderStyle::clonePtr(renderer.style()); + return m_data->getAnimatedStyleForRenderer(renderer); +} + +bool CSSAnimationController::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const +{ + if (!renderer.isCSSAnimating()) + return true; + + return m_data->computeExtentOfAnimation(renderer, bounds); +} + +void CSSAnimationController::notifyAnimationStarted(RenderElement& renderer, double startTime) +{ + LOG(Animations, "CSSAnimationController %p notifyAnimationStarted on renderer %p, time=%f", this, &renderer, startTime); + UNUSED_PARAM(renderer); + + AnimationUpdateBlock animationUpdateBlock(this); + m_data->receivedStartTimeResponse(startTime); +} + +bool CSSAnimationController::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) +{ + AnimationUpdateBlock animationUpdateBlock(this); + return m_data->pauseAnimationAtTime(renderer, name, t); +} + +unsigned CSSAnimationController::numberOfActiveAnimations(Document* document) const +{ + return m_data->numberOfActiveAnimations(document); +} + +bool CSSAnimationController::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) +{ + AnimationUpdateBlock animationUpdateBlock(this); + return m_data->pauseTransitionAtTime(renderer, property, t); +} + +bool CSSAnimationController::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + return renderer.isCSSAnimating() && m_data->isRunningAnimationOnRenderer(renderer, property, runningState); +} + +bool CSSAnimationController::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + return renderer.isCSSAnimating() && m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, runningState); +} + +bool CSSAnimationController::isSuspended() const +{ + return m_data->isSuspended(); +} + +void CSSAnimationController::suspendAnimations() +{ + LOG(Animations, "controller is suspending animations"); + m_data->suspendAnimations(); +} + +void CSSAnimationController::resumeAnimations() +{ + LOG(Animations, "controller is resuming animations"); + m_data->resumeAnimations(); +} + +bool CSSAnimationController::allowsNewAnimationsWhileSuspended() const +{ + return m_data->allowsNewAnimationsWhileSuspended(); +} + +void CSSAnimationController::setAllowsNewAnimationsWhileSuspended(bool allowed) +{ + m_data->setAllowsNewAnimationsWhileSuspended(allowed); +} + +void CSSAnimationController::serviceAnimations() +{ + m_data->animationFrameCallbackFired(); +} + +bool CSSAnimationController::animationsAreSuspendedForDocument(Document* document) +{ + return m_data->animationsAreSuspendedForDocument(document); +} + +void CSSAnimationController::detachFromDocument(Document* document) +{ + return m_data->detachFromDocument(document); +} + +void CSSAnimationController::suspendAnimationsForDocument(Document* document) +{ + LOG(Animations, "suspending animations for document %p", document); + m_data->suspendAnimationsForDocument(document); +} + +void CSSAnimationController::resumeAnimationsForDocument(Document* document) +{ + LOG(Animations, "resuming animations for document %p", document); + AnimationUpdateBlock animationUpdateBlock(this); + m_data->resumeAnimationsForDocument(document); +} + +void CSSAnimationController::startAnimationsIfNotSuspended(Document* document) +{ + LOG(Animations, "animations may start for document %p", document); + + AnimationUpdateBlock animationUpdateBlock(this); + m_data->startAnimationsIfNotSuspended(document); +} + +void CSSAnimationController::beginAnimationUpdate() +{ + m_data->beginAnimationUpdate(); +} + +void CSSAnimationController::endAnimationUpdate() +{ + m_data->endAnimationUpdate(); +} + +bool CSSAnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property) +{ + return CSSPropertyAnimation::animationOfPropertyIsAccelerated(property); +} + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) +bool CSSAnimationController::wantsScrollUpdates() const +{ + return m_data->wantsScrollUpdates(); +} + +void CSSAnimationController::scrollWasUpdated() +{ + m_data->scrollWasUpdated(); +} +#endif + +bool CSSAnimationController::hasAnimations() const +{ + return m_data->hasAnimations(); +} + +} // namespace WebCore |