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/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 = &currentKeyframe.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 = &currentKeyframe.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