Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / animation / Animation.cpp
index 228b52e..d81f8ee 100644 (file)
 #include "config.h"
 #include "core/animation/Animation.h"
 
+#include "bindings/v8/Dictionary.h"
 #include "core/animation/ActiveAnimations.h"
+#include "core/animation/AnimationHelpers.h"
 #include "core/animation/CompositorAnimations.h"
+#include "core/animation/DocumentTimeline.h"
+#include "core/animation/KeyframeEffectModel.h"
 #include "core/animation/Player.h"
+#include "core/css/parser/BisonCSSParser.h"
+#include "core/css/resolver/StyleResolver.h"
 #include "core/dom/Element.h"
+#include "core/rendering/RenderLayer.h"
+#include "wtf/text/StringBuilder.h"
 
 namespace WebCore {
 
@@ -43,6 +51,272 @@ PassRefPtr<Animation> Animation::create(PassRefPtr<Element> target, PassRefPtr<A
     return adoptRef(new Animation(target, effect, timing, priority, eventDelegate));
 }
 
+static bool checkDocumentAndRenderer(Element* element)
+{
+    if (!element->inActiveDocument())
+        return false;
+    element->document().updateStyleIfNeeded();
+    if (!element->renderer())
+        return false;
+    return true;
+}
+
+PassRefPtr<Animation> Animation::create(Element* element, Vector<Dictionary> keyframeDictionaryVector, Dictionary timingInput)
+{
+    ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
+
+    // FIXME: This test will not be neccessary once resolution of keyframe values occurs at
+    // animation application time.
+    if (!checkDocumentAndRenderer(element))
+        return 0;
+
+    return createUnsafe(element, keyframeDictionaryVector, timingInput);
+}
+
+PassRefPtr<Animation> Animation::create(Element* element, Vector<Dictionary> keyframeDictionaryVector, double timingInput)
+{
+    ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
+
+    // FIXME: This test will not be neccessary once resolution of keyframe values occurs at
+    // animation application time.
+    if (!checkDocumentAndRenderer(element))
+        return 0;
+
+    return createUnsafe(element, keyframeDictionaryVector, timingInput);
+}
+
+PassRefPtr<Animation> Animation::create(Element* element, Vector<Dictionary> keyframeDictionaryVector)
+{
+    ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
+
+    // FIXME: This test will not be neccessary once resolution of keyframe values occurs at
+    // animation application time.
+    if (!checkDocumentAndRenderer(element))
+        return 0;
+
+    return createUnsafe(element, keyframeDictionaryVector);
+}
+
+void Animation::setStartDelay(Timing& timing, double startDelay)
+{
+    if (std::isfinite(startDelay))
+        timing.startDelay = startDelay;
+    else
+        timing.startDelay = 0;
+}
+
+void Animation::setEndDelay(Timing& timing, double endDelay)
+{
+    if (std::isfinite(endDelay))
+        timing.endDelay = endDelay;
+    else
+        timing.endDelay = 0;
+}
+
+void Animation::setFillMode(Timing& timing, String fillMode)
+{
+    if (fillMode == "none") {
+        timing.fillMode = Timing::FillModeNone;
+    } else if (fillMode == "backwards") {
+        timing.fillMode = Timing::FillModeBackwards;
+    } else if (fillMode == "both") {
+        timing.fillMode = Timing::FillModeBoth;
+    } else if (fillMode == "forwards") {
+        timing.fillMode = Timing::FillModeForwards;
+    } else {
+        timing.fillMode = Timing::FillModeAuto;
+    }
+}
+
+void Animation::setIterationStart(Timing& timing, double iterationStart)
+{
+    if (!std::isnan(iterationStart) && !std::isinf(iterationStart))
+        timing.iterationStart = std::max<double>(iterationStart, 0);
+    else
+        timing.iterationStart = 0;
+}
+
+void Animation::setIterationCount(Timing& timing, double iterationCount)
+{
+    if (!std::isnan(iterationCount))
+        timing.iterationCount = std::max<double>(iterationCount, 0);
+    else
+        timing.iterationCount = 1;
+}
+
+void Animation::setIterationDuration(Timing& timing, double iterationDuration)
+{
+    if (!std::isnan(iterationDuration) && iterationDuration >= 0)
+        timing.iterationDuration = iterationDuration;
+    else
+        timing.iterationDuration = std::numeric_limits<double>::quiet_NaN();
+}
+
+void Animation::setPlaybackRate(Timing& timing, double playbackRate)
+{
+    if (!std::isnan(playbackRate) && !std::isinf(playbackRate))
+        timing.playbackRate = playbackRate;
+    else
+        timing.playbackRate = 1;
+}
+
+void Animation::setPlaybackDirection(Timing& timing, String direction)
+{
+    if (direction == "reverse") {
+        timing.direction = Timing::PlaybackDirectionReverse;
+    } else if (direction == "alternate") {
+        timing.direction = Timing::PlaybackDirectionAlternate;
+    } else if (direction == "alternate-reverse") {
+        timing.direction = Timing::PlaybackDirectionAlternateReverse;
+    } else {
+        timing.direction = Timing::PlaybackDirectionNormal;
+    }
+}
+
+void Animation::setTimingFunction(Timing& timing, String timingFunctionString)
+{
+    RefPtr<CSSValue> timingFunctionValue = BisonCSSParser::parseAnimationTimingFunctionValue(timingFunctionString);
+    if (timingFunctionValue) {
+        RefPtr<TimingFunction> timingFunction = CSSToStyleMap::animationTimingFunction(timingFunctionValue.get(), false);
+        if (timingFunction) {
+            timing.timingFunction = timingFunction;
+            return;
+        }
+    }
+    timing.timingFunction = LinearTimingFunction::create();
+}
+
+void Animation::populateTiming(Timing& timing, Dictionary timingInputDictionary)
+{
+    // FIXME: This method needs to be refactored to handle invalid
+    // null, NaN, Infinity values better.
+    // See: http://www.w3.org/TR/WebIDL/#es-double
+    double startDelay = 0;
+    timingInputDictionary.get("delay", startDelay);
+    setStartDelay(timing, startDelay);
+
+    double endDelay = 0;
+    timingInputDictionary.get("endDelay", endDelay);
+    setEndDelay(timing, endDelay);
+
+    String fillMode;
+    timingInputDictionary.get("fill", fillMode);
+    setFillMode(timing, fillMode);
+
+    double iterationStart = 0;
+    timingInputDictionary.get("iterationStart", iterationStart);
+    setIterationStart(timing, iterationStart);
+
+    double iterationCount = 1;
+    timingInputDictionary.get("iterations", iterationCount);
+    setIterationCount(timing, iterationCount);
+
+    v8::Local<v8::Value> iterationDurationValue;
+    if (timingInputDictionary.get("duration", iterationDurationValue)) {
+        double iterationDuration = iterationDurationValue->NumberValue();
+        setIterationDuration(timing, iterationDuration);
+    }
+
+    double playbackRate = 1;
+    timingInputDictionary.get("playbackRate", playbackRate);
+    setPlaybackRate(timing, playbackRate);
+
+    String direction;
+    timingInputDictionary.get("direction", direction);
+    setPlaybackDirection(timing, direction);
+
+    String timingFunctionString;
+    timingInputDictionary.get("easing", timingFunctionString);
+    setTimingFunction(timing, timingFunctionString);
+
+    timing.assertValid();
+}
+
+static PassRefPtr<KeyframeEffectModel> createKeyframeEffectModel(Element* element, Vector<Dictionary> keyframeDictionaryVector)
+{
+    KeyframeEffectModel::KeyframeVector keyframes;
+    Vector<RefPtr<MutableStylePropertySet> > propertySetVector;
+
+    for (size_t i = 0; i < keyframeDictionaryVector.size(); ++i) {
+        RefPtr<MutableStylePropertySet> propertySet = MutableStylePropertySet::create();
+        propertySetVector.append(propertySet);
+
+        RefPtr<Keyframe> keyframe = Keyframe::create();
+        keyframes.append(keyframe);
+
+        double offset;
+        if (keyframeDictionaryVector[i].get("offset", offset)) {
+            keyframe->setOffset(offset);
+        }
+
+        String compositeString;
+        keyframeDictionaryVector[i].get("composite", compositeString);
+        if (compositeString == "add")
+            keyframe->setComposite(AnimationEffect::CompositeAdd);
+
+        String timingFunctionString;
+        if (keyframeDictionaryVector[i].get("easing", timingFunctionString)) {
+            RefPtr<CSSValue> timingFunctionValue = BisonCSSParser::parseAnimationTimingFunctionValue(timingFunctionString);
+            if (timingFunctionValue) {
+                keyframe->setEasing(CSSToStyleMap::animationTimingFunction(timingFunctionValue.get(), false));
+            }
+        }
+
+        Vector<String> keyframeProperties;
+        keyframeDictionaryVector[i].getOwnPropertyNames(keyframeProperties);
+
+        for (size_t j = 0; j < keyframeProperties.size(); ++j) {
+            String property = keyframeProperties[j];
+            CSSPropertyID id = camelCaseCSSPropertyNameToID(property);
+
+            // FIXME: There is no way to store invalid properties or invalid values
+            // in a Keyframe object, so for now I just skip over them. Eventually we
+            // will need to support getFrames(), which should return exactly the
+            // keyframes that were input through the API. We will add a layer to wrap
+            // KeyframeEffectModel, store input keyframes and implement getFrames.
+            if (id == CSSPropertyInvalid || !CSSAnimations::isAnimatableProperty(id))
+                continue;
+
+            String value;
+            keyframeDictionaryVector[i].get(property, value);
+            propertySet->setProperty(id, value);
+        }
+    }
+
+    // FIXME: Replace this with code that just parses, when that code is available.
+    RefPtr<KeyframeEffectModel> effect = StyleResolver::createKeyframeEffectModel(*element, propertySetVector, keyframes);
+    return effect;
+}
+
+PassRefPtr<Animation> Animation::createUnsafe(Element* element, Vector<Dictionary> keyframeDictionaryVector, Dictionary timingInput)
+{
+    RefPtr<KeyframeEffectModel> effect = createKeyframeEffectModel(element, keyframeDictionaryVector);
+
+    Timing timing;
+    populateTiming(timing, timingInput);
+
+    return create(element, effect, timing);
+}
+
+PassRefPtr<Animation> Animation::createUnsafe(Element* element, Vector<Dictionary> keyframeDictionaryVector, double timingInput)
+{
+    RefPtr<KeyframeEffectModel> effect = createKeyframeEffectModel(element, keyframeDictionaryVector);
+
+    Timing timing;
+    if (!std::isnan(timingInput))
+        timing.iterationDuration = std::max<double>(timingInput, 0);
+
+    return create(element, effect, timing);
+}
+
+PassRefPtr<Animation> Animation::createUnsafe(Element* element, Vector<Dictionary> keyframeDictionaryVector)
+{
+    RefPtr<KeyframeEffectModel> effect = createKeyframeEffectModel(element, keyframeDictionaryVector);
+    Timing timing;
+
+    return create(element, effect, timing);
+}
+
 Animation::Animation(PassRefPtr<Element> target, PassRefPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate)
     : TimedItem(timing, eventDelegate)
     , m_target(target)
@@ -98,7 +372,15 @@ void Animation::clearEffects()
     ASSERT(player());
     ASSERT(m_activeInAnimationStack);
     ensureAnimationStack(m_target.get()).remove(this);
-    cancelAnimationOnCompositor();
+
+    {
+        // FIXME: clearEffects is called from withins style recalc.
+        // This queries compositingState, which is not necessarily up to date.
+        // https://code.google.com/p/chromium/issues/detail?id=339847
+        DisableCompositingQueryAsserts disabler;
+        cancelAnimationOnCompositor();
+    }
+
     m_activeInAnimationStack = false;
     m_compositableValues.clear();
     m_target->setNeedsAnimationStyleRecalc();
@@ -120,28 +402,38 @@ bool Animation::updateChildrenAndEffects() const
     return false;
 }
 
-double Animation::calculateTimeToEffectChange(double localTime, double timeToNextIteration) const
+double Animation::calculateTimeToEffectChange(bool forwards, double localTime, double timeToNextIteration) const
 {
-    const double activeStartTime = startTime() + specified().startDelay;
+    const double start = startTime() + specifiedTiming().startDelay;
+    const double end = start + activeDuration();
+
     switch (phase()) {
     case PhaseBefore:
-        return activeStartTime - localTime;
+        ASSERT(start >= localTime);
+        return forwards
+            ? start - localTime
+            : std::numeric_limits<double>::infinity();
     case PhaseActive:
-        if (hasActiveAnimationsOnCompositor()) {
+        if (forwards && hasActiveAnimationsOnCompositor()) {
+            ASSERT(specifiedTiming().playbackRate == 1);
             // Need service to apply fill / fire events.
-            const double activeEndTime = activeStartTime + activeDuration();
-            return std::min(activeEndTime - localTime, timeToNextIteration);
+            return std::min(end - localTime, timeToNextIteration);
         }
         return 0;
     case PhaseAfter:
+        ASSERT(localTime >= end);
         // If this Animation is still in effect then it will need to update
         // when its parent goes out of effect. We have no way of knowing when
         // that will be, however, so the parent will need to supply it.
-        return std::numeric_limits<double>::infinity();
+        return forwards
+            ? std::numeric_limits<double>::infinity()
+            : localTime - end;
     case PhaseNone:
+        ASSERT(player() && player()->timeline() && !player()->timeline()->hasStarted());
+        return std::numeric_limits<double>::infinity();
     default:
         ASSERT_NOT_REACHED();
-        return 0;
+        return std::numeric_limits<double>::infinity();
     }
 }
 
@@ -149,7 +441,7 @@ bool Animation::isCandidateForAnimationOnCompositor() const
 {
     if (!effect() || !m_target)
         return false;
-    return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(specified(), *effect());
+    return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(specifiedTiming(), *effect());
 }
 
 bool Animation::maybeStartAnimationOnCompositor()
@@ -159,7 +451,7 @@ bool Animation::maybeStartAnimationOnCompositor()
         return false;
     if (!CompositorAnimations::instance()->canStartAnimationOnCompositor(*m_target.get()))
         return false;
-    if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target.get(), specified(), *effect(), m_compositorAnimationIds))
+    if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target.get(), specifiedTiming(), *effect(), m_compositorAnimationIds))
         return false;
     ASSERT(!m_compositorAnimationIds.isEmpty());
     return true;