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/page/animation/KeyframeAnimation.h" 31 32 #include "CSSPropertyNames.h" 33 #include "core/css/resolver/StyleResolver.h" 34 #include "core/dom/EventNames.h" 35 #include "core/page/UseCounter.h" 36 #include "core/page/animation/AnimationControllerPrivate.h" 37 #include "core/page/animation/CSSPropertyAnimation.h" 38 #include "core/page/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()->styleResolver()->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 WebKit::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 static const CSSAnimationData* getAnimationFromStyleByName(const RenderStyle* style, const AtomicString& name) 74 { 75 if (!style->animations()) 76 return 0; 77 78 size_t animationCount = style->animations()->size(); 79 for (size_t i = 0; i < animationCount; i++) { 80 if (name == style->animations()->animation(i)->name()) 81 return style->animations()->animation(i); 82 } 83 84 return 0; 85 } 86 87 void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const 88 { 89 // Find the first key 90 double elapsedTime = getElapsedTime(); 91 if (m_animation->duration() && m_animation->iterationCount() != CSSAnimationData::IterationCountInfinite) 92 elapsedTime = min(elapsedTime, m_animation->duration() * m_animation->iterationCount()); 93 94 const double fractionalTime = this->fractionalTime(1, elapsedTime, 0); 95 96 size_t numKeyframes = m_keyframes.size(); 97 if (!numKeyframes) 98 return; 99 100 ASSERT(!m_keyframes[0].key()); 101 ASSERT(m_keyframes[m_keyframes.size() - 1].key() == 1); 102 103 size_t currentIndex = 0; 104 size_t firstIndex = 0; 105 size_t lastIndex = numKeyframes - 1; 106 size_t distance = numKeyframes; 107 108 // Find keyframe that is closest to elapsed time. 109 while (distance > 1) { 110 currentIndex = (lastIndex + firstIndex) >> 1; 111 float key = m_keyframes[currentIndex].key(); 112 distance = lastIndex - currentIndex; 113 114 if (key < fractionalTime) { 115 if (distance < 2) 116 currentIndex++; 117 firstIndex = currentIndex; 118 } else { 119 lastIndex = currentIndex; 120 } 121 } 122 123 int prevIndex = -1; 124 int nextIndex = -1; 125 126 // Iterate forward to find next keyframe that is used to animate CSS property. 127 for (size_t i = currentIndex; i < numKeyframes; ++i) { 128 const KeyframeValue& keyFrame = m_keyframes[i]; 129 if (keyFrame.key() > fractionalTime && keyFrame.containsProperty(property)) { 130 nextIndex = i; 131 break; 132 } 133 } 134 135 // Iterate backward to find previous keyframe. 136 for (size_t i = currentIndex; i < numKeyframes; --i) { 137 const KeyframeValue& keyFrame = m_keyframes[i]; 138 if (keyFrame.key() <= fractionalTime && keyFrame.containsProperty(property)) { 139 prevIndex = i; 140 break; 141 } 142 } 143 144 double scale = 1; 145 double offset = 0; 146 147 if (prevIndex == -1) 148 prevIndex = 0; 149 150 if (nextIndex == -1) 151 nextIndex = numKeyframes - 1; 152 153 const KeyframeValue& prevKeyframe = m_keyframes[prevIndex]; 154 const KeyframeValue& nextKeyframe = m_keyframes[nextIndex]; 155 156 fromStyle = prevKeyframe.style(); 157 toStyle = nextKeyframe.style(); 158 159 offset = prevKeyframe.key(); 160 scale = 1.0 / (nextKeyframe.key() - prevKeyframe.key()); 161 162 const TimingFunction* timingFunction = 0; 163 if (const CSSAnimationData* matchedAnimation = getAnimationFromStyleByName(fromStyle, name())) 164 timingFunction = matchedAnimation->timingFunction().get(); 165 166 prog = progress(scale, offset, timingFunction); 167 } 168 169 void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) 170 { 171 // Fire the start timeout if needed 172 fireAnimationEventsIfNeeded(); 173 174 // If we have not yet started, we will not have a valid start time, so just start the animation if needed. 175 if (isNew() && m_animation->playState() == AnimPlayStatePlaying) 176 updateStateMachine(AnimationStateInputStartAnimation, -1); 177 178 // If we get this far and the animation is done, it means we are cleaning up a just finished animation. 179 // If so, we need to send back the targetStyle. 180 if (postActive()) { 181 if (!animatedStyle) 182 animatedStyle = const_cast<RenderStyle*>(targetStyle); 183 return; 184 } 185 186 // If we are waiting for the start timer, we don't want to change the style yet. 187 // Special case 1 - if the delay time is 0, then we do want to set the first frame of the 188 // animation right away. This avoids a flash when the animation starts. 189 // Special case 2 - if there is a backwards fill mode, then we want to continue 190 // through to the style blend so that we get the fromStyle. 191 if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards()) 192 return; 193 194 // If we have no keyframes, don't animate. 195 if (!m_keyframes.size()) { 196 updateStateMachine(AnimationStateInputEndAnimation, -1); 197 return; 198 } 199 200 // Run a cycle of animation. 201 // We know we will need a new render style, so make one if needed. 202 if (!animatedStyle) 203 animatedStyle = RenderStyle::clone(targetStyle); 204 205 // FIXME: we need to be more efficient about determining which keyframes we are animating between. 206 // We should cache the last pair or something. 207 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties(); 208 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { 209 // Get the from/to styles and progress between 210 const RenderStyle* fromStyle = 0; 211 const RenderStyle* toStyle = 0; 212 double progress = 0.0; 213 fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress); 214 215 bool needsAnim = CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress); 216 if (!needsAnim) 217 // If we are running an accelerated animation, set a flag in the style 218 // to indicate it. This can be used to make sure we get an updated 219 // style for hit testing, etc. 220 animatedStyle->setIsRunningAcceleratedAnimation(); 221 } 222 } 223 224 void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) 225 { 226 // If we're in the delay phase and we're not backwards filling, tell the caller 227 // to use the current style. 228 if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards()) 229 return; 230 231 if (!m_keyframes.size()) 232 return; 233 234 if (!animatedStyle) 235 animatedStyle = RenderStyle::clone(m_object->style()); 236 237 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties(); 238 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { 239 // Get the from/to styles and progress between 240 const RenderStyle* fromStyle = 0; 241 const RenderStyle* toStyle = 0; 242 double progress = 0.0; 243 fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress); 244 245 CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress); 246 } 247 } 248 249 bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const 250 { 251 return m_keyframes.containsProperty(property); 252 } 253 254 bool KeyframeAnimation::startAnimation(double timeOffset) 255 { 256 if (m_object && m_object->isComposited()) { 257 return toRenderBoxModelObject(m_object)->startAnimation(timeOffset, m_animation.get(), m_keyframes); 258 } 259 return false; 260 } 261 262 void KeyframeAnimation::pauseAnimation(double timeOffset) 263 { 264 if (!m_object) 265 return; 266 267 if (m_object->isComposited()) 268 toRenderBoxModelObject(m_object)->animationPaused(timeOffset, m_keyframes.animationName()); 269 270 // Restore the original (unanimated) style 271 if (!paused()) 272 setNeedsStyleRecalc(m_object->node()); 273 } 274 275 void KeyframeAnimation::endAnimation() 276 { 277 if (!m_object) 278 return; 279 280 if (m_object->isComposited()) 281 toRenderBoxModelObject(m_object)->animationFinished(m_keyframes.animationName()); 282 283 // Restore the original (unanimated) style 284 if (!paused()) 285 setNeedsStyleRecalc(m_object->node()); 286 } 287 288 bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listenerType) const 289 { 290 return m_object->document()->hasListenerType(listenerType); 291 } 292 293 void KeyframeAnimation::onAnimationStart(double elapsedTime) 294 { 295 sendAnimationEvent(eventNames().webkitAnimationStartEvent, elapsedTime); 296 } 297 298 void KeyframeAnimation::onAnimationIteration(double elapsedTime) 299 { 300 sendAnimationEvent(eventNames().webkitAnimationIterationEvent, elapsedTime); 301 } 302 303 void KeyframeAnimation::onAnimationEnd(double elapsedTime) 304 { 305 sendAnimationEvent(eventNames().webkitAnimationEndEvent, elapsedTime); 306 // End the animation if we don't fill forwards. Forward filling 307 // animations are ended properly in the class destructor. 308 if (!m_animation->fillsForwards()) 309 endAnimation(); 310 } 311 312 bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime) 313 { 314 Document::ListenerType listenerType; 315 if (eventType == eventNames().webkitAnimationIterationEvent) 316 listenerType = Document::ANIMATIONITERATION_LISTENER; 317 else if (eventType == eventNames().webkitAnimationEndEvent) 318 listenerType = Document::ANIMATIONEND_LISTENER; 319 else { 320 ASSERT(eventType == eventNames().webkitAnimationStartEvent); 321 if (m_startEventDispatched) 322 return false; 323 m_startEventDispatched = true; 324 listenerType = Document::ANIMATIONSTART_LISTENER; 325 } 326 327 if (shouldSendEventForListener(listenerType)) { 328 // Dispatch the event 329 RefPtr<Element> element; 330 if (m_object->node() && m_object->node()->isElementNode()) 331 element = toElement(m_object->node()); 332 333 if (!element) 334 return false; 335 336 // Schedule event handling 337 m_compAnim->animationController()->addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime); 338 339 // Restore the original (unanimated) style 340 if (eventType == eventNames().webkitAnimationEndEvent && element->renderer()) 341 setNeedsStyleRecalc(element.get()); 342 343 return true; // Did dispatch an event 344 } 345 346 return false; // Did not dispatch an event 347 } 348 349 void KeyframeAnimation::overrideAnimations() 350 { 351 // This will override implicit animations that match the properties in the keyframe animation 352 HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties(); 353 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) 354 compositeAnimation()->overrideImplicitAnimations(*it); 355 } 356 357 void KeyframeAnimation::resumeOverriddenAnimations() 358 { 359 // This will resume overridden implicit animations 360 HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties(); 361 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) 362 compositeAnimation()->resumeOverriddenImplicitAnimations(*it); 363 } 364 365 bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const 366 { 367 return m_keyframes.containsProperty(property); 368 } 369 370 void KeyframeAnimation::validateTransformFunctionList() 371 { 372 m_transformFunctionListValid = false; 373 374 if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitTransform)) 375 return; 376 377 // Empty transforms match anything, so find the first non-empty entry as the reference 378 size_t numKeyframes = m_keyframes.size(); 379 size_t firstNonEmptyTransformKeyframeIndex = numKeyframes; 380 381 for (size_t i = 0; i < numKeyframes; ++i) { 382 const KeyframeValue& currentKeyframe = m_keyframes[i]; 383 if (currentKeyframe.style()->transform().operations().size()) { 384 firstNonEmptyTransformKeyframeIndex = i; 385 break; 386 } 387 } 388 389 if (firstNonEmptyTransformKeyframeIndex == numKeyframes) 390 return; 391 392 const TransformOperations* firstVal = &m_keyframes[firstNonEmptyTransformKeyframeIndex].style()->transform(); 393 394 // See if the keyframes are valid 395 for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; ++i) { 396 const KeyframeValue& currentKeyframe = m_keyframes[i]; 397 const TransformOperations* val = ¤tKeyframe.style()->transform(); 398 399 // An emtpy transform list matches anything. 400 if (val->operations().isEmpty()) 401 continue; 402 403 if (!firstVal->operationsMatch(*val)) 404 return; 405 } 406 407 // Keyframes are valid 408 m_transformFunctionListValid = true; 409 } 410 411 void KeyframeAnimation::checkForMatchingFilterFunctionLists() 412 { 413 m_filterFunctionListsMatch = false; 414 415 if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitFilter)) 416 return; 417 418 // Empty filters match anything, so find the first non-empty entry as the reference 419 size_t numKeyframes = m_keyframes.size(); 420 size_t firstNonEmptyFilterKeyframeIndex = numKeyframes; 421 422 for (size_t i = 0; i < numKeyframes; ++i) { 423 const KeyframeValue& currentKeyframe = m_keyframes[i]; 424 if (currentKeyframe.style()->filter().operations().size()) { 425 firstNonEmptyFilterKeyframeIndex = i; 426 break; 427 } 428 } 429 430 if (firstNonEmptyFilterKeyframeIndex == numKeyframes) 431 return; 432 433 const FilterOperations* firstVal = &m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter(); 434 435 for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) { 436 const KeyframeValue& currentKeyframe = m_keyframes[i]; 437 const FilterOperations* val = ¤tKeyframe.style()->filter(); 438 439 // An emtpy filter list matches anything. 440 if (val->operations().isEmpty()) 441 continue; 442 443 if (!firstVal->operationsMatch(*val)) 444 return; 445 } 446 447 m_filterFunctionListsMatch = true; 448 } 449 450 double KeyframeAnimation::timeToNextService() 451 { 452 double t = AnimationBase::timeToNextService(); 453 if (t != 0 || preActive()) 454 return t; 455 456 // A return value of 0 means we need service. But if we only have accelerated animations we 457 // only need service at the end of the transition 458 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties(); 459 bool acceleratedPropertiesOnly = true; 460 461 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { 462 if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(*it) || !isAccelerated()) { 463 acceleratedPropertiesOnly = false; 464 break; 465 } 466 } 467 468 if (acceleratedPropertiesOnly) { 469 bool isLooping; 470 getTimeToNextEvent(t, isLooping); 471 } 472 473 return t; 474 } 475 476 } // namespace WebCore 477