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/css/CSSKeyframeRule.h"
42 #include "core/css/resolver/StyleResolver.h"
43 #include "core/dom/Element.h"
44 #include "core/dom/PseudoElement.h"
45 #include "core/events/ThreadLocalEventNames.h"
46 #include "core/events/TransitionEvent.h"
47 #include "core/events/WebKitAnimationEvent.h"
48 #include "core/frame/UseCounter.h"
49 #include "core/rendering/RenderObject.h"
50 #include "core/rendering/style/KeyframeList.h"
51 #include "platform/animation/TimingFunction.h"
52 #include "public/platform/Platform.h"
53 #include "wtf/BitArray.h"
54 #include "wtf/HashSet.h"
60 bool isEarlierPhase(TimedItem::Phase target, TimedItem::Phase reference)
62 ASSERT(target != TimedItem::PhaseNone);
63 ASSERT(reference != TimedItem::PhaseNone);
64 return target < reference;
67 bool isLaterPhase(TimedItem::Phase target, TimedItem::Phase reference)
69 ASSERT(target != TimedItem::PhaseNone);
70 ASSERT(reference != TimedItem::PhaseNone);
71 return target > reference;
74 static PassRefPtr<TimingFunction> generateTimingFunction(const KeyframeEffectModel::KeyframeVector keyframes, const HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions)
76 // Generate the chained timing function. Note that timing functions apply
77 // from the keyframe in which they're specified to the next keyframe.
78 bool isTimingFunctionLinearThroughout = true;
79 RefPtr<ChainedTimingFunction> chainedTimingFunction = ChainedTimingFunction::create();
80 for (size_t i = 0; i < keyframes.size() - 1; ++i) {
81 double lowerBound = keyframes[i]->offset();
82 ASSERT(lowerBound >=0 && lowerBound < 1);
83 double upperBound = keyframes[i + 1]->offset();
84 ASSERT(upperBound > 0 && upperBound <= 1);
85 TimingFunction* timingFunction = perKeyframeTimingFunctions.get(lowerBound);
86 isTimingFunctionLinearThroughout &= timingFunction->type() == TimingFunction::LinearFunction;
87 chainedTimingFunction->appendSegment(upperBound, timingFunction);
89 if (isTimingFunctionLinearThroughout)
90 return LinearTimingFunction::create();
91 return chainedTimingFunction;
94 static void resolveKeyframes(StyleResolver* resolver, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, const AtomicString& name, TimingFunction* defaultTimingFunction,
95 Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > >& keyframesAndTimingFunctions)
97 ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled());
98 // When the element is null, use its parent for scoping purposes.
99 const Element* elementForScoping = element ? element : &parentElement;
100 const StyleRuleKeyframes* keyframesRule = CSSAnimations::matchScopedKeyframesRule(resolver, elementForScoping, name.impl());
104 const Vector<RefPtr<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
105 if (styleKeyframes.isEmpty())
108 // Construct and populate the style for each keyframe
109 PropertySet specifiedProperties;
110 KeyframeEffectModel::KeyframeVector keyframes;
111 HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions;
112 for (size_t i = 0; i < styleKeyframes.size(); ++i) {
113 const StyleKeyframe* styleKeyframe = styleKeyframes[i].get();
114 // It's OK to pass a null element here.
115 RefPtr<RenderStyle> keyframeStyle = resolver->styleForKeyframe(element, style, parentStyle, styleKeyframe, name);
116 RefPtr<Keyframe> keyframe = Keyframe::create();
117 const Vector<double>& offsets = styleKeyframe->keys();
118 ASSERT(!offsets.isEmpty());
119 keyframe->setOffset(offsets[0]);
120 TimingFunction* timingFunction = defaultTimingFunction;
121 const StylePropertySet* properties = styleKeyframe->properties();
122 for (unsigned j = 0; j < properties->propertyCount(); j++) {
123 CSSPropertyID property = properties->propertyAt(j).id();
124 specifiedProperties.add(property);
125 if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction)
126 timingFunction = KeyframeValue::timingFunction(*keyframeStyle);
127 else if (CSSAnimations::isAnimatableProperty(property))
128 keyframe->setPropertyValue(property, CSSAnimatableValueFactory::create(property, *keyframeStyle).get());
130 keyframes.append(keyframe);
131 // The last keyframe specified at a given offset is used.
132 perKeyframeTimingFunctions.set(offsets[0], timingFunction);
133 for (size_t j = 1; j < offsets.size(); ++j) {
134 keyframes.append(keyframe->cloneWithOffset(offsets[j]));
135 perKeyframeTimingFunctions.set(offsets[j], timingFunction);
138 ASSERT(!keyframes.isEmpty());
140 if (!perKeyframeTimingFunctions.contains(0))
141 perKeyframeTimingFunctions.set(0, defaultTimingFunction);
143 for (PropertySet::const_iterator iter = specifiedProperties.begin(); iter != specifiedProperties.end(); ++iter) {
144 const CSSPropertyID property = *iter;
145 ASSERT(property != CSSPropertyInvalid);
146 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property));
149 // Remove duplicate keyframes. In CSS the last keyframe at a given offset takes priority.
150 std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets);
151 size_t targetIndex = 0;
152 for (size_t i = 1; i < keyframes.size(); i++) {
153 if (keyframes[i]->offset() != keyframes[targetIndex]->offset())
155 if (targetIndex != i)
156 keyframes[targetIndex] = keyframes[i];
158 keyframes.shrink(targetIndex + 1);
160 // Add 0% and 100% keyframes if absent.
161 RefPtr<Keyframe> startKeyframe = keyframes[0];
162 if (startKeyframe->offset()) {
163 startKeyframe = Keyframe::create();
164 startKeyframe->setOffset(0);
165 keyframes.prepend(startKeyframe);
167 RefPtr<Keyframe> endKeyframe = keyframes[keyframes.size() - 1];
168 if (endKeyframe->offset() != 1) {
169 endKeyframe = Keyframe::create();
170 endKeyframe->setOffset(1);
171 keyframes.append(endKeyframe);
173 ASSERT(keyframes.size() >= 2);
174 ASSERT(!keyframes.first()->offset());
175 ASSERT(keyframes.last()->offset() == 1);
177 // Snapshot current property values for 0% and 100% if missing.
178 PropertySet allProperties;
179 size_t numKeyframes = keyframes.size();
180 for (size_t i = 0; i < numKeyframes; i++) {
181 const PropertySet& keyframeProperties = keyframes[i]->properties();
182 for (PropertySet::const_iterator iter = keyframeProperties.begin(); iter != keyframeProperties.end(); ++iter)
183 allProperties.add(*iter);
185 const PropertySet& startKeyframeProperties = startKeyframe->properties();
186 const PropertySet& endKeyframeProperties = endKeyframe->properties();
187 bool missingStartValues = startKeyframeProperties.size() < allProperties.size();
188 bool missingEndValues = endKeyframeProperties.size() < allProperties.size();
189 if (missingStartValues || missingEndValues) {
190 for (PropertySet::const_iterator iter = allProperties.begin(); iter != allProperties.end(); ++iter) {
191 const CSSPropertyID property = *iter;
192 bool startNeedsValue = missingStartValues && !startKeyframeProperties.contains(property);
193 bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains(property);
194 if (!startNeedsValue && !endNeedsValue)
196 RefPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style);
198 startKeyframe->setPropertyValue(property, snapshotValue.get());
200 endKeyframe->setPropertyValue(property, snapshotValue.get());
203 ASSERT(startKeyframe->properties().size() == allProperties.size());
204 ASSERT(endKeyframe->properties().size() == allProperties.size());
206 // Determine how many keyframes specify each property. Note that this must
207 // be done after we've filled in end keyframes.
208 typedef HashCountedSet<CSSPropertyID> PropertyCountedSet;
209 PropertyCountedSet propertyCounts;
210 for (size_t i = 0; i < numKeyframes; ++i) {
211 const PropertySet& properties = keyframes[i]->properties();
212 for (PropertySet::const_iterator iter = properties.begin(); iter != properties.end(); ++iter)
213 propertyCounts.add(*iter);
216 // Split keyframes into groups, where each group contains only keyframes
217 // which specify all properties used in that group. Each group is animated
218 // in a separate animation, to allow per-keyframe timing functions to be
219 // applied correctly.
220 for (PropertyCountedSet::const_iterator iter = propertyCounts.begin(); iter != propertyCounts.end(); ++iter) {
221 const CSSPropertyID property = iter->key;
222 const size_t count = iter->value;
223 ASSERT(count <= numKeyframes);
224 if (count == numKeyframes)
226 KeyframeEffectModel::KeyframeVector splitOutKeyframes;
227 for (size_t i = 0; i < numKeyframes; i++) {
228 Keyframe* keyframe = keyframes[i].get();
229 if (!keyframe->properties().contains(property)) {
230 ASSERT(i && i != numKeyframes - 1);
233 RefPtr<Keyframe> clonedKeyframe = Keyframe::create();
234 clonedKeyframe->setOffset(keyframe->offset());
235 clonedKeyframe->setComposite(keyframe->composite());
236 clonedKeyframe->setPropertyValue(property, keyframe->propertyValue(property));
237 splitOutKeyframes.append(clonedKeyframe);
238 // Note that it's OK if this keyframe ends up having no
239 // properties. This can only happen when none of the properties
240 // are specified in all keyframes, in which case we won't animate
241 // anything with these keyframes.
242 keyframe->clearPropertyValue(property);
244 ASSERT(!splitOutKeyframes.first()->offset());
245 ASSERT(splitOutKeyframes.last()->offset() == 1);
247 for (size_t j = 0; j < splitOutKeyframes.size(); ++j)
248 ASSERT(splitOutKeyframes[j]->properties().size() == 1);
250 keyframesAndTimingFunctions.append(std::make_pair(splitOutKeyframes, generateTimingFunction(splitOutKeyframes, perKeyframeTimingFunctions)));
253 unsigned numPropertiesSpecifiedInAllKeyframes = keyframes.first()->properties().size();
255 for (size_t i = 1; i < numKeyframes; ++i)
256 ASSERT(keyframes[i]->properties().size() == numPropertiesSpecifiedInAllKeyframes);
259 // If the animation specifies any keyframes, we always provide at least one
260 // vector of resolved keyframes, even if no properties are animated.
261 if (numPropertiesSpecifiedInAllKeyframes || keyframesAndTimingFunctions.isEmpty())
262 keyframesAndTimingFunctions.append(std::make_pair(keyframes, generateTimingFunction(keyframes, perKeyframeTimingFunctions)));
265 // Returns the default timing function.
266 const PassRefPtr<TimingFunction> timingFromAnimationData(const CSSAnimationData* animationData, Timing& timing, bool& isPaused)
268 if (animationData->isDelaySet())
269 timing.startDelay = animationData->delay();
270 if (animationData->isDurationSet()) {
271 timing.iterationDuration = animationData->duration();
272 timing.hasIterationDuration = true;
274 if (animationData->isIterationCountSet()) {
275 if (animationData->iterationCount() == CSSAnimationData::IterationCountInfinite)
276 timing.iterationCount = std::numeric_limits<double>::infinity();
278 timing.iterationCount = animationData->iterationCount();
280 if (animationData->isFillModeSet()) {
281 switch (animationData->fillMode()) {
282 case AnimationFillModeForwards:
283 timing.fillMode = Timing::FillModeForwards;
285 case AnimationFillModeBackwards:
286 timing.fillMode = Timing::FillModeBackwards;
288 case AnimationFillModeBoth:
289 timing.fillMode = Timing::FillModeBoth;
291 case AnimationFillModeNone:
292 timing.fillMode = Timing::FillModeNone;
295 ASSERT_NOT_REACHED();
298 timing.fillMode = Timing::FillModeNone;
300 if (animationData->isDirectionSet()) {
301 switch (animationData->direction()) {
302 case CSSAnimationData::AnimationDirectionNormal:
303 timing.direction = Timing::PlaybackDirectionNormal;
305 case CSSAnimationData::AnimationDirectionAlternate:
306 timing.direction = Timing::PlaybackDirectionAlternate;
308 case CSSAnimationData::AnimationDirectionReverse:
309 timing.direction = Timing::PlaybackDirectionReverse;
311 case CSSAnimationData::AnimationDirectionAlternateReverse:
312 timing.direction = Timing::PlaybackDirectionAlternateReverse;
315 ASSERT_NOT_REACHED();
319 // For CSS, the constraints on the timing properties are tighter than in
320 // the general case of the Web Animations model.
321 timing.assertValid();
322 ASSERT(!timing.iterationStart);
323 ASSERT(timing.playbackRate == 1);
324 ASSERT(timing.iterationDuration >= 0 && std::isfinite(timing.iterationDuration));
326 isPaused = animationData->isPlayStateSet() && animationData->playState() == AnimPlayStatePaused;
327 return animationData->isTimingFunctionSet() ? animationData->timingFunction() : CSSAnimationData::initialAnimationTimingFunction();
332 const StyleRuleKeyframes* CSSAnimations::matchScopedKeyframesRule(StyleResolver* resolver, const Element* element, const StringImpl* animationName)
334 if (resolver->styleTreeHasOnlyScopedResolverForDocument())
335 return resolver->styleTreeScopedStyleResolverForDocument()->keyframeStylesForAnimation(animationName);
337 Vector<ScopedStyleResolver*, 8> stack;
338 resolver->styleTreeResolveScopedKeyframesRules(element, stack);
342 for (size_t i = 0; i < stack.size(); ++i) {
343 if (const StyleRuleKeyframes* keyframesRule = stack.at(i)->keyframeStylesForAnimation(animationName))
344 return keyframesRule;
349 PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
351 ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled());
352 OwnPtr<CSSAnimationUpdate> update = adoptPtr(new CSSAnimationUpdate());
353 calculateAnimationUpdate(update.get(), element, parentElement, style, parentStyle, resolver);
354 calculateAnimationCompositableValues(update.get(), element);
355 calculateTransitionUpdate(update.get(), element, style);
356 calculateTransitionCompositableValues(update.get(), element);
357 return update->isEmpty() ? nullptr : update.release();
360 void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
362 const ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
365 // If we're in an animation style change, no animations can have started, been cancelled or changed play state.
366 // When ASSERT is enabled, we verify this optimization.
367 if (activeAnimations && activeAnimations->isAnimationStyleChange())
371 const CSSAnimationDataList* animationDataList = style.animations();
372 const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : 0;
374 HashSet<AtomicString> inactive;
376 for (AnimationMap::const_iterator iter = cssAnimations->m_animations.begin(); iter != cssAnimations->m_animations.end(); ++iter)
377 inactive.add(iter->key);
379 if (style.display() != NONE) {
380 for (size_t i = 0; animationDataList && i < animationDataList->size(); ++i) {
381 const CSSAnimationData* animationData = animationDataList->animation(i);
382 if (animationData->isNoneAnimation())
384 ASSERT(animationData->isValidAnimation());
385 AtomicString animationName(animationData->name());
387 // Keyframes and animation properties are snapshotted when the
388 // animation starts, so we don't need to track changes to these,
389 // with the exception of play-state.
391 AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName));
392 if (existing != cssAnimations->m_animations.end()) {
393 inactive.remove(animationName);
394 const HashSet<RefPtr<Player> >& players = existing->value;
395 ASSERT(!players.isEmpty());
396 bool isFirstPlayerPaused = (*players.begin())->paused();
398 for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter)
399 ASSERT((*iter)->paused() == isFirstPlayerPaused);
401 if ((animationData->playState() == AnimPlayStatePaused) != isFirstPlayerPaused) {
402 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
403 update->toggleAnimationPaused(animationName);
411 RefPtr<TimingFunction> defaultTimingFunction = timingFromAnimationData(animationData, timing, isPaused);
412 Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > > keyframesAndTimingFunctions;
413 resolveKeyframes(resolver, element, parentElement, style, parentStyle, animationName, defaultTimingFunction.get(), keyframesAndTimingFunctions);
414 if (!keyframesAndTimingFunctions.isEmpty()) {
415 HashSet<RefPtr<InertAnimation> > animations;
416 for (size_t j = 0; j < keyframesAndTimingFunctions.size(); ++j) {
417 ASSERT(!keyframesAndTimingFunctions[j].first.isEmpty());
418 timing.timingFunction = keyframesAndTimingFunctions[j].second;
419 // FIXME: crbug.com/268791 - Keyframes are already normalized, perhaps there should be a flag on KeyframeEffectModel to skip normalization.
420 animations.add(InertAnimation::create(KeyframeEffectModel::create(keyframesAndTimingFunctions[j].first), timing, isPaused));
422 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
423 update->startAnimation(animationName, animations);
428 ASSERT(inactive.isEmpty() || cssAnimations);
429 for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter) {
430 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
431 update->cancelAnimation(*iter, cssAnimations->m_animations.get(*iter));
435 void CSSAnimations::maybeApplyPendingUpdate(Element* element)
437 if (!m_pendingUpdate) {
438 m_previousCompositableValuesForAnimations.clear();
442 OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
444 m_previousCompositableValuesForAnimations.swap(update->compositableValuesForAnimations());
446 for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationNames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) {
447 const HashSet<RefPtr<Player> >& players = m_animations.take(*iter);
448 for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter)
452 for (Vector<AtomicString>::const_iterator iter = update->animationsWithPauseToggled().begin(); iter != update->animationsWithPauseToggled().end(); ++iter) {
453 const HashSet<RefPtr<Player> >& players = m_animations.get(*iter);
454 ASSERT(!players.isEmpty());
455 bool isFirstPlayerPaused = (*players.begin())->paused();
456 for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter) {
457 Player* player = iter->get();
458 ASSERT(player->paused() == isFirstPlayerPaused);
459 player->setPaused(!isFirstPlayerPaused);
463 for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
464 OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEventDelegate(element, iter->name));
465 HashSet<RefPtr<Player> > players;
466 for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = iter->animations.begin(); animationsIter != iter->animations.end(); ++animationsIter) {
467 const InertAnimation* inertAnimation = animationsIter->get();
468 // The event delegate is set on the the first animation only. We
469 // rely on the behavior of OwnPtr::release() to achieve this.
470 RefPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specified(), Animation::DefaultPriority, eventDelegate.release());
471 Player* player = element->document().timeline()->createPlayer(animation.get());
472 player->setPaused(inertAnimation->paused());
473 element->document().cssPendingAnimations().add(player);
477 m_animations.set(iter->name, players);
480 // Transitions that are run on the compositor only update main-thread state
481 // lazily. However, we need the new state to know what the from state shoud
482 // be when transitions are retargeted. Instead of triggering complete style
483 // recalculation, we find these cases by searching for new transitions that
484 // have matching cancelled animation property IDs on the compositor.
485 HashMap<CSSPropertyID, std::pair<RefPtr<Animation>, double> > retargetedCompositorTransitions;
486 const ActiveAnimations* activeAnimations = element->activeAnimations();
487 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
488 CSSPropertyID id = *iter;
489 ASSERT(m_transitions.contains(id));
490 Player* player = m_transitions.take(id).transition->player();
491 if (activeAnimations && activeAnimations->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end())
492 retargetedCompositorTransitions.add(id, std::pair<RefPtr<Animation>, double>(toAnimation(player->source()), player->startTime()));
496 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter) {
497 const CSSAnimationUpdate::NewTransition& newTransition = iter->value;
499 RunningTransition runningTransition;
500 runningTransition.from = newTransition.from;
501 runningTransition.to = newTransition.to;
503 CSSPropertyID id = newTransition.id;
504 InertAnimation* inertAnimation = newTransition.animation.get();
505 OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionEventDelegate(element, id));
507 RefPtr<AnimationEffect> effect = inertAnimation->effect();
509 if (retargetedCompositorTransitions.contains(id)) {
510 const std::pair<RefPtr<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id);
511 RefPtr<Animation> oldAnimation = oldTransition.first;
512 double oldStartTime = oldTransition.second;
513 double inheritedTime = isNull(oldStartTime) ? 0 : element->document().transitionTimeline()->currentTime() - oldStartTime;
514 oldAnimation->updateInheritedTime(inheritedTime);
515 KeyframeEffectModel* oldEffect = toKeyframeEffectModel(inertAnimation->effect());
516 const KeyframeEffectModel::KeyframeVector& frames = oldEffect->getFrames();
517 KeyframeEffectModel::KeyframeVector newFrames;
518 newFrames.append(frames[0]->clone());
519 newFrames[0]->clearPropertyValue(id);
520 ASSERT(oldAnimation->compositableValues()->size() == 1);
521 const AnimationEffect::CompositableValue* compositableValue = oldAnimation->compositableValues()->at(0).second.get();
522 ASSERT(!compositableValue->dependsOnUnderlyingValue());
523 newFrames[0]->setPropertyValue(id, compositableValue->compositeOnto(0).get());
524 newFrames.append(frames[1]->clone());
525 effect = KeyframeEffectModel::create(newFrames);
527 RefPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specified(), Animation::TransitionPriority, eventDelegate.release());
528 RefPtr<Player> player = element->document().transitionTimeline()->createPlayer(transition.get());
530 element->document().cssPendingAnimations().add(player.get());
531 runningTransition.transition = transition.get();
532 m_transitions.set(id, runningTransition);
533 ASSERT(id != CSSPropertyInvalid);
534 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id));
538 void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, const CSSAnimationData* anim, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* activeTransitions, CSSAnimationUpdate* update, const Element* element)
540 RefPtr<AnimatableValue> to;
541 if (activeTransitions) {
542 TransitionMap::const_iterator activeTransitionIter = activeTransitions->find(id);
543 if (activeTransitionIter != activeTransitions->end()) {
544 to = CSSAnimatableValueFactory::create(id, style);
545 const AnimatableValue* activeTo = activeTransitionIter->value.to;
546 if (to->equals(activeTo))
548 update->cancelTransition(id);
549 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
553 to = CSSAnimatableValueFactory::create(id, style);
554 RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
555 if (to->equals(from.get()))
558 // If we have multiple transitions on the same property, we will use the
559 // last one since we iterate over them in order.
560 if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get()))
563 KeyframeEffectModel::KeyframeVector keyframes;
565 RefPtr<Keyframe> startKeyframe = Keyframe::create();
566 startKeyframe->setPropertyValue(id, from.get());
567 startKeyframe->setOffset(0);
568 keyframes.append(startKeyframe);
570 RefPtr<Keyframe> endKeyframe = Keyframe::create();
571 endKeyframe->setPropertyValue(id, to.get());
572 endKeyframe->setOffset(1);
573 keyframes.append(endKeyframe);
575 RefPtr<KeyframeEffectModel> effect = KeyframeEffectModel::create(keyframes);
579 RefPtr<TimingFunction> timingFunction = timingFromAnimationData(anim, timing, isPaused);
581 timing.timingFunction = timingFunction;
582 // Note that the backwards part is required for delay to work.
583 timing.fillMode = Timing::FillModeBoth;
585 update->startTransition(id, from.get(), to.get(), InertAnimation::create(effect, timing, isPaused));
586 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
589 void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const Element* element, const RenderStyle& style)
594 ActiveAnimations* activeAnimations = element->activeAnimations();
595 const TransitionMap* activeTransitions = activeAnimations ? &activeAnimations->cssAnimations().m_transitions : 0;
598 // In release builds we avoid the cost of checking for new and interrupted transitions if the style recalc is due to animation.
599 const bool animationStyleRecalc = activeAnimations && activeAnimations->isAnimationStyleChange();
601 // 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.
602 const bool animationStyleRecalc = false;
605 BitArray<numCSSProperties> listedProperties;
606 bool anyTransitionHadAnimateAll = false;
607 const RenderObject* renderer = element->renderer();
608 if (!animationStyleRecalc && style.display() != NONE && renderer && renderer->style() && style.transitions()) {
609 const RenderStyle& oldStyle = *renderer->style();
611 for (size_t i = 0; i < style.transitions()->size(); ++i) {
612 const CSSAnimationData* anim = style.transitions()->animation(i);
613 CSSAnimationData::AnimationMode mode = anim->animationMode();
614 if (anim->duration() + anim->delay() <= 0 || mode == CSSAnimationData::AnimateNone)
617 bool animateAll = mode == CSSAnimationData::AnimateAll;
618 ASSERT(animateAll || mode == CSSAnimationData::AnimateSingleProperty);
620 anyTransitionHadAnimateAll = true;
621 const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(anim->property());
622 // If not a shorthand we only execute one iteration of this loop, and refer to the property directly.
623 for (unsigned j = 0; !j || j < propertyList.length(); ++j) {
624 CSSPropertyID id = propertyList.length() ? propertyList.properties()[j] : anim->property();
627 if (CSSAnimations::isAnimatableProperty(id))
628 listedProperties.set(id);
633 // FIXME: We should transition if an !important property changes even when an animation is running,
634 // but this is a bit hard to do with the current applyMatchedProperties system.
635 if (!update->compositableValuesForAnimations().contains(id)
636 && (!activeAnimations || !activeAnimations->cssAnimations().m_previousCompositableValuesForAnimations.contains(id))) {
637 calculateTransitionUpdateForProperty(id, anim, oldStyle, style, activeTransitions, update, element);
643 if (activeTransitions) {
644 for (TransitionMap::const_iterator iter = activeTransitions->begin(); iter != activeTransitions->end(); ++iter) {
645 const TimedItem* timedItem = iter->value.transition;
646 CSSPropertyID id = iter->key;
647 if (timedItem->phase() == TimedItem::PhaseAfter || (!anyTransitionHadAnimateAll && !animationStyleRecalc && !listedProperties.get(id))) {
648 ASSERT(timedItem->phase() == TimedItem::PhaseAfter || !(activeAnimations && activeAnimations->isAnimationStyleChange()));
649 update->cancelTransition(id);
655 void CSSAnimations::cancel()
657 for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animations.end(); ++iter) {
658 const HashSet<RefPtr<Player> >& players = iter->value;
659 for (HashSet<RefPtr<Player> >::const_iterator animationsIter = players.begin(); animationsIter != players.end(); ++animationsIter)
660 (*animationsIter)->cancel();
663 for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transitions.end(); ++iter)
664 iter->value.transition->player()->cancel();
666 m_animations.clear();
667 m_transitions.clear();
668 m_pendingUpdate = nullptr;
671 void CSSAnimations::calculateAnimationCompositableValues(CSSAnimationUpdate* update, const Element* element)
673 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
674 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
676 if (update->newAnimations().isEmpty() && update->cancelledAnimationPlayers().isEmpty()) {
677 AnimationEffect::CompositableValueMap compositableValuesForAnimations(AnimationStack::compositableValues(animationStack, 0, 0, Animation::DefaultPriority));
678 update->adoptCompositableValuesForAnimations(compositableValuesForAnimations);
682 Vector<InertAnimation*> newAnimations;
683 for (size_t i = 0; i < update->newAnimations().size(); ++i) {
684 HashSet<RefPtr<InertAnimation> > animations = update->newAnimations()[i].animations;
685 for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = animations.begin(); animationsIter != animations.end(); ++animationsIter)
686 newAnimations.append(animationsIter->get());
688 AnimationEffect::CompositableValueMap compositableValuesForAnimations(AnimationStack::compositableValues(animationStack, &newAnimations, &update->cancelledAnimationPlayers(), Animation::DefaultPriority));
689 update->adoptCompositableValuesForAnimations(compositableValuesForAnimations);
692 void CSSAnimations::calculateTransitionCompositableValues(CSSAnimationUpdate* update, const Element* element)
694 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
695 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
697 AnimationEffect::CompositableValueMap compositableValuesForTransitions;
698 if (update->newTransitions().isEmpty() && update->cancelledTransitions().isEmpty()) {
699 compositableValuesForTransitions = AnimationStack::compositableValues(animationStack, 0, 0, Animation::TransitionPriority);
701 Vector<InertAnimation*> newTransitions;
702 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter)
703 newTransitions.append(iter->value.animation.get());
705 HashSet<const Player*> cancelledPlayers;
706 if (!update->cancelledTransitions().isEmpty()) {
707 ASSERT(activeAnimations);
708 const TransitionMap& transitionMap = activeAnimations->cssAnimations().m_transitions;
709 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
710 ASSERT(transitionMap.contains(*iter));
711 cancelledPlayers.add(transitionMap.get(*iter).transition->player());
715 compositableValuesForTransitions = AnimationStack::compositableValues(animationStack, &newTransitions, &cancelledPlayers, Animation::TransitionPriority);
718 // Properties being animated by animations don't get values from transitions applied.
719 if (!update->compositableValuesForAnimations().isEmpty() && !compositableValuesForTransitions.isEmpty()) {
720 for (AnimationEffect::CompositableValueMap::const_iterator iter = update->compositableValuesForAnimations().begin(); iter != update->compositableValuesForAnimations().end(); ++iter)
721 compositableValuesForTransitions.remove(iter->key);
723 update->adoptCompositableValuesForTransitions(compositableValuesForTransitions);
726 void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType listenerType, const AtomicString& eventName, double elapsedTime)
728 if (m_target->document().hasListenerType(listenerType))
729 m_target->document().timeline()->addEventToDispatch(m_target, WebKitAnimationEvent::create(eventName, m_name, elapsedTime));
732 void CSSAnimations::AnimationEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
734 // Events for a single document are queued and dispatched as a group at
735 // the end of DocumentTimeline::serviceAnimations.
736 // FIXME: Events which are queued outside of serviceAnimations should
737 // trigger a timer to dispatch when control is released.
738 const TimedItem::Phase currentPhase = timedItem->phase();
739 const double currentIteration = timedItem->currentIteration();
741 // Note that the elapsedTime is measured from when the animation starts playing.
742 if (!isFirstSample && previousPhase == TimedItem::PhaseActive && currentPhase == TimedItem::PhaseActive && previousIteration != currentIteration) {
743 ASSERT(!isNull(previousIteration));
744 ASSERT(!isNull(currentIteration));
745 // We fire only a single event for all iterations thast terminate
746 // between a single pair of samples. See http://crbug.com/275263. For
747 // compatibility with the existing implementation, this event uses
748 // the elapsedTime for the first iteration in question.
749 ASSERT(timedItem->specified().hasIterationDuration);
750 const double elapsedTime = timedItem->specified().iterationDuration * (previousIteration + 1);
751 maybeDispatch(Document::ANIMATIONITERATION_LISTENER, EventTypeNames::animationiteration, elapsedTime);
754 if ((isFirstSample || previousPhase == TimedItem::PhaseBefore) && isLaterPhase(currentPhase, TimedItem::PhaseBefore)) {
755 ASSERT(timedItem->specified().startDelay > 0 || isFirstSample);
756 // The spec states that the elapsed time should be
757 // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing
758 // implementation. See crbug.com/279611
759 maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animationstart, 0);
761 if ((isFirstSample || isEarlierPhase(previousPhase, TimedItem::PhaseAfter)) && currentPhase == TimedItem::PhaseAfter)
762 maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animationend, timedItem->activeDuration());
765 void CSSAnimations::TransitionEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
767 // Events for a single document are queued and dispatched as a group at
768 // the end of DocumentTimeline::serviceAnimations.
769 // FIXME: Events which are queued outside of serviceAnimations should
770 // trigger a timer to dispatch when control is released.
771 const TimedItem::Phase currentPhase = timedItem->phase();
772 if (currentPhase == TimedItem::PhaseAfter && (isFirstSample || previousPhase != currentPhase) && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
773 String propertyName = getPropertyNameString(m_property);
774 const Timing& timing = timedItem->specified();
775 double elapsedTime = timing.iterationDuration;
776 const AtomicString& eventType = EventTypeNames::transitionend;
777 String pseudoElement = PseudoElement::pseudoElementNameForEvents(m_target->pseudoId());
778 m_target->document().transitionTimeline()->addEventToDispatch(m_target, TransitionEvent::create(eventType, propertyName, elapsedTime, pseudoElement));
783 bool CSSAnimations::isAnimatableProperty(CSSPropertyID property)
786 case CSSPropertyBackgroundColor:
787 case CSSPropertyBackgroundImage:
788 case CSSPropertyBackgroundPositionX:
789 case CSSPropertyBackgroundPositionY:
790 case CSSPropertyBackgroundSize:
791 case CSSPropertyBaselineShift:
792 case CSSPropertyBorderBottomColor:
793 case CSSPropertyBorderBottomLeftRadius:
794 case CSSPropertyBorderBottomRightRadius:
795 case CSSPropertyBorderBottomWidth:
796 case CSSPropertyBorderImageOutset:
797 case CSSPropertyBorderImageSlice:
798 case CSSPropertyBorderImageSource:
799 case CSSPropertyBorderImageWidth:
800 case CSSPropertyBorderLeftColor:
801 case CSSPropertyBorderLeftWidth:
802 case CSSPropertyBorderRightColor:
803 case CSSPropertyBorderRightWidth:
804 case CSSPropertyBorderTopColor:
805 case CSSPropertyBorderTopLeftRadius:
806 case CSSPropertyBorderTopRightRadius:
807 case CSSPropertyBorderTopWidth:
808 case CSSPropertyBottom:
809 case CSSPropertyBoxShadow:
810 case CSSPropertyClip:
811 case CSSPropertyColor:
812 case CSSPropertyFill:
813 case CSSPropertyFillOpacity:
814 case CSSPropertyFlexBasis:
815 case CSSPropertyFlexGrow:
816 case CSSPropertyFlexShrink:
817 case CSSPropertyFloodColor:
818 case CSSPropertyFloodOpacity:
819 case CSSPropertyFontSize:
820 case CSSPropertyHeight:
821 case CSSPropertyKerning:
822 case CSSPropertyLeft:
823 case CSSPropertyLetterSpacing:
824 case CSSPropertyLightingColor:
825 case CSSPropertyLineHeight:
826 case CSSPropertyListStyleImage:
827 case CSSPropertyMarginBottom:
828 case CSSPropertyMarginLeft:
829 case CSSPropertyMarginRight:
830 case CSSPropertyMarginTop:
831 case CSSPropertyMaxHeight:
832 case CSSPropertyMaxWidth:
833 case CSSPropertyMinHeight:
834 case CSSPropertyMinWidth:
835 case CSSPropertyObjectPosition:
836 case CSSPropertyOpacity:
837 case CSSPropertyOrphans:
838 case CSSPropertyOutlineColor:
839 case CSSPropertyOutlineOffset:
840 case CSSPropertyOutlineWidth:
841 case CSSPropertyPaddingBottom:
842 case CSSPropertyPaddingLeft:
843 case CSSPropertyPaddingRight:
844 case CSSPropertyPaddingTop:
845 case CSSPropertyRight:
846 case CSSPropertyStopColor:
847 case CSSPropertyStopOpacity:
848 case CSSPropertyStroke:
849 case CSSPropertyStrokeDasharray:
850 case CSSPropertyStrokeDashoffset:
851 case CSSPropertyStrokeMiterlimit:
852 case CSSPropertyStrokeOpacity:
853 case CSSPropertyStrokeWidth:
854 case CSSPropertyTextDecorationColor:
855 case CSSPropertyTextIndent:
856 case CSSPropertyTextShadow:
858 case CSSPropertyVisibility:
859 case CSSPropertyWebkitBackgroundSize:
860 case CSSPropertyWebkitBorderHorizontalSpacing:
861 case CSSPropertyWebkitBorderVerticalSpacing:
862 case CSSPropertyWebkitBoxShadow:
863 case CSSPropertyWebkitClipPath:
864 case CSSPropertyWebkitColumnCount:
865 case CSSPropertyWebkitColumnGap:
866 case CSSPropertyWebkitColumnRuleColor:
867 case CSSPropertyWebkitColumnRuleWidth:
868 case CSSPropertyWebkitColumnWidth:
869 case CSSPropertyWebkitFilter:
870 case CSSPropertyWebkitMaskBoxImageOutset:
871 case CSSPropertyWebkitMaskBoxImageSlice:
872 case CSSPropertyWebkitMaskBoxImageSource:
873 case CSSPropertyWebkitMaskBoxImageWidth:
874 case CSSPropertyWebkitMaskImage:
875 case CSSPropertyWebkitMaskPositionX:
876 case CSSPropertyWebkitMaskPositionY:
877 case CSSPropertyWebkitMaskSize:
878 case CSSPropertyWebkitPerspective:
879 case CSSPropertyWebkitPerspectiveOriginX:
880 case CSSPropertyWebkitPerspectiveOriginY:
881 case CSSPropertyShapeInside:
882 case CSSPropertyShapeOutside:
883 case CSSPropertyShapeMargin:
884 case CSSPropertyShapeImageThreshold:
885 case CSSPropertyWebkitTextStrokeColor:
886 case CSSPropertyWebkitTransform:
887 case CSSPropertyWebkitTransformOriginX:
888 case CSSPropertyWebkitTransformOriginY:
889 case CSSPropertyWebkitTransformOriginZ:
890 case CSSPropertyWidows:
891 case CSSPropertyWidth:
892 case CSSPropertyWordSpacing:
893 case CSSPropertyZIndex:
894 case CSSPropertyZoom:
896 // FIXME: Shorthands should not be present in this list, but
897 // CSSPropertyAnimation implements animation of these shorthands
898 // directly and makes use of this method.
899 case CSSPropertyFlex:
900 return !RuntimeEnabledFeatures::webAnimationsCSSEnabled();
906 const StylePropertyShorthand& CSSAnimations::animatableProperties()
908 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
909 DEFINE_STATIC_LOCAL(StylePropertyShorthand, propertyShorthand, ());
910 if (properties.isEmpty()) {
911 for (int i = firstCSSProperty; i < lastCSSProperty; ++i) {
912 CSSPropertyID id = convertToCSSPropertyID(i);
913 if (isAnimatableProperty(id))
914 properties.append(id);
916 propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, properties.begin(), properties.size());
918 return propertyShorthand;
921 } // namespace WebCore