2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "core/animation/Animation.h"
34 #include "bindings/v8/Dictionary.h"
35 #include "core/animation/ActiveAnimations.h"
36 #include "core/animation/AnimationHelpers.h"
37 #include "core/animation/CompositorAnimations.h"
38 #include "core/animation/DocumentTimeline.h"
39 #include "core/animation/KeyframeEffectModel.h"
40 #include "core/animation/Player.h"
41 #include "core/css/parser/BisonCSSParser.h"
42 #include "core/css/resolver/StyleResolver.h"
43 #include "core/dom/Element.h"
44 #include "core/rendering/RenderLayer.h"
45 #include "wtf/text/StringBuilder.h"
49 PassRefPtr<Animation> Animation::create(PassRefPtr<Element> target, PassRefPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate)
51 return adoptRef(new Animation(target, effect, timing, priority, eventDelegate));
54 static bool checkDocumentAndRenderer(Element* element)
56 if (!element->inActiveDocument())
58 element->document().updateStyleIfNeeded();
59 if (!element->renderer())
64 PassRefPtr<Animation> Animation::create(Element* element, Vector<Dictionary> keyframeDictionaryVector, Dictionary timingInput)
66 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
68 // FIXME: This test will not be neccessary once resolution of keyframe values occurs at
69 // animation application time.
70 if (!checkDocumentAndRenderer(element))
73 return createUnsafe(element, keyframeDictionaryVector, timingInput);
76 PassRefPtr<Animation> Animation::create(Element* element, Vector<Dictionary> keyframeDictionaryVector, double timingInput)
78 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
80 // FIXME: This test will not be neccessary once resolution of keyframe values occurs at
81 // animation application time.
82 if (!checkDocumentAndRenderer(element))
85 return createUnsafe(element, keyframeDictionaryVector, timingInput);
88 PassRefPtr<Animation> Animation::create(Element* element, Vector<Dictionary> keyframeDictionaryVector)
90 ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
92 // FIXME: This test will not be neccessary once resolution of keyframe values occurs at
93 // animation application time.
94 if (!checkDocumentAndRenderer(element))
97 return createUnsafe(element, keyframeDictionaryVector);
100 void Animation::setStartDelay(Timing& timing, double startDelay)
102 if (std::isfinite(startDelay))
103 timing.startDelay = startDelay;
105 timing.startDelay = 0;
108 void Animation::setEndDelay(Timing& timing, double endDelay)
110 if (std::isfinite(endDelay))
111 timing.endDelay = endDelay;
116 void Animation::setFillMode(Timing& timing, String fillMode)
118 if (fillMode == "none") {
119 timing.fillMode = Timing::FillModeNone;
120 } else if (fillMode == "backwards") {
121 timing.fillMode = Timing::FillModeBackwards;
122 } else if (fillMode == "both") {
123 timing.fillMode = Timing::FillModeBoth;
124 } else if (fillMode == "forwards") {
125 timing.fillMode = Timing::FillModeForwards;
127 timing.fillMode = Timing::FillModeAuto;
131 void Animation::setIterationStart(Timing& timing, double iterationStart)
133 if (!std::isnan(iterationStart) && !std::isinf(iterationStart))
134 timing.iterationStart = std::max<double>(iterationStart, 0);
136 timing.iterationStart = 0;
139 void Animation::setIterationCount(Timing& timing, double iterationCount)
141 if (!std::isnan(iterationCount))
142 timing.iterationCount = std::max<double>(iterationCount, 0);
144 timing.iterationCount = 1;
147 void Animation::setIterationDuration(Timing& timing, double iterationDuration)
149 if (!std::isnan(iterationDuration) && iterationDuration >= 0)
150 timing.iterationDuration = iterationDuration;
152 timing.iterationDuration = std::numeric_limits<double>::quiet_NaN();
155 void Animation::setPlaybackRate(Timing& timing, double playbackRate)
157 if (!std::isnan(playbackRate) && !std::isinf(playbackRate))
158 timing.playbackRate = playbackRate;
160 timing.playbackRate = 1;
163 void Animation::setPlaybackDirection(Timing& timing, String direction)
165 if (direction == "reverse") {
166 timing.direction = Timing::PlaybackDirectionReverse;
167 } else if (direction == "alternate") {
168 timing.direction = Timing::PlaybackDirectionAlternate;
169 } else if (direction == "alternate-reverse") {
170 timing.direction = Timing::PlaybackDirectionAlternateReverse;
172 timing.direction = Timing::PlaybackDirectionNormal;
176 void Animation::setTimingFunction(Timing& timing, String timingFunctionString)
178 RefPtr<CSSValue> timingFunctionValue = BisonCSSParser::parseAnimationTimingFunctionValue(timingFunctionString);
179 if (timingFunctionValue) {
180 RefPtr<TimingFunction> timingFunction = CSSToStyleMap::animationTimingFunction(timingFunctionValue.get(), false);
181 if (timingFunction) {
182 timing.timingFunction = timingFunction;
186 timing.timingFunction = LinearTimingFunction::create();
189 void Animation::populateTiming(Timing& timing, Dictionary timingInputDictionary)
191 // FIXME: This method needs to be refactored to handle invalid
192 // null, NaN, Infinity values better.
193 // See: http://www.w3.org/TR/WebIDL/#es-double
194 double startDelay = 0;
195 timingInputDictionary.get("delay", startDelay);
196 setStartDelay(timing, startDelay);
199 timingInputDictionary.get("endDelay", endDelay);
200 setEndDelay(timing, endDelay);
203 timingInputDictionary.get("fill", fillMode);
204 setFillMode(timing, fillMode);
206 double iterationStart = 0;
207 timingInputDictionary.get("iterationStart", iterationStart);
208 setIterationStart(timing, iterationStart);
210 double iterationCount = 1;
211 timingInputDictionary.get("iterations", iterationCount);
212 setIterationCount(timing, iterationCount);
214 v8::Local<v8::Value> iterationDurationValue;
215 if (timingInputDictionary.get("duration", iterationDurationValue)) {
216 double iterationDuration = iterationDurationValue->NumberValue();
217 setIterationDuration(timing, iterationDuration);
220 double playbackRate = 1;
221 timingInputDictionary.get("playbackRate", playbackRate);
222 setPlaybackRate(timing, playbackRate);
225 timingInputDictionary.get("direction", direction);
226 setPlaybackDirection(timing, direction);
228 String timingFunctionString;
229 timingInputDictionary.get("easing", timingFunctionString);
230 setTimingFunction(timing, timingFunctionString);
232 timing.assertValid();
235 static PassRefPtr<KeyframeEffectModel> createKeyframeEffectModel(Element* element, Vector<Dictionary> keyframeDictionaryVector)
237 KeyframeEffectModel::KeyframeVector keyframes;
238 Vector<RefPtr<MutableStylePropertySet> > propertySetVector;
240 for (size_t i = 0; i < keyframeDictionaryVector.size(); ++i) {
241 RefPtr<MutableStylePropertySet> propertySet = MutableStylePropertySet::create();
242 propertySetVector.append(propertySet);
244 RefPtr<Keyframe> keyframe = Keyframe::create();
245 keyframes.append(keyframe);
248 if (keyframeDictionaryVector[i].get("offset", offset)) {
249 keyframe->setOffset(offset);
252 String compositeString;
253 keyframeDictionaryVector[i].get("composite", compositeString);
254 if (compositeString == "add")
255 keyframe->setComposite(AnimationEffect::CompositeAdd);
257 String timingFunctionString;
258 if (keyframeDictionaryVector[i].get("easing", timingFunctionString)) {
259 RefPtr<CSSValue> timingFunctionValue = BisonCSSParser::parseAnimationTimingFunctionValue(timingFunctionString);
260 if (timingFunctionValue) {
261 keyframe->setEasing(CSSToStyleMap::animationTimingFunction(timingFunctionValue.get(), false));
265 Vector<String> keyframeProperties;
266 keyframeDictionaryVector[i].getOwnPropertyNames(keyframeProperties);
268 for (size_t j = 0; j < keyframeProperties.size(); ++j) {
269 String property = keyframeProperties[j];
270 CSSPropertyID id = camelCaseCSSPropertyNameToID(property);
272 // FIXME: There is no way to store invalid properties or invalid values
273 // in a Keyframe object, so for now I just skip over them. Eventually we
274 // will need to support getFrames(), which should return exactly the
275 // keyframes that were input through the API. We will add a layer to wrap
276 // KeyframeEffectModel, store input keyframes and implement getFrames.
277 if (id == CSSPropertyInvalid || !CSSAnimations::isAnimatableProperty(id))
281 keyframeDictionaryVector[i].get(property, value);
282 propertySet->setProperty(id, value);
286 // FIXME: Replace this with code that just parses, when that code is available.
287 RefPtr<KeyframeEffectModel> effect = StyleResolver::createKeyframeEffectModel(*element, propertySetVector, keyframes);
291 PassRefPtr<Animation> Animation::createUnsafe(Element* element, Vector<Dictionary> keyframeDictionaryVector, Dictionary timingInput)
293 RefPtr<KeyframeEffectModel> effect = createKeyframeEffectModel(element, keyframeDictionaryVector);
296 populateTiming(timing, timingInput);
298 return create(element, effect, timing);
301 PassRefPtr<Animation> Animation::createUnsafe(Element* element, Vector<Dictionary> keyframeDictionaryVector, double timingInput)
303 RefPtr<KeyframeEffectModel> effect = createKeyframeEffectModel(element, keyframeDictionaryVector);
306 if (!std::isnan(timingInput))
307 timing.iterationDuration = std::max<double>(timingInput, 0);
309 return create(element, effect, timing);
312 PassRefPtr<Animation> Animation::createUnsafe(Element* element, Vector<Dictionary> keyframeDictionaryVector)
314 RefPtr<KeyframeEffectModel> effect = createKeyframeEffectModel(element, keyframeDictionaryVector);
317 return create(element, effect, timing);
320 Animation::Animation(PassRefPtr<Element> target, PassRefPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate)
321 : TimedItem(timing, eventDelegate)
324 , m_activeInAnimationStack(false)
325 , m_priority(priority)
329 void Animation::didAttach()
332 m_target->ensureActiveAnimations()->players().add(player());
335 void Animation::willDetach()
338 m_target->activeAnimations()->players().remove(player());
339 if (m_activeInAnimationStack)
343 static AnimationStack& ensureAnimationStack(Element* element)
345 return element->ensureActiveAnimations()->defaultStack();
348 bool Animation::applyEffects(bool previouslyInEffect)
350 ASSERT(isInEffect());
351 if (!m_target || !m_effect)
354 if (player() && !previouslyInEffect) {
355 ensureAnimationStack(m_target.get()).add(this);
356 m_activeInAnimationStack = true;
359 double iteration = currentIteration();
360 ASSERT(iteration >= 0);
361 // FIXME: Handle iteration values which overflow int.
362 m_compositableValues = m_effect->sample(static_cast<int>(iteration), timeFraction());
364 m_target->setNeedsAnimationStyleRecalc();
370 void Animation::clearEffects()
373 ASSERT(m_activeInAnimationStack);
374 ensureAnimationStack(m_target.get()).remove(this);
377 // FIXME: clearEffects is called from withins style recalc.
378 // This queries compositingState, which is not necessarily up to date.
379 // https://code.google.com/p/chromium/issues/detail?id=339847
380 DisableCompositingQueryAsserts disabler;
381 cancelAnimationOnCompositor();
384 m_activeInAnimationStack = false;
385 m_compositableValues.clear();
386 m_target->setNeedsAnimationStyleRecalc();
390 bool Animation::updateChildrenAndEffects() const
396 return const_cast<Animation*>(this)->applyEffects(m_activeInAnimationStack);
398 if (m_activeInAnimationStack) {
399 const_cast<Animation*>(this)->clearEffects();
405 double Animation::calculateTimeToEffectChange(bool forwards, double localTime, double timeToNextIteration) const
407 const double start = startTime() + specifiedTiming().startDelay;
408 const double end = start + activeDuration();
412 ASSERT(start >= localTime);
415 : std::numeric_limits<double>::infinity();
417 if (forwards && hasActiveAnimationsOnCompositor()) {
418 ASSERT(specifiedTiming().playbackRate == 1);
419 // Need service to apply fill / fire events.
420 return std::min(end - localTime, timeToNextIteration);
424 ASSERT(localTime >= end);
425 // If this Animation is still in effect then it will need to update
426 // when its parent goes out of effect. We have no way of knowing when
427 // that will be, however, so the parent will need to supply it.
429 ? std::numeric_limits<double>::infinity()
432 ASSERT(player() && player()->timeline() && !player()->timeline()->hasStarted());
433 return std::numeric_limits<double>::infinity();
435 ASSERT_NOT_REACHED();
436 return std::numeric_limits<double>::infinity();
440 bool Animation::isCandidateForAnimationOnCompositor() const
442 if (!effect() || !m_target)
444 return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(specifiedTiming(), *effect());
447 bool Animation::maybeStartAnimationOnCompositor()
449 ASSERT(!hasActiveAnimationsOnCompositor());
450 if (!isCandidateForAnimationOnCompositor())
452 if (!CompositorAnimations::instance()->canStartAnimationOnCompositor(*m_target.get()))
454 if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target.get(), specifiedTiming(), *effect(), m_compositorAnimationIds))
456 ASSERT(!m_compositorAnimationIds.isEmpty());
460 bool Animation::hasActiveAnimationsOnCompositor() const
462 return !m_compositorAnimationIds.isEmpty();
465 bool Animation::hasActiveAnimationsOnCompositor(CSSPropertyID property) const
467 return hasActiveAnimationsOnCompositor() && affects(property);
470 bool Animation::affects(CSSPropertyID property) const
472 return m_effect && m_effect->affects(property);
475 void Animation::cancelAnimationOnCompositor()
477 if (!hasActiveAnimationsOnCompositor())
479 if (!m_target || !m_target->renderer())
481 for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i)
482 CompositorAnimations::instance()->cancelAnimationOnCompositor(*m_target.get(), m_compositorAnimationIds[i]);
483 m_compositorAnimationIds.clear();
486 void Animation::pauseAnimationForTestingOnCompositor(double pauseTime)
488 ASSERT(hasActiveAnimationsOnCompositor());
489 if (!m_target || !m_target->renderer())
491 for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i)
492 CompositorAnimations::instance()->pauseAnimationForTestingOnCompositor(*m_target.get(), m_compositorAnimationIds[i], pauseTime);
495 } // namespace WebCore