1/* 2 * Copyright (C) 2013 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "config.h" 32#include "core/animation/Animation.h" 33 34#include "bindings/core/v8/Dictionary.h" 35#include "bindings/core/v8/ExceptionState.h" 36#include "core/animation/ActiveAnimations.h" 37#include "core/animation/AnimationPlayer.h" 38#include "core/animation/AnimationTimeline.h" 39#include "core/animation/CompositorAnimations.h" 40#include "core/animation/Interpolation.h" 41#include "core/animation/KeyframeEffectModel.h" 42#include "core/dom/Element.h" 43#include "core/frame/UseCounter.h" 44#include "core/rendering/RenderLayer.h" 45 46namespace blink { 47 48PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtrWillBeRawPtr<EventDelegate> eventDelegate) 49{ 50 return adoptRefWillBeNoop(new Animation(target, effect, timing, priority, eventDelegate)); 51} 52 53PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Dictionary& timingInputDictionary) 54{ 55 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); 56 return create(element, effect, TimingInput::convert(timingInputDictionary)); 57} 58PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, PassRefPtrWillBeRawPtr<AnimationEffect> effect, double duration) 59{ 60 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); 61 return create(element, effect, TimingInput::convert(duration)); 62} 63PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, PassRefPtrWillBeRawPtr<AnimationEffect> effect) 64{ 65 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); 66 return create(element, effect, Timing()); 67} 68PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, const Dictionary& timingInputDictionary, ExceptionState& exceptionState) 69{ 70 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); 71 if (element) 72 UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectObjectTiming); 73 return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), TimingInput::convert(timingInputDictionary)); 74} 75PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, double duration, ExceptionState& exceptionState) 76{ 77 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); 78 if (element) 79 UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectDoubleTiming); 80 return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), TimingInput::convert(duration)); 81} 82PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState) 83{ 84 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled()); 85 if (element) 86 UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectNoTiming); 87 return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), Timing()); 88} 89 90Animation::Animation(Element* target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtrWillBeRawPtr<EventDelegate> eventDelegate) 91 : AnimationNode(timing, eventDelegate) 92 , m_target(target) 93 , m_effect(effect) 94 , m_sampledEffect(nullptr) 95 , m_priority(priority) 96{ 97#if !ENABLE(OILPAN) 98 if (m_target) 99 m_target->ensureActiveAnimations().addAnimation(this); 100#endif 101} 102 103Animation::~Animation() 104{ 105#if !ENABLE(OILPAN) 106 if (m_target) 107 m_target->activeAnimations()->notifyAnimationDestroyed(this); 108#endif 109} 110 111void Animation::attach(AnimationPlayer* player) 112{ 113 if (m_target) { 114 m_target->ensureActiveAnimations().players().add(player); 115 m_target->setNeedsAnimationStyleRecalc(); 116 } 117 AnimationNode::attach(player); 118} 119 120void Animation::detach() 121{ 122 if (m_target) 123 m_target->activeAnimations()->players().remove(player()); 124 if (m_sampledEffect) 125 clearEffects(); 126 AnimationNode::detach(); 127} 128 129void Animation::specifiedTimingChanged() 130{ 131 if (player()) { 132 // FIXME: Needs to consider groups when added. 133 ASSERT(player()->source() == this); 134 player()->setCompositorPending(true); 135 } 136} 137 138static AnimationStack& ensureAnimationStack(Element* element) 139{ 140 return element->ensureActiveAnimations().defaultStack(); 141} 142 143void Animation::applyEffects() 144{ 145 ASSERT(isInEffect()); 146 ASSERT(player()); 147 if (!m_target || !m_effect) 148 return; 149 150 double iteration = currentIteration(); 151 ASSERT(iteration >= 0); 152 // FIXME: Handle iteration values which overflow int. 153 OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > interpolations = m_effect->sample(static_cast<int>(iteration), timeFraction(), iterationDuration()); 154 if (m_sampledEffect) { 155 m_sampledEffect->setInterpolations(interpolations.release()); 156 } else if (!interpolations->isEmpty()) { 157 OwnPtrWillBeRawPtr<SampledEffect> sampledEffect = SampledEffect::create(this, interpolations.release()); 158 m_sampledEffect = sampledEffect.get(); 159 ensureAnimationStack(m_target).add(sampledEffect.release()); 160 } else { 161 return; 162 } 163 164 m_target->setNeedsAnimationStyleRecalc(); 165} 166 167void Animation::clearEffects() 168{ 169 ASSERT(player()); 170 ASSERT(m_sampledEffect); 171 172 m_sampledEffect->clear(); 173 m_sampledEffect = nullptr; 174 cancelAnimationOnCompositor(); 175 m_target->setNeedsAnimationStyleRecalc(); 176 invalidate(); 177} 178 179void Animation::updateChildrenAndEffects() const 180{ 181 if (!m_effect) 182 return; 183 if (isInEffect()) 184 const_cast<Animation*>(this)->applyEffects(); 185 else if (m_sampledEffect) 186 const_cast<Animation*>(this)->clearEffects(); 187} 188 189double Animation::calculateTimeToEffectChange(bool forwards, double localTime, double timeToNextIteration) const 190{ 191 const double start = startTimeInternal() + specifiedTiming().startDelay; 192 const double end = start + activeDurationInternal(); 193 194 switch (phase()) { 195 case PhaseNone: 196 return std::numeric_limits<double>::infinity(); 197 case PhaseBefore: 198 ASSERT(start >= localTime); 199 return forwards 200 ? start - localTime 201 : std::numeric_limits<double>::infinity(); 202 case PhaseActive: 203 if (forwards && hasActiveAnimationsOnCompositor()) { 204 // Need service to apply fill / fire events. 205 const double timeToEnd = end - localTime; 206 if (hasEvents()) { 207 return std::min(timeToEnd, timeToNextIteration); 208 } else { 209 return timeToEnd; 210 } 211 } 212 return 0; 213 case PhaseAfter: 214 ASSERT(localTime >= end); 215 // If this Animation is still in effect then it will need to update 216 // when its parent goes out of effect. We have no way of knowing when 217 // that will be, however, so the parent will need to supply it. 218 return forwards 219 ? std::numeric_limits<double>::infinity() 220 : localTime - end; 221 default: 222 ASSERT_NOT_REACHED(); 223 return std::numeric_limits<double>::infinity(); 224 } 225} 226 227void Animation::notifySampledEffectRemovedFromAnimationStack() 228{ 229 ASSERT(m_sampledEffect); 230 m_sampledEffect = nullptr; 231} 232 233#if !ENABLE(OILPAN) 234void Animation::notifyElementDestroyed() 235{ 236 // If our player is kept alive just by the sampledEffect, we might get our 237 // destructor called when we call SampledEffect::clear(), so we need to 238 // clear m_sampledEffect first. 239 m_target = nullptr; 240 clearEventDelegate(); 241 SampledEffect* sampledEffect = m_sampledEffect; 242 m_sampledEffect = nullptr; 243 if (sampledEffect) 244 sampledEffect->clear(); 245} 246#endif 247 248bool Animation::isCandidateForAnimationOnCompositor(double playerPlaybackRate) const 249{ 250 if (!effect() || !m_target) 251 return false; 252 return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(specifiedTiming(), *effect(), playerPlaybackRate); 253} 254 255bool Animation::maybeStartAnimationOnCompositor(double startTime, double currentTime) 256{ 257 return maybeStartAnimationOnCompositor(startTime, currentTime, 1); 258} 259 260bool Animation::maybeStartAnimationOnCompositor(double startTime, double currentTime, double playerPlaybackRate) 261{ 262 ASSERT(!hasActiveAnimationsOnCompositor()); 263 if (!isCandidateForAnimationOnCompositor(playerPlaybackRate)) 264 return false; 265 if (!CompositorAnimations::instance()->canStartAnimationOnCompositor(*m_target)) 266 return false; 267 if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target, startTime, currentTime, specifiedTiming(), *effect(), m_compositorAnimationIds, playerPlaybackRate)) 268 return false; 269 ASSERT(!m_compositorAnimationIds.isEmpty()); 270 return true; 271} 272 273bool Animation::hasActiveAnimationsOnCompositor() const 274{ 275 return !m_compositorAnimationIds.isEmpty(); 276} 277 278bool Animation::hasActiveAnimationsOnCompositor(CSSPropertyID property) const 279{ 280 return hasActiveAnimationsOnCompositor() && affects(property); 281} 282 283bool Animation::affects(CSSPropertyID property) const 284{ 285 return m_effect && m_effect->affects(property); 286} 287 288void Animation::cancelAnimationOnCompositor() 289{ 290 // FIXME: cancelAnimationOnCompositor is called from withins style recalc. 291 // This queries compositingState, which is not necessarily up to date. 292 // https://code.google.com/p/chromium/issues/detail?id=339847 293 DisableCompositingQueryAsserts disabler; 294 if (!hasActiveAnimationsOnCompositor()) 295 return; 296 if (!m_target || !m_target->renderer()) 297 return; 298 for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i) 299 CompositorAnimations::instance()->cancelAnimationOnCompositor(*m_target, m_compositorAnimationIds[i]); 300 m_compositorAnimationIds.clear(); 301 player()->setCompositorPending(true); 302} 303 304void Animation::pauseAnimationForTestingOnCompositor(double pauseTime) 305{ 306 ASSERT(hasActiveAnimationsOnCompositor()); 307 if (!m_target || !m_target->renderer()) 308 return; 309 for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i) 310 CompositorAnimations::instance()->pauseAnimationForTestingOnCompositor(*m_target, m_compositorAnimationIds[i], pauseTime); 311} 312 313void Animation::trace(Visitor* visitor) 314{ 315 visitor->trace(m_target); 316 visitor->trace(m_effect); 317 visitor->trace(m_sampledEffect); 318 AnimationNode::trace(visitor); 319} 320 321} // namespace blink 322