1 /* 2 * Copyright (C) 2007, 2012 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 * 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. 16 * 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. 27 */ 28 29 #include "config.h" 30 #include "core/frame/animation/KeyframeAnimation.h" 31 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" 42 43 using namespace std; 44 45 namespace WebCore { 46 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()) 50 , m_index(index) 51 , m_startEventDispatched(false) 52 , m_unanimatedStyle(unanimatedStyle) 53 { 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); 57 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)); 64 } 65 66 KeyframeAnimation::~KeyframeAnimation() 67 { 68 // Make sure to tell the renderer that we are ending. This will make sure any accelerated animations are removed. 69 if (!postActive()) 70 endAnimation(); 71 } 72 73 void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const 74 { 75 // Find the first key 76 double elapsedTime = getElapsedTime(); 77 if (m_animation->duration() && m_animation->iterationCount() != CSSAnimationData::IterationCountInfinite) 78 elapsedTime = min(elapsedTime, m_animation->duration() * m_animation->iterationCount()); 79 80 const double fractionalTime = this->fractionalTime(1, elapsedTime, 0); 81 82 size_t numKeyframes = m_keyframes.size(); 83 if (!numKeyframes) 84 return; 85 86 ASSERT(!m_keyframes[0].key()); 87 ASSERT(m_keyframes[m_keyframes.size() - 1].key() == 1); 88 89 size_t currentIndex = 0; 90 size_t firstIndex = 0; 91 size_t lastIndex = numKeyframes - 1; 92 size_t distance = numKeyframes; 93 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; 99 100 if (key < fractionalTime) { 101 if (distance < 2) 102 currentIndex++; 103 firstIndex = currentIndex; 104 } else { 105 lastIndex = currentIndex; 106 } 107 } 108 109 int prevIndex = -1; 110 int nextIndex = -1; 111 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)) { 116 nextIndex = i; 117 break; 118 } 119 } 120 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)) { 125 prevIndex = i; 126 break; 127 } 128 } 129 130 double scale = 1; 131 double offset = 0; 132 133 if (prevIndex == -1) 134 prevIndex = 0; 135 136 if (nextIndex == -1) 137 nextIndex = numKeyframes - 1; 138 139 const KeyframeValue& prevKeyframe = m_keyframes[prevIndex]; 140 const KeyframeValue& nextKeyframe = m_keyframes[nextIndex]; 141 142 fromStyle = prevKeyframe.style(); 143 toStyle = nextKeyframe.style(); 144 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)); 149 150 prog = progress(scale, offset, KeyframeValue::timingFunction(*prevKeyframe.style())); 151 } 152 153 void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) 154 { 155 // Fire the start timeout if needed 156 fireAnimationEventsIfNeeded(); 157 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); 161 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. 164 if (postActive()) { 165 if (!animatedStyle) 166 animatedStyle = const_cast<RenderStyle*>(targetStyle); 167 return; 168 } 169 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()) 176 return; 177 178 // If we have no keyframes, don't animate. 179 if (!m_keyframes.size()) { 180 updateStateMachine(AnimationStateInputEndAnimation, -1); 181 return; 182 } 183 184 // Run a cycle of animation. 185 // We know we will need a new render style, so make one if needed. 186 if (!animatedStyle) 187 animatedStyle = RenderStyle::clone(targetStyle); 188 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); 198 199 bool needsAnim = CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress); 200 if (!needsAnim) 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(); 205 } 206 } 207 208 void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) 209 { 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()) 213 return; 214 215 if (!m_keyframes.size()) 216 return; 217 218 if (!animatedStyle) 219 animatedStyle = RenderStyle::clone(m_object->style()); 220 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); 228 229 CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress); 230 } 231 } 232 233 bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const 234 { 235 return m_keyframes.containsProperty(property); 236 } 237 238 void KeyframeAnimation::startAnimation(double timeOffset) 239 { 240 if (m_object && m_object->compositingState() == PaintsIntoOwnBacking) 241 m_isAccelerated = toRenderBoxModelObject(m_object)->startAnimation(timeOffset, m_animation.get(), m_keyframes); 242 } 243 244 void KeyframeAnimation::pauseAnimation(double timeOffset) 245 { 246 if (!m_object) 247 return; 248 249 if (m_object && m_object->compositingState() == PaintsIntoOwnBacking && isAccelerated()) 250 toRenderBoxModelObject(m_object)->animationPaused(timeOffset, m_keyframes.animationName()); 251 252 // Restore the original (unanimated) style 253 if (!paused()) 254 setNeedsStyleRecalc(m_object->node()); 255 } 256 257 void KeyframeAnimation::endAnimation() 258 { 259 if (!m_object) 260 return; 261 262 if (m_object && m_object->compositingState() == PaintsIntoOwnBacking && isAccelerated()) 263 toRenderBoxModelObject(m_object)->animationFinished(m_keyframes.animationName()); 264 m_isAccelerated = false; 265 266 // Restore the original (unanimated) style 267 if (!paused()) 268 setNeedsStyleRecalc(m_object->node()); 269 } 270 271 bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listenerType) const 272 { 273 return m_object->document().hasListenerType(listenerType); 274 } 275 276 void KeyframeAnimation::onAnimationStart(double elapsedTime) 277 { 278 sendAnimationEvent(EventTypeNames::animationstart, elapsedTime); 279 } 280 281 void KeyframeAnimation::onAnimationIteration(double elapsedTime) 282 { 283 sendAnimationEvent(EventTypeNames::animationiteration, elapsedTime); 284 } 285 286 void KeyframeAnimation::onAnimationEnd(double elapsedTime) 287 { 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()) 292 endAnimation(); 293 } 294 295 bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime) 296 { 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; 302 else { 303 ASSERT(eventType == EventTypeNames::animationstart); 304 if (m_startEventDispatched) 305 return false; 306 m_startEventDispatched = true; 307 listenerType = Document::ANIMATIONSTART_LISTENER; 308 } 309 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()); 315 316 if (!element) 317 return false; 318 319 // Schedule event handling 320 m_compAnim->animationController()->addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime); 321 322 // Restore the original (unanimated) style 323 if (eventType == EventTypeNames::animationend && element->renderer()) 324 setNeedsStyleRecalc(element.get()); 325 326 return true; // Did dispatch an event 327 } 328 329 return false; // Did not dispatch an event 330 } 331 332 void KeyframeAnimation::overrideAnimations() 333 { 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); 338 } 339 340 void KeyframeAnimation::resumeOverriddenAnimations() 341 { 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); 346 } 347 348 bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const 349 { 350 return m_keyframes.containsProperty(property); 351 } 352 353 void KeyframeAnimation::validateTransformFunctionList() 354 { 355 m_transformFunctionListValid = false; 356 357 if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitTransform)) 358 return; 359 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; 363 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; 368 break; 369 } 370 } 371 372 if (firstNonEmptyTransformKeyframeIndex == numKeyframes) 373 return; 374 375 const TransformOperations* firstVal = &m_keyframes[firstNonEmptyTransformKeyframeIndex].style()->transform(); 376 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(); 381 382 // An emtpy transform list matches anything. 383 if (val->operations().isEmpty()) 384 continue; 385 386 if (!firstVal->operationsMatch(*val)) 387 return; 388 } 389 390 // Keyframes are valid 391 m_transformFunctionListValid = true; 392 } 393 394 void KeyframeAnimation::checkForMatchingFilterFunctionLists() 395 { 396 m_filterFunctionListsMatch = false; 397 398 if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitFilter)) 399 return; 400 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; 404 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; 409 break; 410 } 411 } 412 413 if (firstNonEmptyFilterKeyframeIndex == numKeyframes) 414 return; 415 416 const FilterOperations* firstVal = &m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter(); 417 418 for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) { 419 const KeyframeValue& currentKeyframe = m_keyframes[i]; 420 const FilterOperations* val = ¤tKeyframe.style()->filter(); 421 422 if (!firstVal->canInterpolateWith(*val)) 423 return; 424 } 425 426 m_filterFunctionListsMatch = true; 427 } 428 429 double KeyframeAnimation::timeToNextService() 430 { 431 double t = AnimationBase::timeToNextService(); 432 if (t != 0 || preActive()) 433 return t; 434 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; 439 440 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { 441 if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(*it) || !isAccelerated()) { 442 acceleratedPropertiesOnly = false; 443 break; 444 } 445 } 446 447 if (acceleratedPropertiesOnly) { 448 bool isLooping; 449 getTimeToNextEvent(t, isLooping); 450 } 451 452 return t; 453 } 454 455 } // namespace WebCore 456