2 * Copyright (C) 2007, 2012 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
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "core/frame/animation/KeyframeAnimation.h"
32 #include "CSSPropertyNames.h"
33 #include "core/css/resolver/StyleResolver.h"
34 #include "core/events/ThreadLocalEventNames.h"
35 #include "core/frame/UseCounter.h"
36 #include "core/frame/animation/AnimationControllerPrivate.h"
37 #include "core/frame/animation/CSSPropertyAnimation.h"
38 #include "core/frame/animation/CompositeAnimation.h"
39 #include "core/rendering/RenderBoxModelObject.h"
40 #include "core/rendering/style/RenderStyle.h"
41 #include "public/platform/Platform.h"
47 KeyframeAnimation::KeyframeAnimation(const CSSAnimationData* animation, RenderObject& renderer, int index, CompositeAnimation* compAnim, RenderStyle& unanimatedStyle)
48 : AnimationBase(animation, renderer, compAnim)
49 , m_keyframes(renderer, animation->name())
51 , m_startEventDispatched(false)
52 , m_unanimatedStyle(unanimatedStyle)
54 // Get the keyframe RenderStyles
55 if (m_object && m_object->node() && m_object->node()->isElementNode())
56 m_object->document().ensureStyleResolver().keyframeStylesForAnimation(toElement(m_object->node()), unanimatedStyle, m_keyframes);
58 // Update the m_transformFunctionListValid flag based on whether the function lists in the keyframes match.
59 validateTransformFunctionList();
60 checkForMatchingFilterFunctionLists();
61 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties();
62 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it)
63 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(*it));
66 KeyframeAnimation::~KeyframeAnimation()
68 // Make sure to tell the renderer that we are ending. This will make sure any accelerated animations are removed.
73 void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const
76 double elapsedTime = getElapsedTime();
77 if (m_animation->duration() && m_animation->iterationCount() != CSSAnimationData::IterationCountInfinite)
78 elapsedTime = min(elapsedTime, m_animation->duration() * m_animation->iterationCount());
80 const double fractionalTime = this->fractionalTime(1, elapsedTime, 0);
82 size_t numKeyframes = m_keyframes.size();
86 ASSERT(!m_keyframes[0].key());
87 ASSERT(m_keyframes[m_keyframes.size() - 1].key() == 1);
89 size_t currentIndex = 0;
90 size_t firstIndex = 0;
91 size_t lastIndex = numKeyframes - 1;
92 size_t distance = numKeyframes;
94 // Find keyframe that is closest to elapsed time.
95 while (distance > 1) {
96 currentIndex = (lastIndex + firstIndex) >> 1;
97 double key = m_keyframes[currentIndex].key();
98 distance = lastIndex - currentIndex;
100 if (key < fractionalTime) {
103 firstIndex = currentIndex;
105 lastIndex = currentIndex;
112 // Iterate forward to find next keyframe that is used to animate CSS property.
113 for (size_t i = currentIndex; i < numKeyframes; ++i) {
114 const KeyframeValue& keyFrame = m_keyframes[i];
115 if (keyFrame.key() > fractionalTime && keyFrame.containsProperty(property)) {
121 // Iterate backward to find previous keyframe.
122 for (int i = currentIndex; i >= 0; --i) {
123 const KeyframeValue& keyFrame = m_keyframes[i];
124 if (keyFrame.key() <= fractionalTime && keyFrame.containsProperty(property)) {
137 nextIndex = numKeyframes - 1;
139 const KeyframeValue& prevKeyframe = m_keyframes[prevIndex];
140 const KeyframeValue& nextKeyframe = m_keyframes[nextIndex];
142 fromStyle = prevKeyframe.style();
143 toStyle = nextKeyframe.style();
145 offset = prevKeyframe.key();
146 scale = 1.0 / (nextKeyframe.key() - prevKeyframe.key());
147 // A scale of infinity is handled in AnimationBase::fractionalTime().
148 ASSERT(scale >= 0 && (!std::isinf(scale) || prevIndex == nextIndex));
150 prog = progress(scale, offset, KeyframeValue::timingFunction(*prevKeyframe.style()));
153 void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle)
155 // Fire the start timeout if needed
156 fireAnimationEventsIfNeeded();
158 // If we have not yet started, we will not have a valid start time, so just start the animation if needed.
159 if (isNew() && m_animation->playState() == AnimPlayStatePlaying)
160 updateStateMachine(AnimationStateInputStartAnimation, -1);
162 // If we get this far and the animation is done, it means we are cleaning up a just finished animation.
163 // If so, we need to send back the targetStyle.
166 animatedStyle = const_cast<RenderStyle*>(targetStyle);
170 // If we are waiting for the start timer, we don't want to change the style yet.
171 // Special case 1 - if the delay time is 0, then we do want to set the first frame of the
172 // animation right away. This avoids a flash when the animation starts.
173 // Special case 2 - if there is a backwards fill mode, then we want to continue
174 // through to the style blend so that we get the fromStyle.
175 if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())
178 // If we have no keyframes, don't animate.
179 if (!m_keyframes.size()) {
180 updateStateMachine(AnimationStateInputEndAnimation, -1);
184 // Run a cycle of animation.
185 // We know we will need a new render style, so make one if needed.
187 animatedStyle = RenderStyle::clone(targetStyle);
189 // FIXME: we need to be more efficient about determining which keyframes we are animating between.
190 // We should cache the last pair or something.
191 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties();
192 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
193 // Get the from/to styles and progress between
194 const RenderStyle* fromStyle = 0;
195 const RenderStyle* toStyle = 0;
196 double progress = 0.0;
197 fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress);
199 bool needsAnim = CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
201 // If we are running an accelerated animation, set a flag in the style
202 // to indicate it. This can be used to make sure we get an updated
203 // style for hit testing, etc.
204 animatedStyle->setIsRunningAcceleratedAnimation();
208 void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle)
210 // If we're in the delay phase and we're not backwards filling, tell the caller
211 // to use the current style.
212 if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())
215 if (!m_keyframes.size())
219 animatedStyle = RenderStyle::clone(m_object->style());
221 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties();
222 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
223 // Get the from/to styles and progress between
224 const RenderStyle* fromStyle = 0;
225 const RenderStyle* toStyle = 0;
226 double progress = 0.0;
227 fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress);
229 CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
233 bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const
235 return m_keyframes.containsProperty(property);
238 void KeyframeAnimation::startAnimation(double timeOffset)
240 if (m_object && m_object->compositingState() == PaintsIntoOwnBacking)
241 m_isAccelerated = toRenderBoxModelObject(m_object)->startAnimation(timeOffset, m_animation.get(), m_keyframes);
244 void KeyframeAnimation::pauseAnimation(double timeOffset)
249 if (m_object && m_object->compositingState() == PaintsIntoOwnBacking && isAccelerated())
250 toRenderBoxModelObject(m_object)->animationPaused(timeOffset, m_keyframes.animationName());
252 // Restore the original (unanimated) style
254 setNeedsStyleRecalc(m_object->node());
257 void KeyframeAnimation::endAnimation()
262 if (m_object && m_object->compositingState() == PaintsIntoOwnBacking && isAccelerated())
263 toRenderBoxModelObject(m_object)->animationFinished(m_keyframes.animationName());
264 m_isAccelerated = false;
266 // Restore the original (unanimated) style
268 setNeedsStyleRecalc(m_object->node());
271 bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listenerType) const
273 return m_object->document().hasListenerType(listenerType);
276 void KeyframeAnimation::onAnimationStart(double elapsedTime)
278 sendAnimationEvent(EventTypeNames::animationstart, elapsedTime);
281 void KeyframeAnimation::onAnimationIteration(double elapsedTime)
283 sendAnimationEvent(EventTypeNames::animationiteration, elapsedTime);
286 void KeyframeAnimation::onAnimationEnd(double elapsedTime)
288 sendAnimationEvent(EventTypeNames::animationend, elapsedTime);
289 // End the animation if we don't fill forwards. Forward filling
290 // animations are ended properly in the class destructor.
291 if (!m_animation->fillsForwards())
295 bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime)
297 Document::ListenerType listenerType;
298 if (eventType == EventTypeNames::animationiteration)
299 listenerType = Document::ANIMATIONITERATION_LISTENER;
300 else if (eventType == EventTypeNames::animationend)
301 listenerType = Document::ANIMATIONEND_LISTENER;
303 ASSERT(eventType == EventTypeNames::animationstart);
304 if (m_startEventDispatched)
306 m_startEventDispatched = true;
307 listenerType = Document::ANIMATIONSTART_LISTENER;
310 if (shouldSendEventForListener(listenerType)) {
311 // Dispatch the event
312 RefPtr<Element> element;
313 if (m_object->node() && m_object->node()->isElementNode())
314 element = toElement(m_object->node());
319 // Schedule event handling
320 m_compAnim->animationController()->addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime);
322 // Restore the original (unanimated) style
323 if (eventType == EventTypeNames::animationend && element->renderer())
324 setNeedsStyleRecalc(element.get());
326 return true; // Did dispatch an event
329 return false; // Did not dispatch an event
332 void KeyframeAnimation::overrideAnimations()
334 // This will override implicit animations that match the properties in the keyframe animation
335 HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties();
336 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it)
337 compositeAnimation()->overrideImplicitAnimations(*it);
340 void KeyframeAnimation::resumeOverriddenAnimations()
342 // This will resume overridden implicit animations
343 HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties();
344 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it)
345 compositeAnimation()->resumeOverriddenImplicitAnimations(*it);
348 bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const
350 return m_keyframes.containsProperty(property);
353 void KeyframeAnimation::validateTransformFunctionList()
355 m_transformFunctionListValid = false;
357 if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitTransform))
360 // Empty transforms match anything, so find the first non-empty entry as the reference
361 size_t numKeyframes = m_keyframes.size();
362 size_t firstNonEmptyTransformKeyframeIndex = numKeyframes;
364 for (size_t i = 0; i < numKeyframes; ++i) {
365 const KeyframeValue& currentKeyframe = m_keyframes[i];
366 if (currentKeyframe.style()->transform().operations().size()) {
367 firstNonEmptyTransformKeyframeIndex = i;
372 if (firstNonEmptyTransformKeyframeIndex == numKeyframes)
375 const TransformOperations* firstVal = &m_keyframes[firstNonEmptyTransformKeyframeIndex].style()->transform();
377 // See if the keyframes are valid
378 for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; ++i) {
379 const KeyframeValue& currentKeyframe = m_keyframes[i];
380 const TransformOperations* val = ¤tKeyframe.style()->transform();
382 // An emtpy transform list matches anything.
383 if (val->operations().isEmpty())
386 if (!firstVal->operationsMatch(*val))
390 // Keyframes are valid
391 m_transformFunctionListValid = true;
394 void KeyframeAnimation::checkForMatchingFilterFunctionLists()
396 m_filterFunctionListsMatch = false;
398 if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitFilter))
401 // Empty filters match anything, so find the first non-empty entry as the reference
402 size_t numKeyframes = m_keyframes.size();
403 size_t firstNonEmptyFilterKeyframeIndex = numKeyframes;
405 for (size_t i = 0; i < numKeyframes; ++i) {
406 const KeyframeValue& currentKeyframe = m_keyframes[i];
407 if (currentKeyframe.style()->filter().operations().size()) {
408 firstNonEmptyFilterKeyframeIndex = i;
413 if (firstNonEmptyFilterKeyframeIndex == numKeyframes)
416 const FilterOperations* firstVal = &m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter();
418 for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) {
419 const KeyframeValue& currentKeyframe = m_keyframes[i];
420 const FilterOperations* val = ¤tKeyframe.style()->filter();
422 if (!firstVal->canInterpolateWith(*val))
426 m_filterFunctionListsMatch = true;
429 double KeyframeAnimation::timeToNextService()
431 double t = AnimationBase::timeToNextService();
432 if (t != 0 || preActive())
435 // A return value of 0 means we need service. But if we only have accelerated animations we
436 // only need service at the end of the transition
437 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties();
438 bool acceleratedPropertiesOnly = true;
440 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
441 if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(*it) || !isAccelerated()) {
442 acceleratedPropertiesOnly = false;
447 if (acceleratedPropertiesOnly) {
449 getTimeToNextEvent(t, isLooping);
455 } // namespace WebCore