Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / animation / css / CSSAnimations.cpp
index 5ba36b0..56b3670 100644 (file)
 #include "config.h"
 #include "core/animation/css/CSSAnimations.h"
 
-#include "StylePropertyShorthand.h"
+#include "core/StylePropertyShorthand.h"
 #include "core/animation/ActiveAnimations.h"
-#include "core/animation/DocumentTimeline.h"
-#include "core/animation/KeyframeAnimationEffect.h"
+#include "core/animation/AnimationTimeline.h"
+#include "core/animation/CompositorAnimations.h"
+#include "core/animation/KeyframeEffectModel.h"
+#include "core/animation/LegacyStyleInterpolation.h"
 #include "core/animation/css/CSSAnimatableValueFactory.h"
+#include "core/animation/css/CSSPropertyEquality.h"
 #include "core/css/CSSKeyframeRule.h"
+#include "core/css/CSSPropertyMetadata.h"
+#include "core/css/resolver/CSSToStyleMap.h"
 #include "core/css/resolver/StyleResolver.h"
 #include "core/dom/Element.h"
-#include "core/events/ThreadLocalEventNames.h"
+#include "core/dom/PseudoElement.h"
+#include "core/dom/StyleEngine.h"
 #include "core/events/TransitionEvent.h"
 #include "core/events/WebKitAnimationEvent.h"
-#include "core/frame/animation/CSSPropertyAnimation.h"
-#include "core/platform/animation/CSSAnimationDataList.h"
-#include "core/platform/animation/TimingFunction.h"
+#include "core/frame/UseCounter.h"
+#include "core/rendering/RenderLayer.h"
+#include "core/rendering/RenderObject.h"
+#include "core/rendering/style/KeyframeList.h"
+#include "platform/animation/TimingFunction.h"
+#include "public/platform/Platform.h"
+#include "wtf/BitArray.h"
 #include "wtf/HashSet.h"
 
-namespace WebCore {
-
-struct CandidateTransition {
-    CandidateTransition(PassRefPtr<AnimatableValue> from, PassRefPtr<AnimatableValue> to, const CSSAnimationData* anim)
-        : from(from)
-        , to(to)
-        , anim(anim)
-    {
-    }
-    CandidateTransition() { } // The HashMap calls the default ctor
-    RefPtr<AnimatableValue> from;
-    RefPtr<AnimatableValue> to;
-    const CSSAnimationData* anim;
-};
-typedef HashMap<CSSPropertyID, CandidateTransition> CandidateTransitionMap;
+namespace blink {
 
 namespace {
 
-bool isEarlierPhase(TimedItem::Phase target, TimedItem::Phase reference)
+CSSPropertyID propertyForAnimation(CSSPropertyID property)
 {
-    ASSERT(target != TimedItem::PhaseNone);
-    ASSERT(reference != TimedItem::PhaseNone);
-    return target < reference;
+    switch (property) {
+    case CSSPropertyWebkitPerspective:
+        return CSSPropertyPerspective;
+    case CSSPropertyWebkitTransform:
+        return CSSPropertyTransform;
+    case CSSPropertyWebkitPerspectiveOriginX:
+    case CSSPropertyWebkitPerspectiveOriginY:
+        return CSSPropertyPerspectiveOrigin;
+    case CSSPropertyWebkitTransformOriginX:
+    case CSSPropertyWebkitTransformOriginY:
+    case CSSPropertyWebkitTransformOriginZ:
+        return CSSPropertyTransformOrigin;
+    default:
+        break;
+    }
+    return property;
 }
 
-bool isLaterPhase(TimedItem::Phase target, TimedItem::Phase reference)
+static void resolveKeyframes(StyleResolver* resolver, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, const AtomicString& name, TimingFunction* defaultTimingFunction,
+    AnimatableValueKeyframeVector& keyframes)
 {
-    ASSERT(target != TimedItem::PhaseNone);
-    ASSERT(reference != TimedItem::PhaseNone);
-    return target > reference;
-}
+    // When the element is null, use its parent for scoping purposes.
+    const Element* elementForScoping = element ? element : &parentElement;
+    const StyleRuleKeyframes* keyframesRule = CSSAnimations::matchScopedKeyframesRule(resolver, elementForScoping, name.impl());
+    if (!keyframesRule)
+        return;
 
-// Returns the default timing function.
-const PassRefPtr<TimingFunction> timingFromAnimationData(const CSSAnimationData* animationData, Timing& timing, bool& isPaused)
-{
-    if (animationData->isDelaySet())
-        timing.startDelay = animationData->delay();
-    if (animationData->isDurationSet()) {
-        timing.iterationDuration = animationData->duration();
-        timing.hasIterationDuration = true;
-    }
-    if (animationData->isIterationCountSet()) {
-        if (animationData->iterationCount() == CSSAnimationData::IterationCountInfinite)
-            timing.iterationCount = std::numeric_limits<double>::infinity();
-        else
-            timing.iterationCount = animationData->iterationCount();
-    }
-    if (animationData->isFillModeSet()) {
-        switch (animationData->fillMode()) {
-        case AnimationFillModeForwards:
-            timing.fillMode = Timing::FillModeForwards;
-            break;
-        case AnimationFillModeBackwards:
-            timing.fillMode = Timing::FillModeBackwards;
-            break;
-        case AnimationFillModeBoth:
-            timing.fillMode = Timing::FillModeBoth;
-            break;
-        case AnimationFillModeNone:
-            timing.fillMode = Timing::FillModeNone;
-            break;
-        default:
-            ASSERT_NOT_REACHED();
+    const WillBeHeapVector<RefPtrWillBeMember<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
+    if (styleKeyframes.isEmpty())
+        return;
+
+    // Construct and populate the style for each keyframe
+    PropertySet specifiedPropertiesForUseCounter;
+    for (size_t i = 0; i < styleKeyframes.size(); ++i) {
+        const StyleKeyframe* styleKeyframe = styleKeyframes[i].get();
+        // It's OK to pass a null element here.
+        RefPtr<RenderStyle> keyframeStyle = resolver->styleForKeyframe(element, style, parentStyle, styleKeyframe, name);
+        RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframe = AnimatableValueKeyframe::create();
+        const Vector<double>& offsets = styleKeyframe->keys();
+        ASSERT(!offsets.isEmpty());
+        keyframe->setOffset(offsets[0]);
+        keyframe->setEasing(defaultTimingFunction);
+        const StylePropertySet& properties = styleKeyframe->properties();
+        for (unsigned j = 0; j < properties.propertyCount(); j++) {
+            specifiedPropertiesForUseCounter.add(properties.propertyAt(j).id());
+            CSSPropertyID property = propertyForAnimation(properties.propertyAt(j).id());
+            if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) {
+                CSSValue* value = properties.propertyAt(j).value();
+                RefPtr<TimingFunction> timingFunction;
+                if (value->isInheritedValue() && parentStyle->animations())
+                    timingFunction = parentStyle->animations()->timingFunctionList()[0];
+                else if (value->isInheritedValue() || value->isInitialValue())
+                    timingFunction = CSSTimingData::initialTimingFunction();
+                else
+                    timingFunction = CSSToStyleMap::mapAnimationTimingFunction(toCSSValueList(value)->item(0));
+                keyframe->setEasing(timingFunction.release());
+            } else if (CSSPropertyMetadata::isAnimatableProperty(property)) {
+                keyframe->setPropertyValue(property, CSSAnimatableValueFactory::create(property, *keyframeStyle).get());
+            }
         }
-    } else {
-        timing.fillMode = Timing::FillModeNone;
-    }
-    if (animationData->isDirectionSet()) {
-        switch (animationData->direction()) {
-        case CSSAnimationData::AnimationDirectionNormal:
-            timing.direction = Timing::PlaybackDirectionNormal;
-            break;
-        case CSSAnimationData::AnimationDirectionAlternate:
-            timing.direction = Timing::PlaybackDirectionAlternate;
-            break;
-        case CSSAnimationData::AnimationDirectionReverse:
-            timing.direction = Timing::PlaybackDirectionReverse;
-            break;
-        case CSSAnimationData::AnimationDirectionAlternateReverse:
-            timing.direction = Timing::PlaybackDirectionAlternateReverse;
-            break;
-        default:
-            ASSERT_NOT_REACHED();
+        keyframes.append(keyframe);
+        // The last keyframe specified at a given offset is used.
+        for (size_t j = 1; j < offsets.size(); ++j) {
+            keyframes.append(toAnimatableValueKeyframe(keyframe->cloneWithOffset(offsets[j]).get()));
         }
     }
-    isPaused = animationData->isPlayStateSet() && animationData->playState() == AnimPlayStatePaused;
-    return animationData->isTimingFunctionSet() ? animationData->timingFunction() : CSSAnimationData::initialAnimationTimingFunction();
-}
+    ASSERT(!keyframes.isEmpty());
 
-void calculateCandidateTransitionForProperty(const CSSAnimationData* anim, CSSPropertyID id, const RenderStyle* oldStyle, const RenderStyle* newStyle, CandidateTransitionMap& candidateMap)
-{
-    if (!CSSPropertyAnimation::propertiesEqual(id, oldStyle, newStyle)) {
-        RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
-        RefPtr<AnimatableValue> to = CSSAnimatableValueFactory::create(id, newStyle);
-        // If we have multiple transitions on the same property, we will use the
-        // last one since we iterate over them in order and this will override
-        // a previously set CandidateTransition.
-        if (from->usesNonDefaultInterpolationWith(to.get()))
-            candidateMap.add(id, CandidateTransition(from.release(), to.release(), anim));
+    for (PropertySet::const_iterator iter = specifiedPropertiesForUseCounter.begin(); iter != specifiedPropertiesForUseCounter.end(); ++iter) {
+        const CSSPropertyID property = *iter;
+        ASSERT(property != CSSPropertyInvalid);
+        blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property));
     }
-}
-
-void computeCandidateTransitions(const RenderStyle* oldStyle, const RenderStyle* newStyle, CandidateTransitionMap& candidateMap, HashSet<CSSPropertyID>& listedProperties)
-{
-    if (!newStyle->transitions())
-        return;
 
-    for (size_t i = 0; i < newStyle->transitions()->size(); ++i) {
-        const CSSAnimationData* anim = newStyle->transitions()->animation(i);
-        CSSAnimationData::AnimationMode mode = anim->animationMode();
-        if (anim->duration() + anim->delay() <= 0 || mode == CSSAnimationData::AnimateNone)
-            continue;
-
-        bool animateAll = mode == CSSAnimationData::AnimateAll;
-        ASSERT(animateAll || mode == CSSAnimationData::AnimateSingleProperty);
-        const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(anim->property());
-        if (!propertyList.length()) {
-            if (!CSSAnimations::isAnimatableProperty(anim->property()))
+    // Remove duplicate keyframes. In CSS the last keyframe at a given offset takes priority.
+    std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets);
+    size_t targetIndex = 0;
+    for (size_t i = 1; i < keyframes.size(); i++) {
+        if (keyframes[i]->offset() != keyframes[targetIndex]->offset())
+            targetIndex++;
+        if (targetIndex != i)
+            keyframes[targetIndex] = keyframes[i];
+    }
+    keyframes.shrink(targetIndex + 1);
+
+    // Add 0% and 100% keyframes if absent.
+    RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = keyframes[0];
+    if (startKeyframe->offset()) {
+        startKeyframe = AnimatableValueKeyframe::create();
+        startKeyframe->setOffset(0);
+        startKeyframe->setEasing(defaultTimingFunction);
+        keyframes.prepend(startKeyframe);
+    }
+    RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = keyframes[keyframes.size() - 1];
+    if (endKeyframe->offset() != 1) {
+        endKeyframe = AnimatableValueKeyframe::create();
+        endKeyframe->setOffset(1);
+        endKeyframe->setEasing(defaultTimingFunction);
+        keyframes.append(endKeyframe);
+    }
+    ASSERT(keyframes.size() >= 2);
+    ASSERT(!keyframes.first()->offset());
+    ASSERT(keyframes.last()->offset() == 1);
+
+    // Snapshot current property values for 0% and 100% if missing.
+    PropertySet allProperties;
+    size_t numKeyframes = keyframes.size();
+    for (size_t i = 0; i < numKeyframes; i++) {
+        const PropertySet& keyframeProperties = keyframes[i]->properties();
+        for (PropertySet::const_iterator iter = keyframeProperties.begin(); iter != keyframeProperties.end(); ++iter)
+            allProperties.add(*iter);
+    }
+    const PropertySet& startKeyframeProperties = startKeyframe->properties();
+    const PropertySet& endKeyframeProperties = endKeyframe->properties();
+    bool missingStartValues = startKeyframeProperties.size() < allProperties.size();
+    bool missingEndValues = endKeyframeProperties.size() < allProperties.size();
+    if (missingStartValues || missingEndValues) {
+        for (PropertySet::const_iterator iter = allProperties.begin(); iter != allProperties.end(); ++iter) {
+            const CSSPropertyID property = *iter;
+            bool startNeedsValue = missingStartValues && !startKeyframeProperties.contains(property);
+            bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains(property);
+            if (!startNeedsValue && !endNeedsValue)
                 continue;
-            listedProperties.add(anim->property());
-            calculateCandidateTransitionForProperty(anim, anim->property(), oldStyle, newStyle, candidateMap);
-        } else {
-            for (unsigned i = 0; i < propertyList.length(); ++i) {
-                CSSPropertyID id = propertyList.properties()[i];
-                if (!animateAll && !CSSAnimations::isAnimatableProperty(id))
-                    continue;
-                listedProperties.add(id);
-                calculateCandidateTransitionForProperty(anim, id, oldStyle, newStyle, candidateMap);
-            }
+            RefPtrWillBeRawPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style);
+            if (startNeedsValue)
+                startKeyframe->setPropertyValue(property, snapshotValue.get());
+            if (endNeedsValue)
+                endKeyframe->setPropertyValue(property, snapshotValue.get());
         }
     }
+    ASSERT(startKeyframe->properties().size() == allProperties.size());
+    ASSERT(endKeyframe->properties().size() == allProperties.size());
 }
 
 } // namespace
 
-CSSAnimationUpdateScope::CSSAnimationUpdateScope(Element* target)
-    : m_target(target)
+const StyleRuleKeyframes* CSSAnimations::matchScopedKeyframesRule(StyleResolver* resolver, const Element* element, const StringImpl* animationName)
 {
-    if (!m_target)
-        return;
-    // It's possible than an update was created outside an update scope. That's harmless
-    // but we must clear it now to avoid applying it if an updated replacement is not
-    // created in this scope.
-    if (ActiveAnimations* activeAnimations = m_target->activeAnimations())
-        activeAnimations->cssAnimations().setPendingUpdate(nullptr);
+    // FIXME: This is all implementation detail of style resolver, CSSAnimations shouldn't be reaching into any of it.
+    if (resolver->document().styleEngine()->hasOnlyScopedResolverForDocument())
+        return element->document().scopedStyleResolver()->keyframeStylesForAnimation(animationName);
+
+    WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8> stack;
+    resolver->styleTreeResolveScopedKeyframesRules(element, stack);
+    if (stack.isEmpty())
+        return 0;
+
+    for (size_t i = 0; i < stack.size(); ++i) {
+        if (const StyleRuleKeyframes* keyframesRule = stack.at(i)->keyframeStylesForAnimation(animationName))
+            return keyframesRule;
+    }
+    return 0;
 }
 
-CSSAnimationUpdateScope::~CSSAnimationUpdateScope()
+CSSAnimations::CSSAnimations()
 {
-    if (!m_target)
-        return;
-    if (ActiveAnimations* activeAnimations = m_target->activeAnimations())
-        activeAnimations->cssAnimations().maybeApplyPendingUpdate(m_target);
 }
 
-PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const RenderStyle* style, StyleResolver* resolver)
+PassOwnPtrWillBeRawPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
 {
-    ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled());
-    OwnPtr<CSSAnimationUpdate> update = adoptPtr(new CSSAnimationUpdate());
-    calculateAnimationUpdate(update.get(), element, style, resolver);
-    calculateAnimationCompositableValues(update.get(), element);
+    OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = adoptPtrWillBeNoop(new CSSAnimationUpdate());
+    calculateAnimationUpdate(update.get(), element, parentElement, style, parentStyle, resolver);
+    calculateAnimationActiveInterpolations(update.get(), element, parentElement.document().timeline().currentTimeInternal());
     calculateTransitionUpdate(update.get(), element, style);
-    calculateTransitionCompositableValues(update.get(), element);
+    calculateTransitionActiveInterpolations(update.get(), element, parentElement.document().timeline().currentTimeInternal());
     return update->isEmpty() ? nullptr : update.release();
 }
 
-void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element* element, const RenderStyle* style, StyleResolver* resolver)
+void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
 {
-    const CSSAnimationDataList* animationDataList = style->animations();
-    const CSSAnimations* cssAnimations = element->activeAnimations() ? &element->activeAnimations()->cssAnimations() : 0;
+    const ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
+
+#if !ENABLE(ASSERT)
+    // If we're in an animation style change, no animations can have started, been cancelled or changed play state.
+    // When ASSERT is enabled, we verify this optimization.
+    if (activeAnimations && activeAnimations->isAnimationStyleChange())
+        return;
+#endif
+
+    const CSSAnimationData* animationData = style.animations();
+    const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : 0;
 
     HashSet<AtomicString> inactive;
     if (cssAnimations)
         for (AnimationMap::const_iterator iter = cssAnimations->m_animations.begin(); iter != cssAnimations->m_animations.end(); ++iter)
             inactive.add(iter->key);
 
-    if (style->display() != NONE) {
-        for (size_t i = 0; animationDataList && i < animationDataList->size(); ++i) {
-            const CSSAnimationData* animationData = animationDataList->animation(i);
-            if (animationData->isNoneAnimation())
+    if (style.display() != NONE) {
+        for (size_t i = 0; animationData && i < animationData->nameList().size(); ++i) {
+            AtomicString animationName(animationData->nameList()[i]);
+            if (animationName == CSSAnimationData::initialName())
                 continue;
-            ASSERT(animationData->isValidAnimation());
-            AtomicString animationName(animationData->name());
+
+            bool isPaused = CSSTimingData::getRepeated(animationData->playStateList(), i) == AnimPlayStatePaused;
 
             // Keyframes and animation properties are snapshotted when the
             // animation starts, so we don't need to track changes to these,
@@ -238,94 +265,98 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element
                 AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName));
                 if (existing != cssAnimations->m_animations.end()) {
                     inactive.remove(animationName);
-                    const HashSet<RefPtr<Player> >& players = existing->value;
-                    ASSERT(!players.isEmpty());
-                    bool isFirstPlayerPaused = (*players.begin())->paused();
-#ifndef NDEBUG
-                    for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter)
-                        ASSERT((*iter)->paused() == isFirstPlayerPaused);
-#endif
-                    if ((animationData->playState() == AnimPlayStatePaused) != isFirstPlayerPaused)
+                    AnimationPlayer* player = existing->value.get();
+                    if (isPaused != player->paused()) {
+                        ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
                         update->toggleAnimationPaused(animationName);
+                    }
                     continue;
                 }
             }
 
-            Timing timing;
-            bool isPaused;
-            RefPtr<TimingFunction> defaultTimingFunction = timingFromAnimationData(animationData, timing, isPaused);
-            Vector<std::pair<KeyframeAnimationEffect::KeyframeVector, RefPtr<TimingFunction> > > keyframesAndTimingFunctions;
-            resolver->resolveKeyframes(element, style, animationName, defaultTimingFunction.get(), keyframesAndTimingFunctions);
-            if (!keyframesAndTimingFunctions.isEmpty()) {
-                HashSet<RefPtr<InertAnimation> > animations;
-                for (size_t j = 0; j < keyframesAndTimingFunctions.size(); ++j) {
-                    ASSERT(!keyframesAndTimingFunctions[j].first.isEmpty());
-                    timing.timingFunction = keyframesAndTimingFunctions[j].second;
-                    // FIXME: crbug.com/268791 - Keyframes are already normalized, perhaps there should be a flag on KeyframeAnimationEffect to skip normalization.
-                    animations.add(InertAnimation::create(KeyframeAnimationEffect::create(keyframesAndTimingFunctions[j].first), timing, isPaused));
-                }
-                update->startAnimation(animationName, animations);
+            Timing timing = animationData->convertToTiming(i);
+            RefPtr<TimingFunction> keyframeTimingFunction = timing.timingFunction;
+            timing.timingFunction = Timing::defaults().timingFunction;
+            AnimatableValueKeyframeVector resolvedKeyframes;
+            resolveKeyframes(resolver, element, parentElement, style, parentStyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes);
+            if (!resolvedKeyframes.isEmpty()) {
+                ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
+                update->startAnimation(animationName, InertAnimation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused));
             }
         }
     }
 
-    for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter)
-        update->cancelAnimation(*iter, cssAnimations->m_animations.get(*iter));
+    ASSERT(inactive.isEmpty() || cssAnimations);
+    for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter) {
+        ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
+        update->cancelAnimation(*iter, *cssAnimations->m_animations.get(*iter));
+    }
 }
 
 void CSSAnimations::maybeApplyPendingUpdate(Element* element)
 {
-    if (!element->renderer())
-        m_pendingUpdate = nullptr;
-
     if (!m_pendingUpdate) {
-        m_previousCompositableValuesForAnimations.clear();
+        m_previousActiveInterpolationsForAnimations.clear();
         return;
     }
 
-    OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
+    OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
+
+    m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations());
 
-    m_previousCompositableValuesForAnimations.swap(update->compositableValuesForAnimations());
+    // FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here
+    // since we call this from recalc style.
+    // https://code.google.com/p/chromium/issues/detail?id=339847
+    DisableCompositingQueryAsserts disabler;
 
     for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationNames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) {
-        const HashSet<RefPtr<Player> >& players = m_animations.take(*iter);
-        for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter)
-            (*iter)->cancel();
+        RefPtr<AnimationPlayer> player = m_animations.take(*iter);
+        player->cancel();
+        player->update(TimingUpdateOnDemand);
     }
 
     for (Vector<AtomicString>::const_iterator iter = update->animationsWithPauseToggled().begin(); iter != update->animationsWithPauseToggled().end(); ++iter) {
-        const HashSet<RefPtr<Player> >& players = m_animations.get(*iter);
-        ASSERT(!players.isEmpty());
-        bool isFirstPlayerPaused = (*players.begin())->paused();
-        for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter) {
-            Player* player = iter->get();
-            ASSERT(player->paused() == isFirstPlayerPaused);
-            player->setPaused(!isFirstPlayerPaused);
-        }
+        AnimationPlayer* player = m_animations.get(*iter);
+        if (player->paused())
+            player->unpause();
+        else
+            player->pause();
+        if (player->outdated())
+            player->update(TimingUpdateOnDemand);
     }
 
-    for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
-        OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEventDelegate(element, iter->name));
-        HashSet<RefPtr<Player> > players;
-        for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = iter->animations.begin(); animationsIter != iter->animations.end(); ++animationsIter) {
-            const InertAnimation* inertAnimation = animationsIter->get();
-            // The event delegate is set on the the first animation only. We
-            // rely on the behavior of OwnPtr::release() to achieve this.
-            RefPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specified(), Animation::DefaultPriority, eventDelegate.release());
-            RefPtr<Player> player = element->document().timeline()->play(animation.get());
-            player->setPaused(inertAnimation->paused());
-            players.add(player.release());
-        }
-        m_animations.set(iter->name, players);
+    for (WillBeHeapVector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
+        const InertAnimation* inertAnimation = iter->animation.get();
+        OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillBeNoop(new AnimationEventDelegate(element, iter->name));
+        RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release());
+        RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(animation.get());
+        element->document().compositorPendingAnimations().add(player.get());
+        if (inertAnimation->paused())
+            player->pause();
+        player->update(TimingUpdateOnDemand);
+        m_animations.set(iter->name, player.get());
     }
 
+    // Transitions that are run on the compositor only update main-thread state
+    // lazily. However, we need the new state to know what the from state shoud
+    // be when transitions are retargeted. Instead of triggering complete style
+    // recalculation, we find these cases by searching for new transitions that
+    // have matching cancelled animation property IDs on the compositor.
+    WillBeHeapHashMap<CSSPropertyID, std::pair<RefPtrWillBeMember<Animation>, double> > retargetedCompositorTransitions;
     for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
-        ASSERT(m_transitions.contains(*iter));
-        m_transitions.take(*iter).transition->player()->cancel();
+        CSSPropertyID id = *iter;
+        ASSERT(m_transitions.contains(id));
+
+        RefPtrWillBeRawPtr<AnimationPlayer> player = m_transitions.take(id).player;
+        Animation* animation = toAnimation(player->source());
+        if (animation->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end())
+            retargetedCompositorTransitions.add(id, std::pair<RefPtrWillBeMember<Animation>, double>(animation, player->startTimeInternal()));
+        player->cancel();
+        player->update(TimingUpdateOnDemand);
     }
 
-    for (size_t i = 0; i < update->newTransitions().size(); ++i) {
-        const CSSAnimationUpdate::NewTransition& newTransition = update->newTransitions()[i];
+    for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter) {
+        const CSSAnimationUpdate::NewTransition& newTransition = iter->value;
 
         RunningTransition runningTransition;
         runningTransition.from = newTransition.from;
@@ -333,76 +364,159 @@ void CSSAnimations::maybeApplyPendingUpdate(Element* element)
 
         CSSPropertyID id = newTransition.id;
         InertAnimation* inertAnimation = newTransition.animation.get();
-        OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionEventDelegate(element, id));
-        RefPtr<Animation> transition = Animation::create(element, inertAnimation->effect(), inertAnimation->specified(), Animation::TransitionPriority, eventDelegate.release());
-        element->document().transitionTimeline()->play(transition.get());
-        runningTransition.transition = transition.get();
+        OwnPtrWillBeRawPtr<TransitionEventDelegate> eventDelegate = adoptPtrWillBeNoop(new TransitionEventDelegate(element, newTransition.eventId));
+
+        RefPtrWillBeRawPtr<AnimationEffect> effect = inertAnimation->effect();
+
+        if (retargetedCompositorTransitions.contains(id)) {
+            const std::pair<RefPtrWillBeMember<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id);
+            RefPtrWillBeRawPtr<Animation> oldAnimation = oldTransition.first;
+            double oldStartTime = oldTransition.second;
+            double inheritedTime = isNull(oldStartTime) ? 0 : element->document().timeline().currentTimeInternal() - oldStartTime;
+
+            AnimatableValueKeyframeEffectModel* oldEffect = toAnimatableValueKeyframeEffectModel(inertAnimation->effect());
+            const KeyframeVector& frames = oldEffect->getFrames();
+
+            AnimatableValueKeyframeVector newFrames;
+            newFrames.append(toAnimatableValueKeyframe(frames[0]->clone().get()));
+            newFrames.append(toAnimatableValueKeyframe(frames[1]->clone().get()));
+
+            newFrames[0]->clearPropertyValue(id);
+            RefPtrWillBeRawPtr<InertAnimation> inertAnimationForSampling = InertAnimation::create(oldAnimation->effect(), oldAnimation->specifiedTiming(), false);
+            OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > sample = inertAnimationForSampling->sample(inheritedTime);
+            ASSERT(sample->size() == 1);
+            newFrames[0]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue());
+
+            effect = AnimatableValueKeyframeEffectModel::create(newFrames);
+        }
+
+        RefPtrWillBeRawPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release());
+        RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(transition.get());
+        element->document().compositorPendingAnimations().add(player.get());
+        player->update(TimingUpdateOnDemand);
+        runningTransition.player = player;
         m_transitions.set(id, runningTransition);
+        ASSERT(id != CSSPropertyInvalid);
+        blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id));
     }
 }
 
-void CSSAnimations::calculateTransitionUpdateForProperty(CSSAnimationUpdate* update, CSSPropertyID id, const CandidateTransition& newTransition, const TransitionMap* existingTransitions)
+void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, CSSPropertyID eventId, const CSSTransitionData& transitionData, size_t transitionIndex, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* activeTransitions, CSSAnimationUpdate* update, const Element* element)
 {
-    if (existingTransitions) {
-        TransitionMap::const_iterator existingTransitionIter = existingTransitions->find(id);
-
-        if (existingTransitionIter != existingTransitions->end() && !update->cancelledTransitions().contains(id)) {
-            const AnimatableValue* existingTo = existingTransitionIter->value.to;
-            if (newTransition.to->equals(existingTo))
+    RefPtrWillBeRawPtr<AnimatableValue> to = nullptr;
+    if (activeTransitions) {
+        TransitionMap::const_iterator activeTransitionIter = activeTransitions->find(id);
+        if (activeTransitionIter != activeTransitions->end()) {
+            to = CSSAnimatableValueFactory::create(id, style);
+            const AnimatableValue* activeTo = activeTransitionIter->value.to;
+            if (to->equals(activeTo))
                 return;
             update->cancelTransition(id);
+            ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
         }
     }
 
-    KeyframeAnimationEffect::KeyframeVector keyframes;
+    if (CSSPropertyEquality::propertiesEqual(id, oldStyle, style))
+        return;
+    if (!to)
+        to = CSSAnimatableValueFactory::create(id, style);
+
+    RefPtrWillBeRawPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
+    // If we have multiple transitions on the same property, we will use the
+    // last one since we iterate over them in order.
+    if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get()))
+        return;
+
+    Timing timing = transitionData.convertToTiming(transitionIndex);
+    if (timing.startDelay + timing.iterationDuration <= 0)
+        return;
+
+    AnimatableValueKeyframeVector keyframes;
 
-    RefPtr<Keyframe> startKeyframe = Keyframe::create();
-    startKeyframe->setPropertyValue(id, newTransition.from.get());
+    RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = AnimatableValueKeyframe::create();
+    startKeyframe->setPropertyValue(id, from.get());
     startKeyframe->setOffset(0);
+    startKeyframe->setEasing(timing.timingFunction.release());
+    timing.timingFunction = LinearTimingFunction::shared();
     keyframes.append(startKeyframe);
 
-    RefPtr<Keyframe> endKeyframe = Keyframe::create();
-    endKeyframe->setPropertyValue(id, newTransition.to.get());
+    RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = AnimatableValueKeyframe::create();
+    endKeyframe->setPropertyValue(id, to.get());
     endKeyframe->setOffset(1);
     keyframes.append(endKeyframe);
 
-    RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(keyframes);
-
-    Timing timing;
-    bool isPaused;
-    RefPtr<TimingFunction> timingFunction = timingFromAnimationData(newTransition.anim, timing, isPaused);
-    ASSERT(!isPaused);
-    timing.timingFunction = timingFunction;
-    // Note that the backwards part is required for delay to work.
-    timing.fillMode = Timing::FillModeBoth;
-
-    update->startTransition(id, newTransition.from.get(), newTransition.to.get(), InertAnimation::create(effect, timing, isPaused));
+    RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes);
+    update->startTransition(id, eventId, from.get(), to.get(), InertAnimation::create(effect, timing, false));
+    ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
 }
 
-void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const Element* element, const RenderStyle* style)
+void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const Element* element, const RenderStyle& style)
 {
+    if (!element)
+        return;
+
     ActiveAnimations* activeAnimations = element->activeAnimations();
-    const TransitionMap* transitions = activeAnimations ? &activeAnimations->cssAnimations().m_transitions : 0;
-
-    HashSet<CSSPropertyID> listedProperties;
-    if (style->display() != NONE && element->renderer() && element->renderer()->style()) {
-        CandidateTransitionMap candidateMap;
-        computeCandidateTransitions(element->renderer()->style(), style, candidateMap, listedProperties);
-        for (CandidateTransitionMap::const_iterator iter = candidateMap.begin(); iter != candidateMap.end(); ++iter) {
-            // FIXME: We should transition if an !important property changes even when an animation is running,
-            // but this is a bit hard to do with the current applyMatchedProperties system.
-            if (!update->compositableValuesForAnimations().contains(iter->key)
-                && (!activeAnimations || !activeAnimations->cssAnimations().m_previousCompositableValuesForAnimations.contains(iter->key)))
-                calculateTransitionUpdateForProperty(update, iter->key, iter->value, transitions);
+    const TransitionMap* activeTransitions = activeAnimations ? &activeAnimations->cssAnimations().m_transitions : 0;
+    const CSSTransitionData* transitionData = style.transitions();
+
+#if ENABLE(ASSERT)
+    // In debug builds we verify that it would have been safe to avoid populating and testing listedProperties if the style recalc is due to animation.
+    const bool animationStyleRecalc = false;
+#else
+    // In release builds we avoid the cost of checking for new and interrupted transitions if the style recalc is due to animation.
+    const bool animationStyleRecalc = activeAnimations && activeAnimations->isAnimationStyleChange();
+#endif
+
+    BitArray<numCSSProperties> listedProperties;
+    bool anyTransitionHadTransitionAll = false;
+    const RenderObject* renderer = element->renderer();
+    if (!animationStyleRecalc && style.display() != NONE && renderer && renderer->style() && transitionData) {
+        const RenderStyle& oldStyle = *renderer->style();
+
+        for (size_t i = 0; i < transitionData->propertyList().size(); ++i) {
+            const CSSTransitionData::TransitionProperty& transitionProperty = transitionData->propertyList()[i];
+            CSSTransitionData::TransitionPropertyType mode = transitionProperty.propertyType;
+            CSSPropertyID property = transitionProperty.propertyId;
+            if (mode == CSSTransitionData::TransitionNone || mode == CSSTransitionData::TransitionUnknown)
+                continue;
+
+            bool animateAll = mode == CSSTransitionData::TransitionAll;
+            ASSERT(animateAll || mode == CSSTransitionData::TransitionSingleProperty);
+            if (animateAll)
+                anyTransitionHadTransitionAll = true;
+            const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(property);
+            // If not a shorthand we only execute one iteration of this loop, and refer to the property directly.
+            for (unsigned j = 0; !j || j < propertyList.length(); ++j) {
+                CSSPropertyID id = propertyList.length() ? propertyList.properties()[j] : property;
+                CSSPropertyID eventId = id;
+
+                if (!animateAll) {
+                    id = propertyForAnimation(id);
+                    if (CSSPropertyMetadata::isAnimatableProperty(id))
+                        listedProperties.set(id);
+                    else
+                        continue;
+                }
+
+                // FIXME: We should transition if an !important property changes even when an animation is running,
+                // but this is a bit hard to do with the current applyMatchedProperties system.
+                if (!update->activeInterpolationsForAnimations().contains(id)
+                    && (!activeAnimations || !activeAnimations->cssAnimations().m_previousActiveInterpolationsForAnimations.contains(id))) {
+                    calculateTransitionUpdateForProperty(id, eventId, *transitionData, i, oldStyle, style, activeTransitions, update, element);
+                }
+            }
         }
     }
 
-    if (transitions) {
-        for (TransitionMap::const_iterator iter = transitions->begin(); iter != transitions->end(); ++iter) {
-            const TimedItem* timedItem = iter->value.transition;
+    if (activeTransitions) {
+        for (TransitionMap::const_iterator iter = activeTransitions->begin(); iter != activeTransitions->end(); ++iter) {
+            const AnimationPlayer& player = *iter->value.player;
             CSSPropertyID id = iter->key;
-            if (timedItem->phase() == TimedItem::PhaseAfter || !listedProperties.contains(id))
+            if (player.finishedInternal() || (!anyTransitionHadTransitionAll && !animationStyleRecalc && !listedProperties.get(id))) {
+                // TODO: Figure out why this fails on Chrome OS login page. crbug.com/365507
+                // ASSERT(player.finishedInternal() || !(activeAnimations && activeAnimations->isAnimationStyleChange()));
                 update->cancelTransition(id);
+            }
         }
     }
 }
@@ -410,250 +524,140 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const
 void CSSAnimations::cancel()
 {
     for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animations.end(); ++iter) {
-        const HashSet<RefPtr<Player> >& players = iter->value;
-        for (HashSet<RefPtr<Player> >::const_iterator animationsIter = players.begin(); animationsIter != players.end(); ++animationsIter)
-            (*animationsIter)->cancel();
+        iter->value->cancel();
+        iter->value->update(TimingUpdateOnDemand);
     }
 
-    for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transitions.end(); ++iter)
-        iter->value.transition->player()->cancel();
+    for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transitions.end(); ++iter) {
+        iter->value.player->cancel();
+        iter->value.player->update(TimingUpdateOnDemand);
+    }
 
     m_animations.clear();
     m_transitions.clear();
     m_pendingUpdate = nullptr;
 }
 
-void CSSAnimations::calculateAnimationCompositableValues(CSSAnimationUpdate* update, const Element* element)
+void CSSAnimations::calculateAnimationActiveInterpolations(CSSAnimationUpdate* update, const Element* element, double timelineCurrentTime)
 {
-    ActiveAnimations* activeAnimations = element->activeAnimations();
+    ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
     AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
 
-    if (update->newAnimations().isEmpty() && update->cancelledAnimationPlayers().isEmpty()) {
-        AnimationEffect::CompositableValueMap compositableValuesForAnimations(AnimationStack::compositableValues(animationStack, 0, 0, Animation::DefaultPriority));
-        update->adoptCompositableValuesForAnimations(compositableValuesForAnimations);
+    if (update->newAnimations().isEmpty() && update->cancelledAnimationAnimationPlayers().isEmpty()) {
+        WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::DefaultPriority, timelineCurrentTime));
+        update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations);
+        return;
     }
 
-    Vector<InertAnimation*> newAnimations;
+    WillBeHeapVector<RawPtrWillBeMember<InertAnimation> > newAnimations;
     for (size_t i = 0; i < update->newAnimations().size(); ++i) {
-        HashSet<RefPtr<InertAnimation> > animations = update->newAnimations()[i].animations;
-        for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = animations.begin(); animationsIter != animations.end(); ++animationsIter)
-            newAnimations.append(animationsIter->get());
+        newAnimations.append(update->newAnimations()[i].animation.get());
     }
-    AnimationEffect::CompositableValueMap compositableValuesForAnimations(AnimationStack::compositableValues(animationStack, &newAnimations, &update->cancelledAnimationPlayers(), Animation::DefaultPriority));
-    update->adoptCompositableValuesForAnimations(compositableValuesForAnimations);
+    WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, &newAnimations, &update->cancelledAnimationAnimationPlayers(), Animation::DefaultPriority, timelineCurrentTime));
+    update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations);
 }
 
-void CSSAnimations::calculateTransitionCompositableValues(CSSAnimationUpdate* update, const Element* element)
+void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate* update, const Element* element, double timelineCurrentTime)
 {
-    ActiveAnimations* activeAnimations = element->activeAnimations();
+    ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
     AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
 
-    AnimationEffect::CompositableValueMap compositableValuesForTransitions;
+    WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForTransitions;
     if (update->newTransitions().isEmpty() && update->cancelledTransitions().isEmpty()) {
-        compositableValuesForTransitions = AnimationStack::compositableValues(animationStack, 0, 0, Animation::TransitionPriority);
+        activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::TransitionPriority, timelineCurrentTime);
     } else {
-        Vector<InertAnimation*> newTransitions;
-        for (size_t i = 0; i < update->newTransitions().size(); ++i)
-            newTransitions.append(update->newTransitions()[i].animation.get());
+        WillBeHeapVector<RawPtrWillBeMember<InertAnimation> > newTransitions;
+        for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter)
+            newTransitions.append(iter->value.animation.get());
 
-        HashSet<const Player*> cancelledPlayers;
+        WillBeHeapHashSet<RawPtrWillBeMember<const AnimationPlayer> > cancelledAnimationPlayers;
         if (!update->cancelledTransitions().isEmpty()) {
+            ASSERT(activeAnimations);
             const TransitionMap& transitionMap = activeAnimations->cssAnimations().m_transitions;
             for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
                 ASSERT(transitionMap.contains(*iter));
-                cancelledPlayers.add(transitionMap.get(*iter).transition->player());
+                cancelledAnimationPlayers.add(transitionMap.get(*iter).player.get());
             }
         }
 
-        compositableValuesForTransitions = AnimationStack::compositableValues(animationStack, &newTransitions, &cancelledPlayers, Animation::TransitionPriority);
+        activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, &newTransitions, &cancelledAnimationPlayers, Animation::TransitionPriority, timelineCurrentTime);
     }
 
     // Properties being animated by animations don't get values from transitions applied.
-    if (!update->compositableValuesForAnimations().isEmpty() && !compositableValuesForTransitions.isEmpty()) {
-        for (AnimationEffect::CompositableValueMap::const_iterator iter = update->compositableValuesForAnimations().begin(); iter != update->compositableValuesForAnimations().end(); ++iter)
-            compositableValuesForTransitions.remove(iter->key);
+    if (!update->activeInterpolationsForAnimations().isEmpty() && !activeInterpolationsForTransitions.isEmpty()) {
+        for (WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >::const_iterator iter = update->activeInterpolationsForAnimations().begin(); iter != update->activeInterpolationsForAnimations().end(); ++iter)
+            activeInterpolationsForTransitions.remove(iter->key);
     }
-    update->adoptCompositableValuesForTransitions(compositableValuesForTransitions);
+    update->adoptActiveInterpolationsForTransitions(activeInterpolationsForTransitions);
 }
 
 void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType listenerType, const AtomicString& eventName, double elapsedTime)
 {
-    if (m_target->document().hasListenerType(listenerType))
-        m_target->document().timeline()->addEventToDispatch(m_target, WebKitAnimationEvent::create(eventName, m_name, elapsedTime));
+    if (m_target->document().hasListenerType(listenerType)) {
+        RefPtrWillBeRawPtr<WebKitAnimationEvent> event = WebKitAnimationEvent::create(eventName, m_name, elapsedTime);
+        event->setTarget(m_target);
+        m_target->document().enqueueAnimationFrameEvent(event);
+    }
 }
 
-void CSSAnimations::AnimationEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
+void CSSAnimations::AnimationEventDelegate::onEventCondition(const AnimationNode* animationNode)
 {
-    // Events for a single document are queued and dispatched as a group at
-    // the end of DocumentTimeline::serviceAnimations.
-    // FIXME: Events which are queued outside of serviceAnimations should
-    // trigger a timer to dispatch when control is released.
-    const TimedItem::Phase currentPhase = timedItem->phase();
-    const double currentIteration = timedItem->currentIteration();
-
-    // Note that the elapsedTime is measured from when the animation starts playing.
-    if (!isFirstSample && previousPhase == TimedItem::PhaseActive && currentPhase == TimedItem::PhaseActive && previousIteration != currentIteration) {
-        ASSERT(!isNull(previousIteration));
-        ASSERT(!isNull(currentIteration));
+    const AnimationNode::Phase currentPhase = animationNode->phase();
+    const double currentIteration = animationNode->currentIteration();
+
+    if (m_previousPhase != currentPhase
+        && (currentPhase == AnimationNode::PhaseActive || currentPhase == AnimationNode::PhaseAfter)
+        && (m_previousPhase == AnimationNode::PhaseNone || m_previousPhase == AnimationNode::PhaseBefore)) {
+        // The spec states that the elapsed time should be
+        // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing
+        // implementation. See crbug.com/279611
+        maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animationstart, 0);
+    }
+
+    if (currentPhase == AnimationNode::PhaseActive && m_previousPhase == currentPhase && m_previousIteration != currentIteration) {
         // We fire only a single event for all iterations thast terminate
         // between a single pair of samples. See http://crbug.com/275263. For
         // compatibility with the existing implementation, this event uses
         // the elapsedTime for the first iteration in question.
-        ASSERT(timedItem->specified().hasIterationDuration);
-        const double elapsedTime = timedItem->specified().iterationDuration * (previousIteration + 1);
+        ASSERT(!std::isnan(animationNode->specifiedTiming().iterationDuration));
+        const double elapsedTime = animationNode->specifiedTiming().iterationDuration * (m_previousIteration + 1);
         maybeDispatch(Document::ANIMATIONITERATION_LISTENER, EventTypeNames::animationiteration, elapsedTime);
-        return;
     }
-    if ((isFirstSample || previousPhase == TimedItem::PhaseBefore) && isLaterPhase(currentPhase, TimedItem::PhaseBefore)) {
-        ASSERT(timedItem->specified().startDelay > 0 || isFirstSample);
-        // The spec states that the elapsed time should be
-        // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing
-        // implementation. See crbug.com/279611
-        maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animationstart, 0);
-    }
-    if ((isFirstSample || isEarlierPhase(previousPhase, TimedItem::PhaseAfter)) && currentPhase == TimedItem::PhaseAfter)
-        maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animationend, timedItem->activeDuration());
+
+    if (currentPhase == AnimationNode::PhaseAfter && m_previousPhase != AnimationNode::PhaseAfter)
+        maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animationend, animationNode->activeDurationInternal());
+
+    m_previousPhase = currentPhase;
+    m_previousIteration = currentIteration;
+}
+
+void CSSAnimations::AnimationEventDelegate::trace(Visitor* visitor)
+{
+    visitor->trace(m_target);
+    AnimationNode::EventDelegate::trace(visitor);
 }
 
-void CSSAnimations::TransitionEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
+void CSSAnimations::TransitionEventDelegate::onEventCondition(const AnimationNode* animationNode)
 {
-    // Events for a single document are queued and dispatched as a group at
-    // the end of DocumentTimeline::serviceAnimations.
-    // FIXME: Events which are queued outside of serviceAnimations should
-    // trigger a timer to dispatch when control is released.
-    const TimedItem::Phase currentPhase = timedItem->phase();
-    if (currentPhase == TimedItem::PhaseAfter && (isFirstSample || previousPhase != currentPhase) && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
+    const AnimationNode::Phase currentPhase = animationNode->phase();
+    if (currentPhase == AnimationNode::PhaseAfter && currentPhase != m_previousPhase && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
         String propertyName = getPropertyNameString(m_property);
-        const Timing& timing = timedItem->specified();
+        const Timing& timing = animationNode->specifiedTiming();
         double elapsedTime = timing.iterationDuration;
         const AtomicString& eventType = EventTypeNames::transitionend;
         String pseudoElement = PseudoElement::pseudoElementNameForEvents(m_target->pseudoId());
-        m_target->document().transitionTimeline()->addEventToDispatch(m_target, TransitionEvent::create(eventType, propertyName, elapsedTime, pseudoElement));
+        RefPtrWillBeRawPtr<TransitionEvent> event = TransitionEvent::create(eventType, propertyName, elapsedTime, pseudoElement);
+        event->setTarget(m_target);
+        m_target->document().enqueueAnimationFrameEvent(event);
     }
-}
 
+    m_previousPhase = currentPhase;
+}
 
-bool CSSAnimations::isAnimatableProperty(CSSPropertyID property)
+void CSSAnimations::TransitionEventDelegate::trace(Visitor* visitor)
 {
-    switch (property) {
-    case CSSPropertyBackgroundColor:
-    case CSSPropertyBackgroundImage:
-    case CSSPropertyBackgroundPositionX:
-    case CSSPropertyBackgroundPositionY:
-    case CSSPropertyBackgroundSize:
-    case CSSPropertyBaselineShift:
-    case CSSPropertyBorderBottomColor:
-    case CSSPropertyBorderBottomLeftRadius:
-    case CSSPropertyBorderBottomRightRadius:
-    case CSSPropertyBorderBottomWidth:
-    case CSSPropertyBorderImageOutset:
-    case CSSPropertyBorderImageSlice:
-    case CSSPropertyBorderImageSource:
-    case CSSPropertyBorderImageWidth:
-    case CSSPropertyBorderLeftColor:
-    case CSSPropertyBorderLeftWidth:
-    case CSSPropertyBorderRightColor:
-    case CSSPropertyBorderRightWidth:
-    case CSSPropertyBorderTopColor:
-    case CSSPropertyBorderTopLeftRadius:
-    case CSSPropertyBorderTopRightRadius:
-    case CSSPropertyBorderTopWidth:
-    case CSSPropertyBottom:
-    case CSSPropertyBoxShadow:
-    case CSSPropertyClip:
-    case CSSPropertyColor:
-    case CSSPropertyFill:
-    case CSSPropertyFillOpacity:
-    case CSSPropertyFlexBasis:
-    case CSSPropertyFlexGrow:
-    case CSSPropertyFlexShrink:
-    case CSSPropertyFloodColor:
-    case CSSPropertyFloodOpacity:
-    case CSSPropertyFontSize:
-    case CSSPropertyHeight:
-    case CSSPropertyKerning:
-    case CSSPropertyLeft:
-    case CSSPropertyLetterSpacing:
-    case CSSPropertyLightingColor:
-    case CSSPropertyLineHeight:
-    case CSSPropertyListStyleImage:
-    case CSSPropertyMarginBottom:
-    case CSSPropertyMarginLeft:
-    case CSSPropertyMarginRight:
-    case CSSPropertyMarginTop:
-    case CSSPropertyMaxHeight:
-    case CSSPropertyMaxWidth:
-    case CSSPropertyMinHeight:
-    case CSSPropertyMinWidth:
-    case CSSPropertyObjectPosition:
-    case CSSPropertyOpacity:
-    case CSSPropertyOrphans:
-    case CSSPropertyOutlineColor:
-    case CSSPropertyOutlineOffset:
-    case CSSPropertyOutlineWidth:
-    case CSSPropertyPaddingBottom:
-    case CSSPropertyPaddingLeft:
-    case CSSPropertyPaddingRight:
-    case CSSPropertyPaddingTop:
-    case CSSPropertyRight:
-    case CSSPropertyStopColor:
-    case CSSPropertyStopOpacity:
-    case CSSPropertyStroke:
-    case CSSPropertyStrokeDasharray:
-    case CSSPropertyStrokeDashoffset:
-    case CSSPropertyStrokeMiterlimit:
-    case CSSPropertyStrokeOpacity:
-    case CSSPropertyStrokeWidth:
-    case CSSPropertyTextDecorationColor:
-    case CSSPropertyTextIndent:
-    case CSSPropertyTextShadow:
-    case CSSPropertyTop:
-    case CSSPropertyVisibility:
-    case CSSPropertyWebkitBackgroundSize:
-    case CSSPropertyWebkitBorderHorizontalSpacing:
-    case CSSPropertyWebkitBorderVerticalSpacing:
-    case CSSPropertyWebkitBoxShadow:
-    case CSSPropertyWebkitClipPath:
-    case CSSPropertyWebkitColumnCount:
-    case CSSPropertyWebkitColumnGap:
-    case CSSPropertyWebkitColumnRuleColor:
-    case CSSPropertyWebkitColumnRuleWidth:
-    case CSSPropertyWebkitColumnWidth:
-    case CSSPropertyWebkitFilter:
-    case CSSPropertyWebkitMaskBoxImageOutset:
-    case CSSPropertyWebkitMaskBoxImageSlice:
-    case CSSPropertyWebkitMaskBoxImageSource:
-    case CSSPropertyWebkitMaskBoxImageWidth:
-    case CSSPropertyWebkitMaskImage:
-    case CSSPropertyWebkitMaskPositionX:
-    case CSSPropertyWebkitMaskPositionY:
-    case CSSPropertyWebkitMaskSize:
-    case CSSPropertyWebkitPerspective:
-    case CSSPropertyWebkitPerspectiveOriginX:
-    case CSSPropertyWebkitPerspectiveOriginY:
-    case CSSPropertyShapeInside:
-    case CSSPropertyShapeOutside:
-    case CSSPropertyShapeMargin:
-    case CSSPropertyWebkitTextStrokeColor:
-    case CSSPropertyWebkitTransform:
-    case CSSPropertyWebkitTransformOriginX:
-    case CSSPropertyWebkitTransformOriginY:
-    case CSSPropertyWebkitTransformOriginZ:
-    case CSSPropertyWidows:
-    case CSSPropertyWidth:
-    case CSSPropertyWordSpacing:
-    case CSSPropertyZIndex:
-    case CSSPropertyZoom:
-        return true;
-    // FIXME: Shorthands should not be present in this list, but
-    // CSSPropertyAnimation implements animation of these shorthands
-    // directly and makes use of this method.
-    case CSSPropertyFlex:
-    case CSSPropertyWebkitMaskBoxImage:
-        return !RuntimeEnabledFeatures::webAnimationsCSSEnabled();
-    default:
-        return false;
-    }
+    visitor->trace(m_target);
+    AnimationNode::EventDelegate::trace(visitor);
 }
 
 const StylePropertyShorthand& CSSAnimations::animatableProperties()
@@ -663,7 +667,7 @@ const StylePropertyShorthand& CSSAnimations::animatableProperties()
     if (properties.isEmpty()) {
         for (int i = firstCSSProperty; i < lastCSSProperty; ++i) {
             CSSPropertyID id = convertToCSSPropertyID(i);
-            if (isAnimatableProperty(id))
+            if (CSSPropertyMetadata::isAnimatableProperty(id))
                 properties.append(id);
         }
         propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, properties.begin(), properties.size());
@@ -671,4 +675,65 @@ const StylePropertyShorthand& CSSAnimations::animatableProperties()
     return propertyShorthand;
 }
 
-} // namespace WebCore
+// Animation properties are not allowed to be affected by Web Animations.
+// http://dev.w3.org/fxtf/web-animations/#not-animatable
+bool CSSAnimations::isAllowedAnimation(CSSPropertyID property)
+{
+    switch (property) {
+    case CSSPropertyAnimation:
+    case CSSPropertyAnimationDelay:
+    case CSSPropertyAnimationDirection:
+    case CSSPropertyAnimationDuration:
+    case CSSPropertyAnimationFillMode:
+    case CSSPropertyAnimationIterationCount:
+    case CSSPropertyAnimationName:
+    case CSSPropertyAnimationPlayState:
+    case CSSPropertyAnimationTimingFunction:
+    case CSSPropertyDisplay:
+    case CSSPropertyTransition:
+    case CSSPropertyTransitionDelay:
+    case CSSPropertyTransitionDuration:
+    case CSSPropertyTransitionProperty:
+    case CSSPropertyTransitionTimingFunction:
+    case CSSPropertyWebkitAnimation:
+    case CSSPropertyWebkitAnimationDelay:
+    case CSSPropertyWebkitAnimationDirection:
+    case CSSPropertyWebkitAnimationDuration:
+    case CSSPropertyWebkitAnimationFillMode:
+    case CSSPropertyWebkitAnimationIterationCount:
+    case CSSPropertyWebkitAnimationName:
+    case CSSPropertyWebkitAnimationPlayState:
+    case CSSPropertyWebkitAnimationTimingFunction:
+    case CSSPropertyWebkitTransition:
+    case CSSPropertyWebkitTransitionDelay:
+    case CSSPropertyWebkitTransitionDuration:
+    case CSSPropertyWebkitTransitionProperty:
+    case CSSPropertyWebkitTransitionTimingFunction:
+        return false;
+    default:
+        return true;
+    }
+}
+
+void CSSAnimations::trace(Visitor* visitor)
+{
+#if ENABLE(OILPAN)
+    visitor->trace(m_transitions);
+    visitor->trace(m_pendingUpdate);
+    visitor->trace(m_animations);
+    visitor->trace(m_previousActiveInterpolationsForAnimations);
+#endif
+}
+
+void CSSAnimationUpdate::trace(Visitor* visitor)
+{
+#if ENABLE(OILPAN)
+    visitor->trace(m_newTransitions);
+    visitor->trace(m_activeInterpolationsForAnimations);
+    visitor->trace(m_activeInterpolationsForTransitions);
+    visitor->trace(m_newAnimations);
+    visitor->trace(m_cancelledAnimationPlayers);
+#endif
+}
+
+} // namespace blink