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/css/CSSAnimations.h"
34 #include "StylePropertyShorthand.h"
35 #include "core/animation/ActiveAnimations.h"
36 #include "core/animation/CompositorAnimations.h"
37 #include "core/animation/DocumentTimeline.h"
38 #include "core/animation/KeyframeEffectModel.h"
39 #include "core/animation/css/CSSAnimatableValueFactory.h"
40 #include "core/animation/css/CSSAnimationDataList.h"
41 #include "core/animation/css/CSSPropertyEquality.h"
42 #include "core/css/CSSKeyframeRule.h"
43 #include "core/css/resolver/StyleResolver.h"
44 #include "core/dom/Element.h"
45 #include "core/dom/PseudoElement.h"
46 #include "core/events/TransitionEvent.h"
47 #include "core/events/WebKitAnimationEvent.h"
48 #include "core/frame/UseCounter.h"
49 #include "core/rendering/RenderLayer.h"
50 #include "core/rendering/RenderObject.h"
51 #include "core/rendering/style/KeyframeList.h"
52 #include "platform/animation/TimingFunction.h"
53 #include "public/platform/Platform.h"
54 #include "wtf/BitArray.h"
55 #include "wtf/HashSet.h"
61 CSSPropertyID propertyForAnimation(CSSPropertyID property)
64 case CSSPropertyWebkitPerspective:
65 return CSSPropertyPerspective;
66 case CSSPropertyWebkitTransform:
67 return CSSPropertyTransform;
68 case CSSPropertyWebkitPerspectiveOriginX:
69 case CSSPropertyWebkitPerspectiveOriginY:
70 if (RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled())
71 return CSSPropertyPerspectiveOrigin;
73 case CSSPropertyWebkitTransformOriginX:
74 case CSSPropertyWebkitTransformOriginY:
75 case CSSPropertyWebkitTransformOriginZ:
76 if (RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled())
77 return CSSPropertyTransformOrigin;
85 static void resolveKeyframes(StyleResolver* resolver, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, const AtomicString& name, TimingFunction* defaultTimingFunction,
86 AnimatableValueKeyframeVector& keyframes)
88 // When the element is null, use its parent for scoping purposes.
89 const Element* elementForScoping = element ? element : &parentElement;
90 const StyleRuleKeyframes* keyframesRule = CSSAnimations::matchScopedKeyframesRule(resolver, elementForScoping, name.impl());
94 const WillBeHeapVector<RefPtrWillBeMember<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
95 if (styleKeyframes.isEmpty())
98 // Construct and populate the style for each keyframe
99 PropertySet specifiedPropertiesForUseCounter;
100 for (size_t i = 0; i < styleKeyframes.size(); ++i) {
101 const StyleKeyframe* styleKeyframe = styleKeyframes[i].get();
102 // It's OK to pass a null element here.
103 RefPtr<RenderStyle> keyframeStyle = resolver->styleForKeyframe(element, style, parentStyle, styleKeyframe, name);
104 RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframe = AnimatableValueKeyframe::create();
105 const Vector<double>& offsets = styleKeyframe->keys();
106 ASSERT(!offsets.isEmpty());
107 keyframe->setOffset(offsets[0]);
108 keyframe->setEasing(defaultTimingFunction);
109 const StylePropertySet& properties = styleKeyframe->properties();
110 for (unsigned j = 0; j < properties.propertyCount(); j++) {
111 specifiedPropertiesForUseCounter.add(properties.propertyAt(j).id());
112 CSSPropertyID property = propertyForAnimation(properties.propertyAt(j).id());
113 if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) {
114 keyframe->setEasing(KeyframeValue::timingFunction(*keyframeStyle));
115 } else if (CSSAnimations::isAnimatableProperty(property)) {
116 keyframe->setPropertyValue(property, CSSAnimatableValueFactory::create(property, *keyframeStyle).get());
119 keyframes.append(keyframe);
120 // The last keyframe specified at a given offset is used.
121 for (size_t j = 1; j < offsets.size(); ++j) {
122 keyframes.append(toAnimatableValueKeyframe(keyframe->cloneWithOffset(offsets[j]).get()));
125 ASSERT(!keyframes.isEmpty());
127 for (PropertySet::const_iterator iter = specifiedPropertiesForUseCounter.begin(); iter != specifiedPropertiesForUseCounter.end(); ++iter) {
128 const CSSPropertyID property = *iter;
129 ASSERT(property != CSSPropertyInvalid);
130 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property));
133 // Remove duplicate keyframes. In CSS the last keyframe at a given offset takes priority.
134 std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets);
135 size_t targetIndex = 0;
136 for (size_t i = 1; i < keyframes.size(); i++) {
137 if (keyframes[i]->offset() != keyframes[targetIndex]->offset())
139 if (targetIndex != i)
140 keyframes[targetIndex] = keyframes[i];
142 keyframes.shrink(targetIndex + 1);
144 // Add 0% and 100% keyframes if absent.
145 RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = keyframes[0];
146 if (startKeyframe->offset()) {
147 startKeyframe = AnimatableValueKeyframe::create();
148 startKeyframe->setOffset(0);
149 startKeyframe->setEasing(defaultTimingFunction);
150 keyframes.prepend(startKeyframe);
152 RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = keyframes[keyframes.size() - 1];
153 if (endKeyframe->offset() != 1) {
154 endKeyframe = AnimatableValueKeyframe::create();
155 endKeyframe->setOffset(1);
156 endKeyframe->setEasing(defaultTimingFunction);
157 keyframes.append(endKeyframe);
159 ASSERT(keyframes.size() >= 2);
160 ASSERT(!keyframes.first()->offset());
161 ASSERT(keyframes.last()->offset() == 1);
163 // Snapshot current property values for 0% and 100% if missing.
164 PropertySet allProperties;
165 size_t numKeyframes = keyframes.size();
166 for (size_t i = 0; i < numKeyframes; i++) {
167 const PropertySet& keyframeProperties = keyframes[i]->properties();
168 for (PropertySet::const_iterator iter = keyframeProperties.begin(); iter != keyframeProperties.end(); ++iter)
169 allProperties.add(*iter);
171 const PropertySet& startKeyframeProperties = startKeyframe->properties();
172 const PropertySet& endKeyframeProperties = endKeyframe->properties();
173 bool missingStartValues = startKeyframeProperties.size() < allProperties.size();
174 bool missingEndValues = endKeyframeProperties.size() < allProperties.size();
175 if (missingStartValues || missingEndValues) {
176 for (PropertySet::const_iterator iter = allProperties.begin(); iter != allProperties.end(); ++iter) {
177 const CSSPropertyID property = *iter;
178 bool startNeedsValue = missingStartValues && !startKeyframeProperties.contains(property);
179 bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains(property);
180 if (!startNeedsValue && !endNeedsValue)
182 RefPtrWillBeRawPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style);
184 startKeyframe->setPropertyValue(property, snapshotValue.get());
186 endKeyframe->setPropertyValue(property, snapshotValue.get());
189 ASSERT(startKeyframe->properties().size() == allProperties.size());
190 ASSERT(endKeyframe->properties().size() == allProperties.size());
193 // Returns the default timing function.
194 const PassRefPtr<TimingFunction> timingFromAnimationData(const CSSAnimationData* animationData, Timing& timing, bool& isPaused)
196 if (animationData->isDelaySet())
197 timing.startDelay = animationData->delay();
198 if (animationData->isDurationSet())
199 timing.iterationDuration = animationData->duration();
200 if (animationData->isIterationCountSet()) {
201 if (animationData->iterationCount() == CSSAnimationData::IterationCountInfinite)
202 timing.iterationCount = std::numeric_limits<double>::infinity();
204 timing.iterationCount = animationData->iterationCount();
206 if (animationData->isFillModeSet()) {
207 switch (animationData->fillMode()) {
208 case AnimationFillModeForwards:
209 timing.fillMode = Timing::FillModeForwards;
211 case AnimationFillModeBackwards:
212 timing.fillMode = Timing::FillModeBackwards;
214 case AnimationFillModeBoth:
215 timing.fillMode = Timing::FillModeBoth;
217 case AnimationFillModeNone:
218 timing.fillMode = Timing::FillModeNone;
221 ASSERT_NOT_REACHED();
224 timing.fillMode = Timing::FillModeNone;
226 if (animationData->isDirectionSet()) {
227 switch (animationData->direction()) {
228 case CSSAnimationData::AnimationDirectionNormal:
229 timing.direction = Timing::PlaybackDirectionNormal;
231 case CSSAnimationData::AnimationDirectionAlternate:
232 timing.direction = Timing::PlaybackDirectionAlternate;
234 case CSSAnimationData::AnimationDirectionReverse:
235 timing.direction = Timing::PlaybackDirectionReverse;
237 case CSSAnimationData::AnimationDirectionAlternateReverse:
238 timing.direction = Timing::PlaybackDirectionAlternateReverse;
241 ASSERT_NOT_REACHED();
245 // For CSS, the constraints on the timing properties are tighter than in
246 // the general case of the Web Animations model.
247 timing.assertValid();
248 ASSERT(!timing.iterationStart);
249 ASSERT(timing.playbackRate == 1);
250 ASSERT(!std::isinf(timing.iterationDuration));
251 ASSERT(timing.timingFunction == LinearTimingFunction::shared());
253 isPaused = animationData->isPlayStateSet() && animationData->playState() == AnimPlayStatePaused;
254 return animationData->isTimingFunctionSet() ? animationData->timingFunction() : CSSAnimationData::initialAnimationTimingFunction();
259 const StyleRuleKeyframes* CSSAnimations::matchScopedKeyframesRule(StyleResolver* resolver, const Element* element, const StringImpl* animationName)
261 if (resolver->styleTreeHasOnlyScopedResolverForDocument())
262 return resolver->styleTreeScopedStyleResolverForDocument()->keyframeStylesForAnimation(animationName);
264 Vector<ScopedStyleResolver*, 8> stack;
265 resolver->styleTreeResolveScopedKeyframesRules(element, stack);
269 for (size_t i = 0; i < stack.size(); ++i) {
270 if (const StyleRuleKeyframes* keyframesRule = stack.at(i)->keyframeStylesForAnimation(animationName))
271 return keyframesRule;
276 CSSAnimations::CSSAnimations()
280 PassOwnPtrWillBeRawPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
282 OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = adoptPtrWillBeNoop(new CSSAnimationUpdate());
283 calculateAnimationUpdate(update.get(), element, parentElement, style, parentStyle, resolver);
284 calculateAnimationActiveInterpolations(update.get(), element, parentElement.document().timeline().currentTimeInternal());
285 calculateTransitionUpdate(update.get(), element, style);
286 calculateTransitionActiveInterpolations(update.get(), element, parentElement.document().transitionTimeline().currentTimeInternal());
287 return update->isEmpty() ? nullptr : update.release();
290 void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
292 const ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
295 // If we're in an animation style change, no animations can have started, been cancelled or changed play state.
296 // When ASSERT is enabled, we verify this optimization.
297 if (activeAnimations && activeAnimations->isAnimationStyleChange())
301 const CSSAnimationDataList* animationDataList = style.animations();
302 const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : 0;
304 HashSet<AtomicString> inactive;
306 for (AnimationMap::const_iterator iter = cssAnimations->m_animations.begin(); iter != cssAnimations->m_animations.end(); ++iter)
307 inactive.add(iter->key);
309 if (style.display() != NONE) {
310 for (size_t i = 0; animationDataList && i < animationDataList->size(); ++i) {
311 const CSSAnimationData* animationData = animationDataList->animation(i);
312 if (animationData->isNoneAnimation())
314 ASSERT(animationData->isValidAnimation());
315 AtomicString animationName(animationData->name());
317 // Keyframes and animation properties are snapshotted when the
318 // animation starts, so we don't need to track changes to these,
319 // with the exception of play-state.
321 AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName));
322 if (existing != cssAnimations->m_animations.end()) {
323 inactive.remove(animationName);
324 AnimationPlayer* player = existing->value.get();
325 if ((animationData->playState() == AnimPlayStatePaused) != player->paused()) {
326 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
327 update->toggleAnimationPaused(animationName);
335 RefPtr<TimingFunction> keyframeTimingFunction = timingFromAnimationData(animationData, timing, isPaused);
336 AnimatableValueKeyframeVector resolvedKeyframes;
337 resolveKeyframes(resolver, element, parentElement, style, parentStyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes);
338 if (!resolvedKeyframes.isEmpty()) {
339 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
340 update->startAnimation(animationName, InertAnimation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused));
345 ASSERT(inactive.isEmpty() || cssAnimations);
346 for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter) {
347 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
348 update->cancelAnimation(*iter, *cssAnimations->m_animations.get(*iter));
352 void CSSAnimations::maybeApplyPendingUpdate(Element* element)
354 if (!m_pendingUpdate) {
355 m_previousActiveInterpolationsForAnimations.clear();
359 OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
361 m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations());
363 // FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here
364 // since we call this from recalc style.
365 // https://code.google.com/p/chromium/issues/detail?id=339847
366 DisableCompositingQueryAsserts disabler;
368 for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationNames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) {
369 RefPtr<AnimationPlayer> player = m_animations.take(*iter);
371 player->update(TimingUpdateOnDemand);
374 for (Vector<AtomicString>::const_iterator iter = update->animationsWithPauseToggled().begin(); iter != update->animationsWithPauseToggled().end(); ++iter) {
375 AnimationPlayer* player = m_animations.get(*iter);
376 if (player->paused())
380 if (player->outdated())
381 player->update(TimingUpdateOnDemand);
384 for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
385 const InertAnimation* inertAnimation = iter->animation.get();
386 OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEventDelegate(element, iter->name));
387 RefPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release());
388 RefPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(animation.get());
389 element->document().compositorPendingAnimations().add(player.get());
390 if (inertAnimation->paused())
392 player->update(TimingUpdateOnDemand);
393 m_animations.set(iter->name, player.get());
396 // Transitions that are run on the compositor only update main-thread state
397 // lazily. However, we need the new state to know what the from state shoud
398 // be when transitions are retargeted. Instead of triggering complete style
399 // recalculation, we find these cases by searching for new transitions that
400 // have matching cancelled animation property IDs on the compositor.
401 HashMap<CSSPropertyID, std::pair<RefPtr<Animation>, double> > retargetedCompositorTransitions;
402 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
403 CSSPropertyID id = *iter;
404 ASSERT(m_transitions.contains(id));
406 RefPtr<AnimationPlayer> player = m_transitions.take(id).player;
407 Animation* animation = toAnimation(player->source());
408 if (animation->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end())
409 retargetedCompositorTransitions.add(id, std::pair<RefPtr<Animation>, double>(animation, player->startTimeInternal()));
411 player->update(TimingUpdateOnDemand);
414 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter) {
415 const CSSAnimationUpdate::NewTransition& newTransition = iter->value;
417 RunningTransition runningTransition;
418 runningTransition.from = newTransition.from;
419 runningTransition.to = newTransition.to;
421 CSSPropertyID id = newTransition.id;
422 InertAnimation* inertAnimation = newTransition.animation.get();
423 OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionEventDelegate(element, newTransition.eventId));
425 RefPtrWillBeRawPtr<AnimationEffect> effect = inertAnimation->effect();
427 if (retargetedCompositorTransitions.contains(id)) {
428 const std::pair<RefPtr<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id);
429 RefPtr<Animation> oldAnimation = oldTransition.first;
430 double oldStartTime = oldTransition.second;
431 double inheritedTime = isNull(oldStartTime) ? 0 : element->document().transitionTimeline().currentTimeInternal() - oldStartTime;
433 AnimatableValueKeyframeEffectModel* oldEffect = toAnimatableValueKeyframeEffectModel(inertAnimation->effect());
434 const KeyframeVector& frames = oldEffect->getFrames();
436 AnimatableValueKeyframeVector newFrames;
437 newFrames.append(toAnimatableValueKeyframe(frames[0]->clone().get()));
438 newFrames.append(toAnimatableValueKeyframe(frames[1]->clone().get()));
440 newFrames[0]->clearPropertyValue(id);
441 RefPtr<InertAnimation> inertAnimationForSampling = InertAnimation::create(oldAnimation->effect(), oldAnimation->specifiedTiming(), false);
442 OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > sample = inertAnimationForSampling->sample(inheritedTime);
443 ASSERT(sample->size() == 1);
444 newFrames[0]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue());
446 effect = AnimatableValueKeyframeEffectModel::create(newFrames);
449 RefPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release());
450 RefPtr<AnimationPlayer> player = element->document().transitionTimeline().createAnimationPlayer(transition.get());
451 element->document().compositorPendingAnimations().add(player.get());
452 player->update(TimingUpdateOnDemand);
453 runningTransition.player = player;
454 m_transitions.set(id, runningTransition);
455 ASSERT(id != CSSPropertyInvalid);
456 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id));
460 void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, const CSSAnimationData* anim, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* activeTransitions, CSSAnimationUpdate* update, const Element* element)
462 RefPtrWillBeRawPtr<AnimatableValue> to = nullptr;
463 if (activeTransitions) {
464 TransitionMap::const_iterator activeTransitionIter = activeTransitions->find(id);
465 if (activeTransitionIter != activeTransitions->end()) {
466 to = CSSAnimatableValueFactory::create(id, style);
467 const AnimatableValue* activeTo = activeTransitionIter->value.to;
468 if (to->equals(activeTo))
470 update->cancelTransition(id);
471 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
475 if (anim->duration() + anim->delay() <= 0)
478 if (CSSPropertyEquality::propertiesEqual(id, oldStyle, style))
481 to = CSSAnimatableValueFactory::create(id, style);
483 RefPtrWillBeRawPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
484 // If we have multiple transitions on the same property, we will use the
485 // last one since we iterate over them in order.
486 if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get()))
491 RefPtr<TimingFunction> timingFunction = timingFromAnimationData(anim, timing, isPaused);
493 // Note that the backwards part is required for delay to work.
494 timing.fillMode = Timing::FillModeBoth;
496 AnimatableValueKeyframeVector keyframes;
498 RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = AnimatableValueKeyframe::create();
499 startKeyframe->setPropertyValue(id, from.get());
500 startKeyframe->setOffset(0);
501 startKeyframe->setEasing(timingFunction);
502 keyframes.append(startKeyframe);
504 RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = AnimatableValueKeyframe::create();
505 endKeyframe->setPropertyValue(id, to.get());
506 endKeyframe->setOffset(1);
507 keyframes.append(endKeyframe);
509 RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes);
511 CSSPropertyID eventId = anim->animationMode() == CSSAnimationData::AnimateAll ? id : anim->property();
512 update->startTransition(id, eventId, from.get(), to.get(), InertAnimation::create(effect, timing, isPaused));
513 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
516 void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const Element* element, const RenderStyle& style)
521 ActiveAnimations* activeAnimations = element->activeAnimations();
522 const TransitionMap* activeTransitions = activeAnimations ? &activeAnimations->cssAnimations().m_transitions : 0;
525 // In release builds we avoid the cost of checking for new and interrupted transitions if the style recalc is due to animation.
526 const bool animationStyleRecalc = activeAnimations && activeAnimations->isAnimationStyleChange();
528 // 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.
529 const bool animationStyleRecalc = false;
532 BitArray<numCSSProperties> listedProperties;
533 bool anyTransitionHadAnimateAll = false;
534 const RenderObject* renderer = element->renderer();
535 if (!animationStyleRecalc && style.display() != NONE && renderer && renderer->style() && style.transitions()) {
536 const RenderStyle& oldStyle = *renderer->style();
538 for (size_t i = 0; i < style.transitions()->size(); ++i) {
539 const CSSAnimationData* anim = style.transitions()->animation(i);
540 CSSAnimationData::AnimationMode mode = anim->animationMode();
541 if (mode == CSSAnimationData::AnimateNone)
544 bool animateAll = mode == CSSAnimationData::AnimateAll;
545 ASSERT(animateAll || mode == CSSAnimationData::AnimateSingleProperty);
547 anyTransitionHadAnimateAll = true;
548 const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(anim->property());
549 // If not a shorthand we only execute one iteration of this loop, and refer to the property directly.
550 for (unsigned j = 0; !j || j < propertyList.length(); ++j) {
551 CSSPropertyID id = propertyList.length() ? propertyList.properties()[j] : anim->property();
554 id = propertyForAnimation(id);
555 if (CSSAnimations::isAnimatableProperty(id))
556 listedProperties.set(id);
561 // FIXME: We should transition if an !important property changes even when an animation is running,
562 // but this is a bit hard to do with the current applyMatchedProperties system.
563 if (!update->activeInterpolationsForAnimations().contains(id)
564 && (!activeAnimations || !activeAnimations->cssAnimations().m_previousActiveInterpolationsForAnimations.contains(id))) {
565 calculateTransitionUpdateForProperty(id, anim, oldStyle, style, activeTransitions, update, element);
571 if (activeTransitions) {
572 for (TransitionMap::const_iterator iter = activeTransitions->begin(); iter != activeTransitions->end(); ++iter) {
573 const AnimationPlayer& player = *iter->value.player;
574 CSSPropertyID id = iter->key;
575 if (player.finishedInternal() || (!anyTransitionHadAnimateAll && !animationStyleRecalc && !listedProperties.get(id))) {
576 // TODO: Figure out why this fails on Chrome OS login page. crbug.com/365507
577 // ASSERT(player.finishedInternal() || !(activeAnimations && activeAnimations->isAnimationStyleChange()));
578 update->cancelTransition(id);
584 void CSSAnimations::cancel()
586 for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animations.end(); ++iter) {
587 iter->value->cancel();
588 iter->value->update(TimingUpdateOnDemand);
591 for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transitions.end(); ++iter) {
592 iter->value.player->cancel();
593 iter->value.player->update(TimingUpdateOnDemand);
596 m_animations.clear();
597 m_transitions.clear();
598 m_pendingUpdate = nullptr;
601 void CSSAnimations::calculateAnimationActiveInterpolations(CSSAnimationUpdate* update, const Element* element, double timelineCurrentTime)
603 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
604 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
606 if (update->newAnimations().isEmpty() && update->cancelledAnimationAnimationPlayers().isEmpty()) {
607 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::DefaultPriority, timelineCurrentTime));
608 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations);
612 Vector<InertAnimation*> newAnimations;
613 for (size_t i = 0; i < update->newAnimations().size(); ++i) {
614 newAnimations.append(update->newAnimations()[i].animation.get());
616 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, &newAnimations, &update->cancelledAnimationAnimationPlayers(), Animation::DefaultPriority, timelineCurrentTime));
617 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations);
620 void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate* update, const Element* element, double timelineCurrentTime)
622 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
623 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
625 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForTransitions;
626 if (update->newTransitions().isEmpty() && update->cancelledTransitions().isEmpty()) {
627 activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::TransitionPriority, timelineCurrentTime);
629 Vector<InertAnimation*> newTransitions;
630 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter)
631 newTransitions.append(iter->value.animation.get());
633 HashSet<const AnimationPlayer*> cancelledAnimationPlayers;
634 if (!update->cancelledTransitions().isEmpty()) {
635 ASSERT(activeAnimations);
636 const TransitionMap& transitionMap = activeAnimations->cssAnimations().m_transitions;
637 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
638 ASSERT(transitionMap.contains(*iter));
639 cancelledAnimationPlayers.add(transitionMap.get(*iter).player.get());
643 activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, &newTransitions, &cancelledAnimationPlayers, Animation::TransitionPriority, timelineCurrentTime);
646 // Properties being animated by animations don't get values from transitions applied.
647 if (!update->activeInterpolationsForAnimations().isEmpty() && !activeInterpolationsForTransitions.isEmpty()) {
648 for (WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >::const_iterator iter = update->activeInterpolationsForAnimations().begin(); iter != update->activeInterpolationsForAnimations().end(); ++iter)
649 activeInterpolationsForTransitions.remove(iter->key);
651 update->adoptActiveInterpolationsForTransitions(activeInterpolationsForTransitions);
654 void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType listenerType, const AtomicString& eventName, double elapsedTime)
656 if (m_target->document().hasListenerType(listenerType)) {
657 RefPtrWillBeRawPtr<WebKitAnimationEvent> event = WebKitAnimationEvent::create(eventName, m_name, elapsedTime);
658 event->setTarget(m_target);
659 m_target->document().enqueueAnimationFrameEvent(event);
663 void CSSAnimations::AnimationEventDelegate::onEventCondition(const TimedItem* timedItem)
665 const TimedItem::Phase currentPhase = timedItem->phase();
666 const double currentIteration = timedItem->currentIteration();
668 if (m_previousPhase != currentPhase
669 && (currentPhase == TimedItem::PhaseActive || currentPhase == TimedItem::PhaseAfter)
670 && (m_previousPhase == TimedItem::PhaseNone || m_previousPhase == TimedItem::PhaseBefore)) {
671 // The spec states that the elapsed time should be
672 // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing
673 // implementation. See crbug.com/279611
674 maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animationstart, 0);
677 if (currentPhase == TimedItem::PhaseActive && m_previousPhase == currentPhase && m_previousIteration != currentIteration) {
678 // We fire only a single event for all iterations thast terminate
679 // between a single pair of samples. See http://crbug.com/275263. For
680 // compatibility with the existing implementation, this event uses
681 // the elapsedTime for the first iteration in question.
682 ASSERT(!std::isnan(timedItem->specifiedTiming().iterationDuration));
683 const double elapsedTime = timedItem->specifiedTiming().iterationDuration * (m_previousIteration + 1);
684 maybeDispatch(Document::ANIMATIONITERATION_LISTENER, EventTypeNames::animationiteration, elapsedTime);
687 if (currentPhase == TimedItem::PhaseAfter && m_previousPhase != TimedItem::PhaseAfter)
688 maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animationend, timedItem->activeDurationInternal());
690 m_previousPhase = currentPhase;
691 m_previousIteration = currentIteration;
694 void CSSAnimations::TransitionEventDelegate::onEventCondition(const TimedItem* timedItem)
696 const TimedItem::Phase currentPhase = timedItem->phase();
697 if (currentPhase == TimedItem::PhaseAfter && currentPhase != m_previousPhase && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
698 String propertyName = getPropertyNameString(m_property);
699 const Timing& timing = timedItem->specifiedTiming();
700 double elapsedTime = timing.iterationDuration;
701 const AtomicString& eventType = EventTypeNames::transitionend;
702 String pseudoElement = PseudoElement::pseudoElementNameForEvents(m_target->pseudoId());
703 RefPtrWillBeRawPtr<TransitionEvent> event = TransitionEvent::create(eventType, propertyName, elapsedTime, pseudoElement);
704 event->setTarget(m_target);
705 m_target->document().enqueueAnimationFrameEvent(event);
708 m_previousPhase = currentPhase;
711 bool CSSAnimations::isAnimatableProperty(CSSPropertyID property)
714 case CSSPropertyBackgroundColor:
715 case CSSPropertyBackgroundImage:
716 case CSSPropertyBackgroundPositionX:
717 case CSSPropertyBackgroundPositionY:
718 case CSSPropertyBackgroundSize:
719 case CSSPropertyBaselineShift:
720 case CSSPropertyBorderBottomColor:
721 case CSSPropertyBorderBottomLeftRadius:
722 case CSSPropertyBorderBottomRightRadius:
723 case CSSPropertyBorderBottomWidth:
724 case CSSPropertyBorderImageOutset:
725 case CSSPropertyBorderImageSlice:
726 case CSSPropertyBorderImageSource:
727 case CSSPropertyBorderImageWidth:
728 case CSSPropertyBorderLeftColor:
729 case CSSPropertyBorderLeftWidth:
730 case CSSPropertyBorderRightColor:
731 case CSSPropertyBorderRightWidth:
732 case CSSPropertyBorderTopColor:
733 case CSSPropertyBorderTopLeftRadius:
734 case CSSPropertyBorderTopRightRadius:
735 case CSSPropertyBorderTopWidth:
736 case CSSPropertyBottom:
737 case CSSPropertyBoxShadow:
738 case CSSPropertyClip:
739 case CSSPropertyColor:
740 case CSSPropertyFill:
741 case CSSPropertyFillOpacity:
742 case CSSPropertyFlexBasis:
743 case CSSPropertyFlexGrow:
744 case CSSPropertyFlexShrink:
745 case CSSPropertyFloodColor:
746 case CSSPropertyFloodOpacity:
747 case CSSPropertyFontSize:
748 case CSSPropertyFontWeight:
749 case CSSPropertyHeight:
750 case CSSPropertyLeft:
751 case CSSPropertyLetterSpacing:
752 case CSSPropertyLightingColor:
753 case CSSPropertyLineHeight:
754 case CSSPropertyListStyleImage:
755 case CSSPropertyMarginBottom:
756 case CSSPropertyMarginLeft:
757 case CSSPropertyMarginRight:
758 case CSSPropertyMarginTop:
759 case CSSPropertyMaxHeight:
760 case CSSPropertyMaxWidth:
761 case CSSPropertyMinHeight:
762 case CSSPropertyMinWidth:
763 case CSSPropertyObjectPosition:
764 case CSSPropertyOpacity:
765 case CSSPropertyOrphans:
766 case CSSPropertyOutlineColor:
767 case CSSPropertyOutlineOffset:
768 case CSSPropertyOutlineWidth:
769 case CSSPropertyPaddingBottom:
770 case CSSPropertyPaddingLeft:
771 case CSSPropertyPaddingRight:
772 case CSSPropertyPaddingTop:
773 case CSSPropertyRight:
774 case CSSPropertyStopColor:
775 case CSSPropertyStopOpacity:
776 case CSSPropertyStroke:
777 case CSSPropertyStrokeDasharray:
778 case CSSPropertyStrokeDashoffset:
779 case CSSPropertyStrokeMiterlimit:
780 case CSSPropertyStrokeOpacity:
781 case CSSPropertyStrokeWidth:
782 case CSSPropertyTextDecorationColor:
783 case CSSPropertyTextIndent:
784 case CSSPropertyTextShadow:
786 case CSSPropertyVisibility:
787 case CSSPropertyWebkitBackgroundSize:
788 case CSSPropertyWebkitBorderHorizontalSpacing:
789 case CSSPropertyWebkitBorderVerticalSpacing:
790 case CSSPropertyWebkitBoxShadow:
791 case CSSPropertyWebkitClipPath:
792 case CSSPropertyWebkitColumnCount:
793 case CSSPropertyWebkitColumnGap:
794 case CSSPropertyWebkitColumnRuleColor:
795 case CSSPropertyWebkitColumnRuleWidth:
796 case CSSPropertyWebkitColumnWidth:
797 case CSSPropertyWebkitFilter:
798 case CSSPropertyWebkitMaskBoxImageOutset:
799 case CSSPropertyWebkitMaskBoxImageSlice:
800 case CSSPropertyWebkitMaskBoxImageSource:
801 case CSSPropertyWebkitMaskBoxImageWidth:
802 case CSSPropertyWebkitMaskImage:
803 case CSSPropertyWebkitMaskPositionX:
804 case CSSPropertyWebkitMaskPositionY:
805 case CSSPropertyWebkitMaskSize:
806 case CSSPropertyPerspective:
807 case CSSPropertyShapeOutside:
808 case CSSPropertyShapeMargin:
809 case CSSPropertyShapeImageThreshold:
810 case CSSPropertyWebkitTextStrokeColor:
811 case CSSPropertyTransform:
812 case CSSPropertyWidows:
813 case CSSPropertyWidth:
814 case CSSPropertyWordSpacing:
815 case CSSPropertyZIndex:
816 case CSSPropertyZoom:
818 case CSSPropertyPerspectiveOrigin:
819 case CSSPropertyTransformOrigin:
820 return RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled();
821 case CSSPropertyWebkitPerspectiveOriginX:
822 case CSSPropertyWebkitPerspectiveOriginY:
823 case CSSPropertyWebkitTransformOriginX:
824 case CSSPropertyWebkitTransformOriginY:
825 case CSSPropertyWebkitTransformOriginZ:
826 return !RuntimeEnabledFeatures::cssTransformsUnprefixedEnabled();
832 const StylePropertyShorthand& CSSAnimations::animatableProperties()
834 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
835 DEFINE_STATIC_LOCAL(StylePropertyShorthand, propertyShorthand, ());
836 if (properties.isEmpty()) {
837 for (int i = firstCSSProperty; i < lastCSSProperty; ++i) {
838 CSSPropertyID id = convertToCSSPropertyID(i);
839 if (isAnimatableProperty(id))
840 properties.append(id);
842 propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, properties.begin(), properties.size());
844 return propertyShorthand;
847 // Animation properties are not allowed to be affected by Web Animations.
848 // http://dev.w3.org/fxtf/web-animations/#not-animatable
849 bool CSSAnimations::isAllowedAnimation(CSSPropertyID property)
852 case CSSPropertyAnimation:
853 case CSSPropertyAnimationDelay:
854 case CSSPropertyAnimationDirection:
855 case CSSPropertyAnimationDuration:
856 case CSSPropertyAnimationFillMode:
857 case CSSPropertyAnimationIterationCount:
858 case CSSPropertyAnimationName:
859 case CSSPropertyAnimationPlayState:
860 case CSSPropertyAnimationTimingFunction:
861 case CSSPropertyDisplay:
862 case CSSPropertyTransition:
863 case CSSPropertyTransitionDelay:
864 case CSSPropertyTransitionDuration:
865 case CSSPropertyTransitionProperty:
866 case CSSPropertyTransitionTimingFunction:
867 case CSSPropertyWebkitAnimation:
868 case CSSPropertyWebkitAnimationDelay:
869 case CSSPropertyWebkitAnimationDirection:
870 case CSSPropertyWebkitAnimationDuration:
871 case CSSPropertyWebkitAnimationFillMode:
872 case CSSPropertyWebkitAnimationIterationCount:
873 case CSSPropertyWebkitAnimationName:
874 case CSSPropertyWebkitAnimationPlayState:
875 case CSSPropertyWebkitAnimationTimingFunction:
876 case CSSPropertyWebkitTransition:
877 case CSSPropertyWebkitTransitionDelay:
878 case CSSPropertyWebkitTransitionDuration:
879 case CSSPropertyWebkitTransitionProperty:
880 case CSSPropertyWebkitTransitionTimingFunction:
887 void CSSAnimations::trace(Visitor* visitor)
889 visitor->trace(m_transitions);
890 visitor->trace(m_pendingUpdate);
891 visitor->trace(m_previousActiveInterpolationsForAnimations);
894 void CSSAnimationUpdate::trace(Visitor* visitor)
896 visitor->trace(m_newTransitions);
897 visitor->trace(m_activeInterpolationsForAnimations);
898 visitor->trace(m_activeInterpolationsForTransitions);
901 } // namespace WebCore