Home | History | Annotate | Download | only in animation
      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 = &currentKeyframe.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 = &currentKeyframe.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