Chromium Code Reviews| Index: Source/core/animation/ElementAnimation.cpp |
| diff --git a/Source/core/animation/ElementAnimation.cpp b/Source/core/animation/ElementAnimation.cpp |
| index 2588c529051033923ea781342ae7919137df7d06..b2426893ad395ab3f4b64e0f51f558fb04052369 100644 |
| --- a/Source/core/animation/ElementAnimation.cpp |
| +++ b/Source/core/animation/ElementAnimation.cpp |
| @@ -32,9 +32,9 @@ |
| #include "core/animation/ElementAnimation.h" |
| #include "bindings/v8/Dictionary.h" |
| +#include "bindings/v8/ScriptValue.h" |
| #include "core/animation/DocumentTimeline.h" |
| #include "core/css/parser/BisonCSSParser.h" |
| -#include "core/css/RuntimeCSSEnabled.h" |
| #include "core/css/resolver/StyleResolver.h" |
| #include "wtf/text/StringBuilder.h" |
| #include <algorithm> |
| @@ -59,22 +59,122 @@ CSSPropertyID ElementAnimation::camelCaseCSSPropertyNameToID(const String& prope |
| return id; |
| } |
| -void ElementAnimation::animate(Element* element, Vector<Dictionary> keyframeDictionaryVector, double duration) |
| +void ElementAnimation::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); |
| + if (!isnan(startDelay) && !isinf(startDelay)) |
| + timing.startDelay = startDelay; |
| + |
| + String fillMode; |
| + timingInputDictionary.get("fill", fillMode); |
| + // FIXME: This will need to be changed to "forwards" when |
| + // Timing.h implements the spec change that makes default |
| + // fill mode "none". |
|
dstockwell
2014/01/21 20:47:29
The default in the spec seems to be "auto"
rjwright
2014/01/22 03:56:08
Done.
|
| + if (fillMode == "none") { |
| + timing.fillMode = Timing::FillModeNone; |
| + } else if (fillMode == "backwards") { |
| + timing.fillMode = Timing::FillModeBackwards; |
| + } else if (fillMode == "both") { |
| + timing.fillMode = Timing::FillModeBoth; |
| + } |
| + |
| + double iterationStart = 0; |
| + timingInputDictionary.get("iterationStart", iterationStart); |
| + if (!isnan(iterationStart) && !isinf(iterationStart)) |
| + timing.iterationStart = std::max<double>(iterationStart, 0); |
| + |
| + double iterationCount = 1; |
| + timingInputDictionary.get("iterations", iterationCount); |
| + if (!isnan(iterationCount)) |
| + timing.iterationCount = std::max<double>(iterationCount, 0); |
| + |
| + v8::Local<v8::Value> iterationDurationValue; |
|
dstockwell
2014/01/21 20:47:29
We shouldn't be using v8::Local in core/, but ther
rjwright
2014/01/22 03:56:08
Done.
|
| + bool hasIterationDurationValue = timingInputDictionary.get("duration", iterationDurationValue); |
| + if (hasIterationDurationValue) { |
| + if (iterationDurationValue->IsString()) { |
| + // All strings are treated as 'auto' except strings that are numbers, e.g. '1', 'Infinity'. |
|
dstockwell
2014/01/21 20:47:29
Looks like it should actually be that numbers less
rjwright
2014/01/22 03:56:08
Done.
|
| + double iterationDuration = iterationDurationValue->NumberValue(); |
| + if (!isnan(iterationDuration)) |
| + timing.iterationDuration = std::max<double>(iterationDuration, 0); |
| + timing.hasIterationDuration = true; |
| + } else if (iterationDurationValue->IsNumber()) { |
| + double iterationDuration = iterationDurationValue->NumberValue(); |
| + if (!isnan(iterationDuration)) { |
| + timing.iterationDuration = std::max<double>(iterationDuration, 0); |
| + timing.hasIterationDuration = true; |
| + } |
| + } |
| + } |
| + |
| + double playbackRate = 1; |
| + timingInputDictionary.get("playbackRate", playbackRate); |
| + if (!isnan(playbackRate) && !isinf(playbackRate)) |
| + timing.playbackRate = playbackRate; |
| + |
| + String direction; |
| + timingInputDictionary.get("direction", 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; |
| + } |
| + |
| + timing.assertValid(); |
| +} |
| + |
| +static bool checkDocumentAndRenderer(Element* element) |
| +{ |
| + if (!element->inActiveDocument()) |
| + return false; |
| + element->document().updateStyleIfNeeded(); |
| + if (!element->renderer()) |
| + return false; |
| + return true; |
| +} |
| + |
| +void ElementAnimation::animate(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 (!element->inActiveDocument()) |
| + if (!checkDocumentAndRenderer(element)) |
| return; |
| - element->document().updateStyleIfNeeded(); |
| - if (!element->renderer()) |
| + |
| + startAnimation(element, keyframeDictionaryVector, timingInput); |
| +} |
| + |
| +void ElementAnimation::animate(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; |
| + |
| + startAnimation(element, keyframeDictionaryVector, timingInput); |
| +} |
| + |
| +void ElementAnimation::animate(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; |
| - startAnimation(element, keyframeDictionaryVector, duration); |
| + startAnimation(element, keyframeDictionaryVector); |
| } |
| -void ElementAnimation::startAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, double duration) |
| +static PassRefPtr<KeyframeEffectModel> createKeyframeEffectModel(Element* element, Vector<Dictionary> keyframeDictionaryVector) |
| { |
| KeyframeEffectModel::KeyframeVector keyframes; |
| Vector<RefPtr<MutableStylePropertySet> > propertySetVector; |
| @@ -94,7 +194,7 @@ void ElementAnimation::startAnimation(Element* element, Vector<Dictionary> keyfr |
| // keyframes without specified offsets. This check can be removed when |
| // that funcitonality is implemented. |
| ASSERT_NOT_REACHED(); |
| - return; |
| + return 0; |
| } |
| String compositeString; |
| @@ -107,7 +207,7 @@ void ElementAnimation::startAnimation(Element* element, Vector<Dictionary> keyfr |
| for (size_t j = 0; j < keyframeProperties.size(); ++j) { |
| String property = keyframeProperties[j]; |
| - CSSPropertyID id = camelCaseCSSPropertyNameToID(property); |
| + CSSPropertyID id = ElementAnimation::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 |
| @@ -125,15 +225,43 @@ void ElementAnimation::startAnimation(Element* element, Vector<Dictionary> keyfr |
| // FIXME: Replace this with code that just parses, when that code is available. |
| RefPtr<KeyframeEffectModel> effect = StyleResolver::createKeyframeEffectModel(*element, propertySetVector, keyframes); |
| + return effect; |
| +} |
| + |
| +void ElementAnimation::startAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, Dictionary timingInput) |
| +{ |
| + RefPtr<KeyframeEffectModel> effect = createKeyframeEffectModel(element, keyframeDictionaryVector); |
| + |
| + Timing timing; |
| + populateTiming(timing, timingInput); |
| + |
| + RefPtr<Animation> animation = Animation::create(element, effect, timing); |
| + DocumentTimeline* timeline = element->document().timeline(); |
| + ASSERT(timeline); |
| + timeline->play(animation.get()); |
| +} |
| + |
| +void ElementAnimation::startAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, double timingInput) |
| +{ |
| + RefPtr<KeyframeEffectModel> effect = createKeyframeEffectModel(element, keyframeDictionaryVector); |
| + |
| + Timing timing; |
| + if (!isnan(timingInput)) { |
| + timing.hasIterationDuration = true; |
| + timing.iterationDuration = std::max<double>(timingInput, 0); |
| + } |
| + |
| + RefPtr<Animation> animation = Animation::create(element, effect, timing); |
| + DocumentTimeline* timeline = element->document().timeline(); |
| + ASSERT(timeline); |
| + timeline->play(animation.get()); |
| +} |
| + |
| +void ElementAnimation::startAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector) |
| +{ |
| + RefPtr<KeyframeEffectModel> effect = createKeyframeEffectModel(element, keyframeDictionaryVector); |
| - // FIXME: Totally hardcoded Timing for now. Will handle timing parameters later. |
| Timing timing; |
| - // FIXME: Currently there is no way to tell whether or not an iterationDuration |
| - // has been specified (becauser the default argument is 0). So any animation |
| - // created using Element.animate() will have a timing with hasIterationDuration() |
| - // == true. |
| - timing.hasIterationDuration = true; |
| - timing.iterationDuration = std::max<double>(duration, 0); |
| RefPtr<Animation> animation = Animation::create(element, effect, timing); |
| DocumentTimeline* timeline = element->document().timeline(); |