| OLD | NEW |
| (Empty) |
| 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, RenderOb
ject& renderer, int index, CompositeAnimation* compAnim, RenderStyle& unanimated
Style) | |
| 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(to
Element(m_object->node()), unanimatedStyle, m_keyframes); | |
| 57 | |
| 58 // Update the m_transformFunctionListValid flag based on whether the functio
n lists in the keyframes match. | |
| 59 validateTransformFunctionList(); | |
| 60 checkForMatchingFilterFunctionLists(); | |
| 61 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProper
ties(); | |
| 62 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties
(); it != endProperties; ++it) | |
| 63 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProper
ties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(*it)); | |
| 64 } | |
| 65 | |
| 66 KeyframeAnimation::~KeyframeAnimation() | |
| 67 { | |
| 68 // Make sure to tell the renderer that we are ending. This will make sure an
y 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) cons
t | |
| 74 { | |
| 75 // Find the first key | |
| 76 double elapsedTime = getElapsedTime(); | |
| 77 if (m_animation->duration() && m_animation->iterationCount() != CSSAnimation
Data::IterationCountInfinite) | |
| 78 elapsedTime = min(elapsedTime, m_animation->duration() * m_animation->it
erationCount()); | |
| 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 propert
y. | |
| 113 for (size_t i = currentIndex; i < numKeyframes; ++i) { | |
| 114 const KeyframeValue& keyFrame = m_keyframes[i]; | |
| 115 if (keyFrame.key() > fractionalTime && keyFrame.containsProperty(propert
y)) { | |
| 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(proper
ty)) { | |
| 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.s
tyle())); | |
| 151 } | |
| 152 | |
| 153 void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const Render
Style*, 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 conti
nue | |
| 174 // through to the style blend so that we get the fromStyle. | |
| 175 if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackw
ards()) | |
| 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.endProper
ties(); | |
| 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, animat
edStyle.get(), fromStyle, toStyle, progress); | |
| 200 if (!needsAnim) | |
| 201 // If we are running an accelerated animation, set a flag in the sty
le | |
| 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 cal
ler | |
| 211 // to use the current style. | |
| 212 if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackw
ards()) | |
| 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.endProper
ties(); | |
| 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(), fr
omStyle, 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(timeO
ffset, 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 && isAc
celerated()) | |
| 250 toRenderBoxModelObject(m_object)->animationPaused(timeOffset, m_keyframe
s.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 && isAc
celerated()) | |
| 263 toRenderBoxModelObject(m_object)->animationFinished(m_keyframes.animatio
nName()); | |
| 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 listen
erType) 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 k
eyframe 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(CSSPropertyWebki
tTransform)) | |
| 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[firstNonEmptyTransformKey
frameIndex].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(CSSPropertyWebki
tFilter)) | |
| 399 return; | |
| 400 | |
| 401 // Empty filters match anything, so find the first non-empty entry as the re
ference | |
| 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[firstNonEmptyFilterKeyframeI
ndex].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 accelerate
d animations we | |
| 436 // only need service at the end of the transition | |
| 437 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProper
ties(); | |
| 438 bool acceleratedPropertiesOnly = true; | |
| 439 | |
| 440 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties
(); it != endProperties; ++it) { | |
| 441 if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(*it) || !isA
ccelerated()) { | |
| 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 | |
| OLD | NEW |