Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / animation / css / CSSAnimations.cpp
index 6b4179a..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/AnimationTimeline.h"
 #include "core/animation/CompositorAnimations.h"
-#include "core/animation/DocumentTimeline.h"
 #include "core/animation/KeyframeEffectModel.h"
+#include "core/animation/LegacyStyleInterpolation.h"
 #include "core/animation/css/CSSAnimatableValueFactory.h"
-#include "core/animation/css/CSSAnimationDataList.h"
-#include "core/animation/css/CSSPropertyAnimation.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/dom/PseudoElement.h"
-#include "core/events/ThreadLocalEventNames.h"
+#include "core/dom/StyleEngine.h"
 #include "core/events/TransitionEvent.h"
 #include "core/events/WebKitAnimationEvent.h"
 #include "core/frame/UseCounter.h"
 #include "wtf/BitArray.h"
 #include "wtf/HashSet.h"
 
-namespace WebCore {
+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;
-}
-
-bool isLaterPhase(TimedItem::Phase target, TimedItem::Phase reference)
-{
-    ASSERT(target != TimedItem::PhaseNone);
-    ASSERT(reference != TimedItem::PhaseNone);
-    return target > reference;
-}
-
-static PassRefPtr<TimingFunction> generateTimingFunction(const KeyframeEffectModel::KeyframeVector keyframes, const HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions)
-{
-    // Generate the chained timing function. Note that timing functions apply
-    // from the keyframe in which they're specified to the next keyframe.
-    bool isTimingFunctionLinearThroughout = true;
-    RefPtr<ChainedTimingFunction> chainedTimingFunction = ChainedTimingFunction::create();
-    for (size_t i = 0; i < keyframes.size() - 1; ++i) {
-        double lowerBound = keyframes[i]->offset();
-        ASSERT(lowerBound >=0 && lowerBound < 1);
-        double upperBound = keyframes[i + 1]->offset();
-        ASSERT(upperBound > 0 && upperBound <= 1);
-        TimingFunction* timingFunction = perKeyframeTimingFunctions.get(lowerBound);
-        isTimingFunctionLinearThroughout &= timingFunction->type() == TimingFunction::LinearFunction;
-        chainedTimingFunction->appendSegment(upperBound, timingFunction);
-    }
-    if (isTimingFunctionLinearThroughout)
-        return LinearTimingFunction::create();
-    return chainedTimingFunction;
+    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;
 }
 
 static void resolveKeyframes(StyleResolver* resolver, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, const AtomicString& name, TimingFunction* defaultTimingFunction,
-    Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > >& keyframesAndTimingFunctions)
+    AnimatableValueKeyframeVector& keyframes)
 {
     // When the element is null, use its parent for scoping purposes.
     const Element* elementForScoping = element ? element : &parentElement;
@@ -102,46 +90,48 @@ static void resolveKeyframes(StyleResolver* resolver, Element* element, const El
     if (!keyframesRule)
         return;
 
-    const Vector<RefPtr<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
+    const WillBeHeapVector<RefPtrWillBeMember<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
     if (styleKeyframes.isEmpty())
         return;
 
     // Construct and populate the style for each keyframe
-    PropertySet specifiedProperties;
-    KeyframeEffectModel::KeyframeVector keyframes;
-    HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions;
+    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);
-        RefPtr<Keyframe> keyframe = Keyframe::create();
+        RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframe = AnimatableValueKeyframe::create();
         const Vector<double>& offsets = styleKeyframe->keys();
         ASSERT(!offsets.isEmpty());
         keyframe->setOffset(offsets[0]);
-        TimingFunction* timingFunction = defaultTimingFunction;
-        const StylePropertySet* properties = styleKeyframe->properties();
-        for (unsigned j = 0; j < properties->propertyCount(); j++) {
-            CSSPropertyID property = properties->propertyAt(j).id();
-            specifiedProperties.add(property);
-            if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction)
-                timingFunction = KeyframeValue::timingFunction(*keyframeStyle);
-            else if (CSSAnimations::isAnimatableProperty(property))
+        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());
+            }
         }
         keyframes.append(keyframe);
         // The last keyframe specified at a given offset is used.
-        perKeyframeTimingFunctions.set(offsets[0], timingFunction);
         for (size_t j = 1; j < offsets.size(); ++j) {
-            keyframes.append(keyframe->cloneWithOffset(offsets[j]));
-            perKeyframeTimingFunctions.set(offsets[j], timingFunction);
+            keyframes.append(toAnimatableValueKeyframe(keyframe->cloneWithOffset(offsets[j]).get()));
         }
     }
     ASSERT(!keyframes.isEmpty());
 
-    if (!perKeyframeTimingFunctions.contains(0))
-        perKeyframeTimingFunctions.set(0, defaultTimingFunction);
-
-    for (PropertySet::const_iterator iter = specifiedProperties.begin(); iter != specifiedProperties.end(); ++iter) {
+    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));
@@ -159,16 +149,18 @@ static void resolveKeyframes(StyleResolver* resolver, Element* element, const El
     keyframes.shrink(targetIndex + 1);
 
     // Add 0% and 100% keyframes if absent.
-    RefPtr<Keyframe> startKeyframe = keyframes[0];
+    RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = keyframes[0];
     if (startKeyframe->offset()) {
-        startKeyframe = Keyframe::create();
+        startKeyframe = AnimatableValueKeyframe::create();
         startKeyframe->setOffset(0);
+        startKeyframe->setEasing(defaultTimingFunction);
         keyframes.prepend(startKeyframe);
     }
-    RefPtr<Keyframe> endKeyframe = keyframes[keyframes.size() - 1];
+    RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = keyframes[keyframes.size() - 1];
     if (endKeyframe->offset() != 1) {
-        endKeyframe = Keyframe::create();
+        endKeyframe = AnimatableValueKeyframe::create();
         endKeyframe->setOffset(1);
+        endKeyframe->setEasing(defaultTimingFunction);
         keyframes.append(endKeyframe);
     }
     ASSERT(keyframes.size() >= 2);
@@ -194,7 +186,7 @@ static void resolveKeyframes(StyleResolver* resolver, Element* element, const El
             bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains(property);
             if (!startNeedsValue && !endNeedsValue)
                 continue;
-            RefPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style);
+            RefPtrWillBeRawPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style);
             if (startNeedsValue)
                 startKeyframe->setPropertyValue(property, snapshotValue.get());
             if (endNeedsValue)
@@ -203,137 +195,17 @@ static void resolveKeyframes(StyleResolver* resolver, Element* element, const El
     }
     ASSERT(startKeyframe->properties().size() == allProperties.size());
     ASSERT(endKeyframe->properties().size() == allProperties.size());
-
-    // Determine how many keyframes specify each property. Note that this must
-    // be done after we've filled in end keyframes.
-    typedef HashCountedSet<CSSPropertyID> PropertyCountedSet;
-    PropertyCountedSet propertyCounts;
-    for (size_t i = 0; i < numKeyframes; ++i) {
-        const PropertySet& properties = keyframes[i]->properties();
-        for (PropertySet::const_iterator iter = properties.begin(); iter != properties.end(); ++iter)
-            propertyCounts.add(*iter);
-    }
-
-    // Split keyframes into groups, where each group contains only keyframes
-    // which specify all properties used in that group. Each group is animated
-    // in a separate animation, to allow per-keyframe timing functions to be
-    // applied correctly.
-    for (PropertyCountedSet::const_iterator iter = propertyCounts.begin(); iter != propertyCounts.end(); ++iter) {
-        const CSSPropertyID property = iter->key;
-        const size_t count = iter->value;
-        ASSERT(count <= numKeyframes);
-        if (count == numKeyframes)
-            continue;
-        KeyframeEffectModel::KeyframeVector splitOutKeyframes;
-        for (size_t i = 0; i < numKeyframes; i++) {
-            Keyframe* keyframe = keyframes[i].get();
-            if (!keyframe->properties().contains(property)) {
-                ASSERT(i && i != numKeyframes - 1);
-                continue;
-            }
-            RefPtr<Keyframe> clonedKeyframe = Keyframe::create();
-            clonedKeyframe->setOffset(keyframe->offset());
-            clonedKeyframe->setComposite(keyframe->composite());
-            clonedKeyframe->setPropertyValue(property, keyframe->propertyValue(property));
-            splitOutKeyframes.append(clonedKeyframe);
-            // Note that it's OK if this keyframe ends up having no
-            // properties. This can only happen when none of the properties
-            // are specified in all keyframes, in which case we won't animate
-            // anything with these keyframes.
-            keyframe->clearPropertyValue(property);
-        }
-        ASSERT(!splitOutKeyframes.first()->offset());
-        ASSERT(splitOutKeyframes.last()->offset() == 1);
-#ifndef NDEBUG
-        for (size_t j = 0; j < splitOutKeyframes.size(); ++j)
-            ASSERT(splitOutKeyframes[j]->properties().size() == 1);
-#endif
-        keyframesAndTimingFunctions.append(std::make_pair(splitOutKeyframes, generateTimingFunction(splitOutKeyframes, perKeyframeTimingFunctions)));
-    }
-
-    unsigned numPropertiesSpecifiedInAllKeyframes = keyframes.first()->properties().size();
-#ifndef NDEBUG
-    for (size_t i = 1; i < numKeyframes; ++i)
-        ASSERT(keyframes[i]->properties().size() == numPropertiesSpecifiedInAllKeyframes);
-#endif
-
-    // If the animation specifies any keyframes, we always provide at least one
-    // vector of resolved keyframes, even if no properties are animated.
-    if (numPropertiesSpecifiedInAllKeyframes || keyframesAndTimingFunctions.isEmpty())
-        keyframesAndTimingFunctions.append(std::make_pair(keyframes, generateTimingFunction(keyframes, perKeyframeTimingFunctions)));
-}
-
-// 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();
-    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();
-        }
-    } 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();
-        }
-    }
-
-    // For CSS, the constraints on the timing properties are tighter than in
-    // the general case of the Web Animations model.
-    timing.assertValid();
-    ASSERT(!timing.iterationStart);
-    ASSERT(timing.playbackRate == 1);
-    ASSERT(!std::isinf(timing.iterationDuration));
-
-    isPaused = animationData->isPlayStateSet() && animationData->playState() == AnimPlayStatePaused;
-    return animationData->isTimingFunctionSet() ? animationData->timingFunction() : CSSAnimationData::initialAnimationTimingFunction();
 }
 
 } // namespace
 
 const StyleRuleKeyframes* CSSAnimations::matchScopedKeyframesRule(StyleResolver* resolver, const Element* element, const StringImpl* animationName)
 {
-    if (resolver->styleTreeHasOnlyScopedResolverForDocument())
-        return resolver->styleTreeScopedStyleResolverForDocument()->keyframeStylesForAnimation(animationName);
+    // 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);
 
-    Vector<ScopedStyleResolver*, 8> stack;
+    WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8> stack;
     resolver->styleTreeResolveScopedKeyframesRules(element, stack);
     if (stack.isEmpty())
         return 0;
@@ -345,13 +217,17 @@ const StyleRuleKeyframes* CSSAnimations::matchScopedKeyframesRule(StyleResolver*
     return 0;
 }
 
-PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
+CSSAnimations::CSSAnimations()
 {
-    OwnPtr<CSSAnimationUpdate> update = adoptPtr(new CSSAnimationUpdate());
+}
+
+PassOwnPtrWillBeRawPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
+{
+    OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = adoptPtrWillBeNoop(new CSSAnimationUpdate());
     calculateAnimationUpdate(update.get(), element, parentElement, style, parentStyle, resolver);
-    calculateAnimationCompositableValues(update.get(), element);
+    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();
 }
 
@@ -359,14 +235,14 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element
 {
     const ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
 
-#if ASSERT_DISABLED
+#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 CSSAnimationDataList* animationDataList = style.animations();
+    const CSSAnimationData* animationData = style.animations();
     const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : 0;
 
     HashSet<AtomicString> inactive;
@@ -375,12 +251,12 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element
             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())
+        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,
@@ -389,14 +265,8 @@ 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);
                     }
@@ -404,21 +274,14 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element
                 }
             }
 
-            Timing timing;
-            bool isPaused;
-            RefPtr<TimingFunction> defaultTimingFunction = timingFromAnimationData(animationData, timing, isPaused);
-            Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > > keyframesAndTimingFunctions;
-            resolveKeyframes(resolver, element, parentElement, style, parentStyle, 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 KeyframeEffectModel to skip normalization.
-                    animations.add(InertAnimation::create(KeyframeEffectModel::create(keyframesAndTimingFunctions[j].first), timing, isPaused));
-                }
+            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, animations);
+                update->startAnimation(animationName, InertAnimation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused));
             }
         }
     }
@@ -426,20 +289,20 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element
     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));
+        update->cancelAnimation(*iter, *cssAnimations->m_animations.get(*iter));
     }
 }
 
 void CSSAnimations::maybeApplyPendingUpdate(Element* element)
 {
     if (!m_pendingUpdate) {
-        m_previousCompositableValuesForAnimations.clear();
+        m_previousActiveInterpolationsForAnimations.clear();
         return;
     }
 
-    OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
+    OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
 
-    m_previousCompositableValuesForAnimations.swap(update->compositableValuesForAnimations());
+    m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations());
 
     // FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here
     // since we call this from recalc style.
@@ -447,41 +310,31 @@ void CSSAnimations::maybeApplyPendingUpdate(Element* element)
     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);
-            if (isFirstPlayerPaused)
-                player->unpause();
-            else
-                player->pause();
-        }
+        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->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release());
-            Player* player = element->document().timeline()->createPlayer(animation.get());
-            if (inertAnimation->paused())
-                player->pause();
-            element->document().cssPendingAnimations().add(player);
-            player->update();
-            players.add(player);
-        }
-        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
@@ -489,15 +342,17 @@ void CSSAnimations::maybeApplyPendingUpdate(Element* element)
     // 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.
-    HashMap<CSSPropertyID, std::pair<RefPtr<Animation>, double> > retargetedCompositorTransitions;
-    const ActiveAnimations* activeAnimations = element->activeAnimations();
+    WillBeHeapHashMap<CSSPropertyID, std::pair<RefPtrWillBeMember<Animation>, double> > retargetedCompositorTransitions;
     for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
         CSSPropertyID id = *iter;
         ASSERT(m_transitions.contains(id));
-        Player* player = m_transitions.take(id).transition->player();
-        if (activeAnimations && activeAnimations->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end())
-            retargetedCompositorTransitions.add(id, std::pair<RefPtr<Animation>, double>(toAnimation(player->source()), player->startTime()));
+
+        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 (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter) {
@@ -509,42 +364,46 @@ void CSSAnimations::maybeApplyPendingUpdate(Element* element)
 
         CSSPropertyID id = newTransition.id;
         InertAnimation* inertAnimation = newTransition.animation.get();
-        OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionEventDelegate(element, id));
+        OwnPtrWillBeRawPtr<TransitionEventDelegate> eventDelegate = adoptPtrWillBeNoop(new TransitionEventDelegate(element, newTransition.eventId));
 
-        RefPtr<AnimationEffect> effect = inertAnimation->effect();
+        RefPtrWillBeRawPtr<AnimationEffect> effect = inertAnimation->effect();
 
         if (retargetedCompositorTransitions.contains(id)) {
-            const std::pair<RefPtr<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id);
-            RefPtr<Animation> oldAnimation = oldTransition.first;
+            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().transitionTimeline()->currentTime() - oldStartTime;
-            oldAnimation->updateInheritedTime(inheritedTime);
-            KeyframeEffectModel* oldEffect = toKeyframeEffectModel(inertAnimation->effect());
-            const KeyframeEffectModel::KeyframeVector& frames = oldEffect->getFrames();
-            KeyframeEffectModel::KeyframeVector newFrames;
-            newFrames.append(frames[0]->clone());
+            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);
-            ASSERT(oldAnimation->compositableValues()->size() == 1);
-            const AnimationEffect::CompositableValue* compositableValue = oldAnimation->compositableValues()->at(0).second.get();
-            ASSERT(!compositableValue->dependsOnUnderlyingValue());
-            newFrames[0]->setPropertyValue(id, compositableValue->compositeOnto(0).get());
-            newFrames.append(frames[1]->clone());
-            effect = KeyframeEffectModel::create(newFrames);
+            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);
         }
-        RefPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release());
-        RefPtr<Player> player = element->document().transitionTimeline()->createPlayer(transition.get());
-        player->update();
-        element->document().cssPendingAnimations().add(player.get());
-        runningTransition.transition = transition.get();
+
+        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(CSSPropertyID id, const CSSAnimationData* anim, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* activeTransitions, CSSAnimationUpdate* update, const Element* element)
+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)
 {
-    RefPtr<AnimatableValue> to;
+    RefPtrWillBeRawPtr<AnimatableValue> to = nullptr;
     if (activeTransitions) {
         TransitionMap::const_iterator activeTransitionIter = activeTransitions->find(id);
         if (activeTransitionIter != activeTransitions->end()) {
@@ -557,43 +416,37 @@ void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, const
         }
     }
 
-    if (anim->duration() + anim->delay() <= 0)
-        return;
-
-    if (CSSPropertyAnimation::propertiesEqual(id, &oldStyle, &style))
+    if (CSSPropertyEquality::propertiesEqual(id, oldStyle, style))
         return;
     if (!to)
         to = CSSAnimatableValueFactory::create(id, style);
 
-    RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
+    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;
 
-    KeyframeEffectModel::KeyframeVector keyframes;
+    Timing timing = transitionData.convertToTiming(transitionIndex);
+    if (timing.startDelay + timing.iterationDuration <= 0)
+        return;
+
+    AnimatableValueKeyframeVector keyframes;
 
-    RefPtr<Keyframe> startKeyframe = Keyframe::create();
+    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();
+    RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = AnimatableValueKeyframe::create();
     endKeyframe->setPropertyValue(id, to.get());
     endKeyframe->setOffset(1);
     keyframes.append(endKeyframe);
 
-    RefPtr<KeyframeEffectModel> effect = KeyframeEffectModel::create(keyframes);
-
-    Timing timing;
-    bool isPaused;
-    RefPtr<TimingFunction> timingFunction = timingFromAnimationData(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, from.get(), 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());
 }
 
@@ -604,38 +457,42 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const
 
     ActiveAnimations* activeAnimations = element->activeAnimations();
     const TransitionMap* activeTransitions = activeAnimations ? &activeAnimations->cssAnimations().m_transitions : 0;
+    const CSSTransitionData* transitionData = style.transitions();
 
-#if ASSERT_DISABLED
-    // 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();
-#else
+#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 anyTransitionHadAnimateAll = false;
+    bool anyTransitionHadTransitionAll = false;
     const RenderObject* renderer = element->renderer();
-    if (!animationStyleRecalc && style.display() != NONE && renderer && renderer->style() && style.transitions()) {
+    if (!animationStyleRecalc && style.display() != NONE && renderer && renderer->style() && transitionData) {
         const RenderStyle& oldStyle = *renderer->style();
 
-        for (size_t i = 0; i < style.transitions()->size(); ++i) {
-            const CSSAnimationData* anim = style.transitions()->animation(i);
-            CSSAnimationData::AnimationMode mode = anim->animationMode();
-            if (mode == CSSAnimationData::AnimateNone)
+        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 == CSSAnimationData::AnimateAll;
-            ASSERT(animateAll || mode == CSSAnimationData::AnimateSingleProperty);
+            bool animateAll = mode == CSSTransitionData::TransitionAll;
+            ASSERT(animateAll || mode == CSSTransitionData::TransitionSingleProperty);
             if (animateAll)
-                anyTransitionHadAnimateAll = true;
-            const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(anim->property());
+                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] : anim->property();
+                CSSPropertyID id = propertyList.length() ? propertyList.properties()[j] : property;
+                CSSPropertyID eventId = id;
 
                 if (!animateAll) {
-                    if (CSSAnimations::isAnimatableProperty(id))
+                    id = propertyForAnimation(id);
+                    if (CSSPropertyMetadata::isAnimatableProperty(id))
                         listedProperties.set(id);
                     else
                         continue;
@@ -643,9 +500,9 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const
 
                 // 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(id)
-                    && (!activeAnimations || !activeAnimations->cssAnimations().m_previousCompositableValuesForAnimations.contains(id))) {
-                    calculateTransitionUpdateForProperty(id, anim, oldStyle, style, activeTransitions, update, element);
+                if (!update->activeInterpolationsForAnimations().contains(id)
+                    && (!activeAnimations || !activeAnimations->cssAnimations().m_previousActiveInterpolationsForAnimations.contains(id))) {
+                    calculateTransitionUpdateForProperty(id, eventId, *transitionData, i, oldStyle, style, activeTransitions, update, element);
                 }
             }
         }
@@ -653,10 +510,11 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const
 
     if (activeTransitions) {
         for (TransitionMap::const_iterator iter = activeTransitions->begin(); iter != activeTransitions->end(); ++iter) {
-            const TimedItem* timedItem = iter->value.transition;
+            const AnimationPlayer& player = *iter->value.player;
             CSSPropertyID id = iter->key;
-            if (timedItem->phase() == TimedItem::PhaseAfter || (!anyTransitionHadAnimateAll && !animationStyleRecalc && !listedProperties.get(id))) {
-                ASSERT(timedItem->phase() == TimedItem::PhaseAfter || !(activeAnimations && activeAnimations->isAnimationStyleChange()));
+            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);
             }
         }
@@ -666,247 +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 ? 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 ? 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;
+        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(!std::isnan(timedItem->specifiedTiming().iterationDuration));
-        const double elapsedTime = timedItem->specifiedTiming().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->specifiedTiming().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::TransitionEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
+void CSSAnimations::AnimationEventDelegate::trace(Visitor* visitor)
 {
-    // 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)) {
+    visitor->trace(m_target);
+    AnimationNode::EventDelegate::trace(visitor);
+}
+
+void CSSAnimations::TransitionEventDelegate::onEventCondition(const AnimationNode* animationNode)
+{
+    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->specifiedTiming();
+        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 CSSPropertyShapeImageThreshold:
-    case CSSPropertyWebkitTextStrokeColor:
-    case CSSPropertyWebkitTransform:
-    case CSSPropertyWebkitTransformOriginX:
-    case CSSPropertyWebkitTransformOriginY:
-    case CSSPropertyWebkitTransformOriginZ:
-    case CSSPropertyWidows:
-    case CSSPropertyWidth:
-    case CSSPropertyWordSpacing:
-    case CSSPropertyZIndex:
-    case CSSPropertyZoom:
-        return true;
-    default:
-        return false;
-    }
+    visitor->trace(m_target);
+    AnimationNode::EventDelegate::trace(visitor);
 }
 
 const StylePropertyShorthand& CSSAnimations::animatableProperties()
@@ -916,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());
@@ -924,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