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/CSSPropertyAnimation.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/ThreadLocalEventNames.h"
47 #include "core/events/TransitionEvent.h"
48 #include "core/events/WebKitAnimationEvent.h"
49 #include "core/frame/UseCounter.h"
50 #include "core/rendering/RenderLayer.h"
51 #include "core/rendering/RenderObject.h"
52 #include "core/rendering/style/KeyframeList.h"
53 #include "platform/animation/TimingFunction.h"
54 #include "public/platform/Platform.h"
55 #include "wtf/BitArray.h"
56 #include "wtf/HashSet.h"
62 bool isEarlierPhase(TimedItem::Phase target, TimedItem::Phase reference)
64 ASSERT(target != TimedItem::PhaseNone);
65 ASSERT(reference != TimedItem::PhaseNone);
66 return target < reference;
69 bool isLaterPhase(TimedItem::Phase target, TimedItem::Phase reference)
71 ASSERT(target != TimedItem::PhaseNone);
72 ASSERT(reference != TimedItem::PhaseNone);
73 return target > reference;
76 static PassRefPtr<TimingFunction> generateTimingFunction(const KeyframeEffectModel::KeyframeVector keyframes, const HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions)
78 // Generate the chained timing function. Note that timing functions apply
79 // from the keyframe in which they're specified to the next keyframe.
80 bool isTimingFunctionLinearThroughout = true;
81 RefPtr<ChainedTimingFunction> chainedTimingFunction = ChainedTimingFunction::create();
82 for (size_t i = 0; i < keyframes.size() - 1; ++i) {
83 double lowerBound = keyframes[i]->offset();
84 ASSERT(lowerBound >=0 && lowerBound < 1);
85 double upperBound = keyframes[i + 1]->offset();
86 ASSERT(upperBound > 0 && upperBound <= 1);
87 TimingFunction* timingFunction = perKeyframeTimingFunctions.get(lowerBound);
88 isTimingFunctionLinearThroughout &= timingFunction->type() == TimingFunction::LinearFunction;
89 chainedTimingFunction->appendSegment(upperBound, timingFunction);
91 if (isTimingFunctionLinearThroughout)
92 return LinearTimingFunction::create();
93 return chainedTimingFunction;
96 static void resolveKeyframes(StyleResolver* resolver, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, const AtomicString& name, TimingFunction* defaultTimingFunction,
97 Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > >& keyframesAndTimingFunctions)
99 // When the element is null, use its parent for scoping purposes.
100 const Element* elementForScoping = element ? element : &parentElement;
101 const StyleRuleKeyframes* keyframesRule = CSSAnimations::matchScopedKeyframesRule(resolver, elementForScoping, name.impl());
105 const Vector<RefPtr<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
106 if (styleKeyframes.isEmpty())
109 // Construct and populate the style for each keyframe
110 PropertySet specifiedProperties;
111 KeyframeEffectModel::KeyframeVector keyframes;
112 HashMap<double, RefPtr<TimingFunction> > perKeyframeTimingFunctions;
113 for (size_t i = 0; i < styleKeyframes.size(); ++i) {
114 const StyleKeyframe* styleKeyframe = styleKeyframes[i].get();
115 // It's OK to pass a null element here.
116 RefPtr<RenderStyle> keyframeStyle = resolver->styleForKeyframe(element, style, parentStyle, styleKeyframe, name);
117 RefPtr<Keyframe> keyframe = Keyframe::create();
118 const Vector<double>& offsets = styleKeyframe->keys();
119 ASSERT(!offsets.isEmpty());
120 keyframe->setOffset(offsets[0]);
121 TimingFunction* timingFunction = defaultTimingFunction;
122 const StylePropertySet* properties = styleKeyframe->properties();
123 for (unsigned j = 0; j < properties->propertyCount(); j++) {
124 CSSPropertyID property = properties->propertyAt(j).id();
125 specifiedProperties.add(property);
126 if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction)
127 timingFunction = KeyframeValue::timingFunction(*keyframeStyle);
128 else if (CSSAnimations::isAnimatableProperty(property))
129 keyframe->setPropertyValue(property, CSSAnimatableValueFactory::create(property, *keyframeStyle).get());
131 keyframes.append(keyframe);
132 // The last keyframe specified at a given offset is used.
133 perKeyframeTimingFunctions.set(offsets[0], timingFunction);
134 for (size_t j = 1; j < offsets.size(); ++j) {
135 keyframes.append(keyframe->cloneWithOffset(offsets[j]));
136 perKeyframeTimingFunctions.set(offsets[j], timingFunction);
139 ASSERT(!keyframes.isEmpty());
141 if (!perKeyframeTimingFunctions.contains(0))
142 perKeyframeTimingFunctions.set(0, defaultTimingFunction);
144 for (PropertySet::const_iterator iter = specifiedProperties.begin(); iter != specifiedProperties.end(); ++iter) {
145 const CSSPropertyID property = *iter;
146 ASSERT(property != CSSPropertyInvalid);
147 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property));
150 // Remove duplicate keyframes. In CSS the last keyframe at a given offset takes priority.
151 std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets);
152 size_t targetIndex = 0;
153 for (size_t i = 1; i < keyframes.size(); i++) {
154 if (keyframes[i]->offset() != keyframes[targetIndex]->offset())
156 if (targetIndex != i)
157 keyframes[targetIndex] = keyframes[i];
159 keyframes.shrink(targetIndex + 1);
161 // Add 0% and 100% keyframes if absent.
162 RefPtr<Keyframe> startKeyframe = keyframes[0];
163 if (startKeyframe->offset()) {
164 startKeyframe = Keyframe::create();
165 startKeyframe->setOffset(0);
166 keyframes.prepend(startKeyframe);
168 RefPtr<Keyframe> endKeyframe = keyframes[keyframes.size() - 1];
169 if (endKeyframe->offset() != 1) {
170 endKeyframe = Keyframe::create();
171 endKeyframe->setOffset(1);
172 keyframes.append(endKeyframe);
174 ASSERT(keyframes.size() >= 2);
175 ASSERT(!keyframes.first()->offset());
176 ASSERT(keyframes.last()->offset() == 1);
178 // Snapshot current property values for 0% and 100% if missing.
179 PropertySet allProperties;
180 size_t numKeyframes = keyframes.size();
181 for (size_t i = 0; i < numKeyframes; i++) {
182 const PropertySet& keyframeProperties = keyframes[i]->properties();
183 for (PropertySet::const_iterator iter = keyframeProperties.begin(); iter != keyframeProperties.end(); ++iter)
184 allProperties.add(*iter);
186 const PropertySet& startKeyframeProperties = startKeyframe->properties();
187 const PropertySet& endKeyframeProperties = endKeyframe->properties();
188 bool missingStartValues = startKeyframeProperties.size() < allProperties.size();
189 bool missingEndValues = endKeyframeProperties.size() < allProperties.size();
190 if (missingStartValues || missingEndValues) {
191 for (PropertySet::const_iterator iter = allProperties.begin(); iter != allProperties.end(); ++iter) {
192 const CSSPropertyID property = *iter;
193 bool startNeedsValue = missingStartValues && !startKeyframeProperties.contains(property);
194 bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains(property);
195 if (!startNeedsValue && !endNeedsValue)
197 RefPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style);
199 startKeyframe->setPropertyValue(property, snapshotValue.get());
201 endKeyframe->setPropertyValue(property, snapshotValue.get());
204 ASSERT(startKeyframe->properties().size() == allProperties.size());
205 ASSERT(endKeyframe->properties().size() == allProperties.size());
207 // Determine how many keyframes specify each property. Note that this must
208 // be done after we've filled in end keyframes.
209 typedef HashCountedSet<CSSPropertyID> PropertyCountedSet;
210 PropertyCountedSet propertyCounts;
211 for (size_t i = 0; i < numKeyframes; ++i) {
212 const PropertySet& properties = keyframes[i]->properties();
213 for (PropertySet::const_iterator iter = properties.begin(); iter != properties.end(); ++iter)
214 propertyCounts.add(*iter);
217 // Split keyframes into groups, where each group contains only keyframes
218 // which specify all properties used in that group. Each group is animated
219 // in a separate animation, to allow per-keyframe timing functions to be
220 // applied correctly.
221 for (PropertyCountedSet::const_iterator iter = propertyCounts.begin(); iter != propertyCounts.end(); ++iter) {
222 const CSSPropertyID property = iter->key;
223 const size_t count = iter->value;
224 ASSERT(count <= numKeyframes);
225 if (count == numKeyframes)
227 KeyframeEffectModel::KeyframeVector splitOutKeyframes;
228 for (size_t i = 0; i < numKeyframes; i++) {
229 Keyframe* keyframe = keyframes[i].get();
230 if (!keyframe->properties().contains(property)) {
231 ASSERT(i && i != numKeyframes - 1);
234 RefPtr<Keyframe> clonedKeyframe = Keyframe::create();
235 clonedKeyframe->setOffset(keyframe->offset());
236 clonedKeyframe->setComposite(keyframe->composite());
237 clonedKeyframe->setPropertyValue(property, keyframe->propertyValue(property));
238 splitOutKeyframes.append(clonedKeyframe);
239 // Note that it's OK if this keyframe ends up having no
240 // properties. This can only happen when none of the properties
241 // are specified in all keyframes, in which case we won't animate
242 // anything with these keyframes.
243 keyframe->clearPropertyValue(property);
245 ASSERT(!splitOutKeyframes.first()->offset());
246 ASSERT(splitOutKeyframes.last()->offset() == 1);
248 for (size_t j = 0; j < splitOutKeyframes.size(); ++j)
249 ASSERT(splitOutKeyframes[j]->properties().size() == 1);
251 keyframesAndTimingFunctions.append(std::make_pair(splitOutKeyframes, generateTimingFunction(splitOutKeyframes, perKeyframeTimingFunctions)));
254 unsigned numPropertiesSpecifiedInAllKeyframes = keyframes.first()->properties().size();
256 for (size_t i = 1; i < numKeyframes; ++i)
257 ASSERT(keyframes[i]->properties().size() == numPropertiesSpecifiedInAllKeyframes);
260 // If the animation specifies any keyframes, we always provide at least one
261 // vector of resolved keyframes, even if no properties are animated.
262 if (numPropertiesSpecifiedInAllKeyframes || keyframesAndTimingFunctions.isEmpty())
263 keyframesAndTimingFunctions.append(std::make_pair(keyframes, generateTimingFunction(keyframes, perKeyframeTimingFunctions)));
266 // Returns the default timing function.
267 const PassRefPtr<TimingFunction> timingFromAnimationData(const CSSAnimationData* animationData, Timing& timing, bool& isPaused)
269 if (animationData->isDelaySet())
270 timing.startDelay = animationData->delay();
271 if (animationData->isDurationSet())
272 timing.iterationDuration = animationData->duration();
273 if (animationData->isIterationCountSet()) {
274 if (animationData->iterationCount() == CSSAnimationData::IterationCountInfinite)
275 timing.iterationCount = std::numeric_limits<double>::infinity();
277 timing.iterationCount = animationData->iterationCount();
279 if (animationData->isFillModeSet()) {
280 switch (animationData->fillMode()) {
281 case AnimationFillModeForwards:
282 timing.fillMode = Timing::FillModeForwards;
284 case AnimationFillModeBackwards:
285 timing.fillMode = Timing::FillModeBackwards;
287 case AnimationFillModeBoth:
288 timing.fillMode = Timing::FillModeBoth;
290 case AnimationFillModeNone:
291 timing.fillMode = Timing::FillModeNone;
294 ASSERT_NOT_REACHED();
297 timing.fillMode = Timing::FillModeNone;
299 if (animationData->isDirectionSet()) {
300 switch (animationData->direction()) {
301 case CSSAnimationData::AnimationDirectionNormal:
302 timing.direction = Timing::PlaybackDirectionNormal;
304 case CSSAnimationData::AnimationDirectionAlternate:
305 timing.direction = Timing::PlaybackDirectionAlternate;
307 case CSSAnimationData::AnimationDirectionReverse:
308 timing.direction = Timing::PlaybackDirectionReverse;
310 case CSSAnimationData::AnimationDirectionAlternateReverse:
311 timing.direction = Timing::PlaybackDirectionAlternateReverse;
314 ASSERT_NOT_REACHED();
318 // For CSS, the constraints on the timing properties are tighter than in
319 // the general case of the Web Animations model.
320 timing.assertValid();
321 ASSERT(!timing.iterationStart);
322 ASSERT(timing.playbackRate == 1);
323 ASSERT(!std::isinf(timing.iterationDuration));
325 isPaused = animationData->isPlayStateSet() && animationData->playState() == AnimPlayStatePaused;
326 return animationData->isTimingFunctionSet() ? animationData->timingFunction() : CSSAnimationData::initialAnimationTimingFunction();
331 const StyleRuleKeyframes* CSSAnimations::matchScopedKeyframesRule(StyleResolver* resolver, const Element* element, const StringImpl* animationName)
333 if (resolver->styleTreeHasOnlyScopedResolverForDocument())
334 return resolver->styleTreeScopedStyleResolverForDocument()->keyframeStylesForAnimation(animationName);
336 Vector<ScopedStyleResolver*, 8> stack;
337 resolver->styleTreeResolveScopedKeyframesRules(element, stack);
341 for (size_t i = 0; i < stack.size(); ++i) {
342 if (const StyleRuleKeyframes* keyframesRule = stack.at(i)->keyframeStylesForAnimation(animationName))
343 return keyframesRule;
348 PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
350 OwnPtr<CSSAnimationUpdate> update = adoptPtr(new CSSAnimationUpdate());
351 calculateAnimationUpdate(update.get(), element, parentElement, style, parentStyle, resolver);
352 calculateAnimationCompositableValues(update.get(), element);
353 calculateTransitionUpdate(update.get(), element, style);
354 calculateTransitionCompositableValues(update.get(), element);
355 return update->isEmpty() ? nullptr : update.release();
358 void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
360 const ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
363 // If we're in an animation style change, no animations can have started, been cancelled or changed play state.
364 // When ASSERT is enabled, we verify this optimization.
365 if (activeAnimations && activeAnimations->isAnimationStyleChange())
369 const CSSAnimationDataList* animationDataList = style.animations();
370 const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : 0;
372 HashSet<AtomicString> inactive;
374 for (AnimationMap::const_iterator iter = cssAnimations->m_animations.begin(); iter != cssAnimations->m_animations.end(); ++iter)
375 inactive.add(iter->key);
377 if (style.display() != NONE) {
378 for (size_t i = 0; animationDataList && i < animationDataList->size(); ++i) {
379 const CSSAnimationData* animationData = animationDataList->animation(i);
380 if (animationData->isNoneAnimation())
382 ASSERT(animationData->isValidAnimation());
383 AtomicString animationName(animationData->name());
385 // Keyframes and animation properties are snapshotted when the
386 // animation starts, so we don't need to track changes to these,
387 // with the exception of play-state.
389 AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName));
390 if (existing != cssAnimations->m_animations.end()) {
391 inactive.remove(animationName);
392 const HashSet<RefPtr<Player> >& players = existing->value;
393 ASSERT(!players.isEmpty());
394 bool isFirstPlayerPaused = (*players.begin())->paused();
396 for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter)
397 ASSERT((*iter)->paused() == isFirstPlayerPaused);
399 if ((animationData->playState() == AnimPlayStatePaused) != isFirstPlayerPaused) {
400 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
401 update->toggleAnimationPaused(animationName);
409 RefPtr<TimingFunction> defaultTimingFunction = timingFromAnimationData(animationData, timing, isPaused);
410 Vector<std::pair<KeyframeEffectModel::KeyframeVector, RefPtr<TimingFunction> > > keyframesAndTimingFunctions;
411 resolveKeyframes(resolver, element, parentElement, style, parentStyle, animationName, defaultTimingFunction.get(), keyframesAndTimingFunctions);
412 if (!keyframesAndTimingFunctions.isEmpty()) {
413 HashSet<RefPtr<InertAnimation> > animations;
414 for (size_t j = 0; j < keyframesAndTimingFunctions.size(); ++j) {
415 ASSERT(!keyframesAndTimingFunctions[j].first.isEmpty());
416 timing.timingFunction = keyframesAndTimingFunctions[j].second;
417 // FIXME: crbug.com/268791 - Keyframes are already normalized, perhaps there should be a flag on KeyframeEffectModel to skip normalization.
418 animations.add(InertAnimation::create(KeyframeEffectModel::create(keyframesAndTimingFunctions[j].first), timing, isPaused));
420 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
421 update->startAnimation(animationName, animations);
426 ASSERT(inactive.isEmpty() || cssAnimations);
427 for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter) {
428 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
429 update->cancelAnimation(*iter, cssAnimations->m_animations.get(*iter));
433 void CSSAnimations::maybeApplyPendingUpdate(Element* element)
435 if (!m_pendingUpdate) {
436 m_previousCompositableValuesForAnimations.clear();
440 OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
442 m_previousCompositableValuesForAnimations.swap(update->compositableValuesForAnimations());
444 // FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here
445 // since we call this from recalc style.
446 // https://code.google.com/p/chromium/issues/detail?id=339847
447 DisableCompositingQueryAsserts disabler;
449 for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationNames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) {
450 const HashSet<RefPtr<Player> >& players = m_animations.take(*iter);
451 for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter)
455 for (Vector<AtomicString>::const_iterator iter = update->animationsWithPauseToggled().begin(); iter != update->animationsWithPauseToggled().end(); ++iter) {
456 const HashSet<RefPtr<Player> >& players = m_animations.get(*iter);
457 ASSERT(!players.isEmpty());
458 bool isFirstPlayerPaused = (*players.begin())->paused();
459 for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); iter != players.end(); ++iter) {
460 Player* player = iter->get();
461 ASSERT(player->paused() == isFirstPlayerPaused);
462 if (isFirstPlayerPaused)
469 for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
470 OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEventDelegate(element, iter->name));
471 HashSet<RefPtr<Player> > players;
472 for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = iter->animations.begin(); animationsIter != iter->animations.end(); ++animationsIter) {
473 const InertAnimation* inertAnimation = animationsIter->get();
474 // The event delegate is set on the the first animation only. We
475 // rely on the behavior of OwnPtr::release() to achieve this.
476 RefPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release());
477 Player* player = element->document().timeline()->createPlayer(animation.get());
478 if (inertAnimation->paused())
480 element->document().cssPendingAnimations().add(player);
484 m_animations.set(iter->name, players);
487 // Transitions that are run on the compositor only update main-thread state
488 // lazily. However, we need the new state to know what the from state shoud
489 // be when transitions are retargeted. Instead of triggering complete style
490 // recalculation, we find these cases by searching for new transitions that
491 // have matching cancelled animation property IDs on the compositor.
492 HashMap<CSSPropertyID, std::pair<RefPtr<Animation>, double> > retargetedCompositorTransitions;
493 const ActiveAnimations* activeAnimations = element->activeAnimations();
494 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
495 CSSPropertyID id = *iter;
496 ASSERT(m_transitions.contains(id));
497 Player* player = m_transitions.take(id).transition->player();
498 if (activeAnimations && activeAnimations->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end())
499 retargetedCompositorTransitions.add(id, std::pair<RefPtr<Animation>, double>(toAnimation(player->source()), player->startTime()));
503 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter) {
504 const CSSAnimationUpdate::NewTransition& newTransition = iter->value;
506 RunningTransition runningTransition;
507 runningTransition.from = newTransition.from;
508 runningTransition.to = newTransition.to;
510 CSSPropertyID id = newTransition.id;
511 InertAnimation* inertAnimation = newTransition.animation.get();
512 OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionEventDelegate(element, id));
514 RefPtr<AnimationEffect> effect = inertAnimation->effect();
516 if (retargetedCompositorTransitions.contains(id)) {
517 const std::pair<RefPtr<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id);
518 RefPtr<Animation> oldAnimation = oldTransition.first;
519 double oldStartTime = oldTransition.second;
520 double inheritedTime = isNull(oldStartTime) ? 0 : element->document().transitionTimeline()->currentTime() - oldStartTime;
521 oldAnimation->updateInheritedTime(inheritedTime);
522 KeyframeEffectModel* oldEffect = toKeyframeEffectModel(inertAnimation->effect());
523 const KeyframeEffectModel::KeyframeVector& frames = oldEffect->getFrames();
524 KeyframeEffectModel::KeyframeVector newFrames;
525 newFrames.append(frames[0]->clone());
526 newFrames[0]->clearPropertyValue(id);
527 ASSERT(oldAnimation->compositableValues()->size() == 1);
528 const AnimationEffect::CompositableValue* compositableValue = oldAnimation->compositableValues()->at(0).second.get();
529 ASSERT(!compositableValue->dependsOnUnderlyingValue());
530 newFrames[0]->setPropertyValue(id, compositableValue->compositeOnto(0).get());
531 newFrames.append(frames[1]->clone());
532 effect = KeyframeEffectModel::create(newFrames);
534 RefPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release());
535 RefPtr<Player> player = element->document().transitionTimeline()->createPlayer(transition.get());
537 element->document().cssPendingAnimations().add(player.get());
538 runningTransition.transition = transition.get();
539 m_transitions.set(id, runningTransition);
540 ASSERT(id != CSSPropertyInvalid);
541 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id));
545 void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, const CSSAnimationData* anim, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* activeTransitions, CSSAnimationUpdate* update, const Element* element)
547 RefPtr<AnimatableValue> to;
548 if (activeTransitions) {
549 TransitionMap::const_iterator activeTransitionIter = activeTransitions->find(id);
550 if (activeTransitionIter != activeTransitions->end()) {
551 to = CSSAnimatableValueFactory::create(id, style);
552 const AnimatableValue* activeTo = activeTransitionIter->value.to;
553 if (to->equals(activeTo))
555 update->cancelTransition(id);
556 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
560 if (anim->duration() + anim->delay() <= 0)
563 if (CSSPropertyAnimation::propertiesEqual(id, &oldStyle, &style))
566 to = CSSAnimatableValueFactory::create(id, style);
568 RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
569 // If we have multiple transitions on the same property, we will use the
570 // last one since we iterate over them in order.
571 if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get()))
574 KeyframeEffectModel::KeyframeVector keyframes;
576 RefPtr<Keyframe> startKeyframe = Keyframe::create();
577 startKeyframe->setPropertyValue(id, from.get());
578 startKeyframe->setOffset(0);
579 keyframes.append(startKeyframe);
581 RefPtr<Keyframe> endKeyframe = Keyframe::create();
582 endKeyframe->setPropertyValue(id, to.get());
583 endKeyframe->setOffset(1);
584 keyframes.append(endKeyframe);
586 RefPtr<KeyframeEffectModel> effect = KeyframeEffectModel::create(keyframes);
590 RefPtr<TimingFunction> timingFunction = timingFromAnimationData(anim, timing, isPaused);
592 timing.timingFunction = timingFunction;
593 // Note that the backwards part is required for delay to work.
594 timing.fillMode = Timing::FillModeBoth;
596 update->startTransition(id, from.get(), to.get(), InertAnimation::create(effect, timing, isPaused));
597 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
600 void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const Element* element, const RenderStyle& style)
605 ActiveAnimations* activeAnimations = element->activeAnimations();
606 const TransitionMap* activeTransitions = activeAnimations ? &activeAnimations->cssAnimations().m_transitions : 0;
609 // In release builds we avoid the cost of checking for new and interrupted transitions if the style recalc is due to animation.
610 const bool animationStyleRecalc = activeAnimations && activeAnimations->isAnimationStyleChange();
612 // 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.
613 const bool animationStyleRecalc = false;
616 BitArray<numCSSProperties> listedProperties;
617 bool anyTransitionHadAnimateAll = false;
618 const RenderObject* renderer = element->renderer();
619 if (!animationStyleRecalc && style.display() != NONE && renderer && renderer->style() && style.transitions()) {
620 const RenderStyle& oldStyle = *renderer->style();
622 for (size_t i = 0; i < style.transitions()->size(); ++i) {
623 const CSSAnimationData* anim = style.transitions()->animation(i);
624 CSSAnimationData::AnimationMode mode = anim->animationMode();
625 if (mode == CSSAnimationData::AnimateNone)
628 bool animateAll = mode == CSSAnimationData::AnimateAll;
629 ASSERT(animateAll || mode == CSSAnimationData::AnimateSingleProperty);
631 anyTransitionHadAnimateAll = true;
632 const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(anim->property());
633 // If not a shorthand we only execute one iteration of this loop, and refer to the property directly.
634 for (unsigned j = 0; !j || j < propertyList.length(); ++j) {
635 CSSPropertyID id = propertyList.length() ? propertyList.properties()[j] : anim->property();
638 if (CSSAnimations::isAnimatableProperty(id))
639 listedProperties.set(id);
644 // FIXME: We should transition if an !important property changes even when an animation is running,
645 // but this is a bit hard to do with the current applyMatchedProperties system.
646 if (!update->compositableValuesForAnimations().contains(id)
647 && (!activeAnimations || !activeAnimations->cssAnimations().m_previousCompositableValuesForAnimations.contains(id))) {
648 calculateTransitionUpdateForProperty(id, anim, oldStyle, style, activeTransitions, update, element);
654 if (activeTransitions) {
655 for (TransitionMap::const_iterator iter = activeTransitions->begin(); iter != activeTransitions->end(); ++iter) {
656 const TimedItem* timedItem = iter->value.transition;
657 CSSPropertyID id = iter->key;
658 if (timedItem->phase() == TimedItem::PhaseAfter || (!anyTransitionHadAnimateAll && !animationStyleRecalc && !listedProperties.get(id))) {
659 ASSERT(timedItem->phase() == TimedItem::PhaseAfter || !(activeAnimations && activeAnimations->isAnimationStyleChange()));
660 update->cancelTransition(id);
666 void CSSAnimations::cancel()
668 for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animations.end(); ++iter) {
669 const HashSet<RefPtr<Player> >& players = iter->value;
670 for (HashSet<RefPtr<Player> >::const_iterator animationsIter = players.begin(); animationsIter != players.end(); ++animationsIter)
671 (*animationsIter)->cancel();
674 for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transitions.end(); ++iter)
675 iter->value.transition->player()->cancel();
677 m_animations.clear();
678 m_transitions.clear();
679 m_pendingUpdate = nullptr;
682 void CSSAnimations::calculateAnimationCompositableValues(CSSAnimationUpdate* update, const Element* element)
684 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
685 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
687 if (update->newAnimations().isEmpty() && update->cancelledAnimationPlayers().isEmpty()) {
688 AnimationEffect::CompositableValueMap compositableValuesForAnimations(AnimationStack::compositableValues(animationStack, 0, 0, Animation::DefaultPriority));
689 update->adoptCompositableValuesForAnimations(compositableValuesForAnimations);
693 Vector<InertAnimation*> newAnimations;
694 for (size_t i = 0; i < update->newAnimations().size(); ++i) {
695 HashSet<RefPtr<InertAnimation> > animations = update->newAnimations()[i].animations;
696 for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = animations.begin(); animationsIter != animations.end(); ++animationsIter)
697 newAnimations.append(animationsIter->get());
699 AnimationEffect::CompositableValueMap compositableValuesForAnimations(AnimationStack::compositableValues(animationStack, &newAnimations, &update->cancelledAnimationPlayers(), Animation::DefaultPriority));
700 update->adoptCompositableValuesForAnimations(compositableValuesForAnimations);
703 void CSSAnimations::calculateTransitionCompositableValues(CSSAnimationUpdate* update, const Element* element)
705 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
706 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
708 AnimationEffect::CompositableValueMap compositableValuesForTransitions;
709 if (update->newTransitions().isEmpty() && update->cancelledTransitions().isEmpty()) {
710 compositableValuesForTransitions = AnimationStack::compositableValues(animationStack, 0, 0, Animation::TransitionPriority);
712 Vector<InertAnimation*> newTransitions;
713 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter)
714 newTransitions.append(iter->value.animation.get());
716 HashSet<const Player*> cancelledPlayers;
717 if (!update->cancelledTransitions().isEmpty()) {
718 ASSERT(activeAnimations);
719 const TransitionMap& transitionMap = activeAnimations->cssAnimations().m_transitions;
720 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
721 ASSERT(transitionMap.contains(*iter));
722 cancelledPlayers.add(transitionMap.get(*iter).transition->player());
726 compositableValuesForTransitions = AnimationStack::compositableValues(animationStack, &newTransitions, &cancelledPlayers, Animation::TransitionPriority);
729 // Properties being animated by animations don't get values from transitions applied.
730 if (!update->compositableValuesForAnimations().isEmpty() && !compositableValuesForTransitions.isEmpty()) {
731 for (AnimationEffect::CompositableValueMap::const_iterator iter = update->compositableValuesForAnimations().begin(); iter != update->compositableValuesForAnimations().end(); ++iter)
732 compositableValuesForTransitions.remove(iter->key);
734 update->adoptCompositableValuesForTransitions(compositableValuesForTransitions);
737 void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType listenerType, const AtomicString& eventName, double elapsedTime)
739 if (m_target->document().hasListenerType(listenerType))
740 m_target->document().timeline()->addEventToDispatch(m_target, WebKitAnimationEvent::create(eventName, m_name, elapsedTime));
743 void CSSAnimations::AnimationEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
745 // Events for a single document are queued and dispatched as a group at
746 // the end of DocumentTimeline::serviceAnimations.
747 // FIXME: Events which are queued outside of serviceAnimations should
748 // trigger a timer to dispatch when control is released.
749 const TimedItem::Phase currentPhase = timedItem->phase();
750 const double currentIteration = timedItem->currentIteration();
752 // Note that the elapsedTime is measured from when the animation starts playing.
753 if (!isFirstSample && previousPhase == TimedItem::PhaseActive && currentPhase == TimedItem::PhaseActive && previousIteration != currentIteration) {
754 ASSERT(!isNull(previousIteration));
755 ASSERT(!isNull(currentIteration));
756 // We fire only a single event for all iterations thast terminate
757 // between a single pair of samples. See http://crbug.com/275263. For
758 // compatibility with the existing implementation, this event uses
759 // the elapsedTime for the first iteration in question.
760 ASSERT(!std::isnan(timedItem->specifiedTiming().iterationDuration));
761 const double elapsedTime = timedItem->specifiedTiming().iterationDuration * (previousIteration + 1);
762 maybeDispatch(Document::ANIMATIONITERATION_LISTENER, EventTypeNames::animationiteration, elapsedTime);
765 if ((isFirstSample || previousPhase == TimedItem::PhaseBefore) && isLaterPhase(currentPhase, TimedItem::PhaseBefore)) {
766 ASSERT(timedItem->specifiedTiming().startDelay > 0 || isFirstSample);
767 // The spec states that the elapsed time should be
768 // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing
769 // implementation. See crbug.com/279611
770 maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animationstart, 0);
772 if ((isFirstSample || isEarlierPhase(previousPhase, TimedItem::PhaseAfter)) && currentPhase == TimedItem::PhaseAfter)
773 maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animationend, timedItem->activeDuration());
776 void CSSAnimations::TransitionEventDelegate::onEventCondition(const TimedItem* timedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration)
778 // Events for a single document are queued and dispatched as a group at
779 // the end of DocumentTimeline::serviceAnimations.
780 // FIXME: Events which are queued outside of serviceAnimations should
781 // trigger a timer to dispatch when control is released.
782 const TimedItem::Phase currentPhase = timedItem->phase();
783 if (currentPhase == TimedItem::PhaseAfter && (isFirstSample || previousPhase != currentPhase) && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
784 String propertyName = getPropertyNameString(m_property);
785 const Timing& timing = timedItem->specifiedTiming();
786 double elapsedTime = timing.iterationDuration;
787 const AtomicString& eventType = EventTypeNames::transitionend;
788 String pseudoElement = PseudoElement::pseudoElementNameForEvents(m_target->pseudoId());
789 m_target->document().transitionTimeline()->addEventToDispatch(m_target, TransitionEvent::create(eventType, propertyName, elapsedTime, pseudoElement));
794 bool CSSAnimations::isAnimatableProperty(CSSPropertyID property)
797 case CSSPropertyBackgroundColor:
798 case CSSPropertyBackgroundImage:
799 case CSSPropertyBackgroundPositionX:
800 case CSSPropertyBackgroundPositionY:
801 case CSSPropertyBackgroundSize:
802 case CSSPropertyBaselineShift:
803 case CSSPropertyBorderBottomColor:
804 case CSSPropertyBorderBottomLeftRadius:
805 case CSSPropertyBorderBottomRightRadius:
806 case CSSPropertyBorderBottomWidth:
807 case CSSPropertyBorderImageOutset:
808 case CSSPropertyBorderImageSlice:
809 case CSSPropertyBorderImageSource:
810 case CSSPropertyBorderImageWidth:
811 case CSSPropertyBorderLeftColor:
812 case CSSPropertyBorderLeftWidth:
813 case CSSPropertyBorderRightColor:
814 case CSSPropertyBorderRightWidth:
815 case CSSPropertyBorderTopColor:
816 case CSSPropertyBorderTopLeftRadius:
817 case CSSPropertyBorderTopRightRadius:
818 case CSSPropertyBorderTopWidth:
819 case CSSPropertyBottom:
820 case CSSPropertyBoxShadow:
821 case CSSPropertyClip:
822 case CSSPropertyColor:
823 case CSSPropertyFill:
824 case CSSPropertyFillOpacity:
825 case CSSPropertyFlexBasis:
826 case CSSPropertyFlexGrow:
827 case CSSPropertyFlexShrink:
828 case CSSPropertyFloodColor:
829 case CSSPropertyFloodOpacity:
830 case CSSPropertyFontSize:
831 case CSSPropertyHeight:
832 case CSSPropertyKerning:
833 case CSSPropertyLeft:
834 case CSSPropertyLetterSpacing:
835 case CSSPropertyLightingColor:
836 case CSSPropertyLineHeight:
837 case CSSPropertyListStyleImage:
838 case CSSPropertyMarginBottom:
839 case CSSPropertyMarginLeft:
840 case CSSPropertyMarginRight:
841 case CSSPropertyMarginTop:
842 case CSSPropertyMaxHeight:
843 case CSSPropertyMaxWidth:
844 case CSSPropertyMinHeight:
845 case CSSPropertyMinWidth:
846 case CSSPropertyObjectPosition:
847 case CSSPropertyOpacity:
848 case CSSPropertyOrphans:
849 case CSSPropertyOutlineColor:
850 case CSSPropertyOutlineOffset:
851 case CSSPropertyOutlineWidth:
852 case CSSPropertyPaddingBottom:
853 case CSSPropertyPaddingLeft:
854 case CSSPropertyPaddingRight:
855 case CSSPropertyPaddingTop:
856 case CSSPropertyRight:
857 case CSSPropertyStopColor:
858 case CSSPropertyStopOpacity:
859 case CSSPropertyStroke:
860 case CSSPropertyStrokeDasharray:
861 case CSSPropertyStrokeDashoffset:
862 case CSSPropertyStrokeMiterlimit:
863 case CSSPropertyStrokeOpacity:
864 case CSSPropertyStrokeWidth:
865 case CSSPropertyTextDecorationColor:
866 case CSSPropertyTextIndent:
867 case CSSPropertyTextShadow:
869 case CSSPropertyVisibility:
870 case CSSPropertyWebkitBackgroundSize:
871 case CSSPropertyWebkitBorderHorizontalSpacing:
872 case CSSPropertyWebkitBorderVerticalSpacing:
873 case CSSPropertyWebkitBoxShadow:
874 case CSSPropertyWebkitClipPath:
875 case CSSPropertyWebkitColumnCount:
876 case CSSPropertyWebkitColumnGap:
877 case CSSPropertyWebkitColumnRuleColor:
878 case CSSPropertyWebkitColumnRuleWidth:
879 case CSSPropertyWebkitColumnWidth:
880 case CSSPropertyWebkitFilter:
881 case CSSPropertyWebkitMaskBoxImageOutset:
882 case CSSPropertyWebkitMaskBoxImageSlice:
883 case CSSPropertyWebkitMaskBoxImageSource:
884 case CSSPropertyWebkitMaskBoxImageWidth:
885 case CSSPropertyWebkitMaskImage:
886 case CSSPropertyWebkitMaskPositionX:
887 case CSSPropertyWebkitMaskPositionY:
888 case CSSPropertyWebkitMaskSize:
889 case CSSPropertyWebkitPerspective:
890 case CSSPropertyWebkitPerspectiveOriginX:
891 case CSSPropertyWebkitPerspectiveOriginY:
892 case CSSPropertyShapeInside:
893 case CSSPropertyShapeOutside:
894 case CSSPropertyShapeMargin:
895 case CSSPropertyShapeImageThreshold:
896 case CSSPropertyWebkitTextStrokeColor:
897 case CSSPropertyWebkitTransform:
898 case CSSPropertyWebkitTransformOriginX:
899 case CSSPropertyWebkitTransformOriginY:
900 case CSSPropertyWebkitTransformOriginZ:
901 case CSSPropertyWidows:
902 case CSSPropertyWidth:
903 case CSSPropertyWordSpacing:
904 case CSSPropertyZIndex:
905 case CSSPropertyZoom:
912 const StylePropertyShorthand& CSSAnimations::animatableProperties()
914 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
915 DEFINE_STATIC_LOCAL(StylePropertyShorthand, propertyShorthand, ());
916 if (properties.isEmpty()) {
917 for (int i = firstCSSProperty; i < lastCSSProperty; ++i) {
918 CSSPropertyID id = convertToCSSPropertyID(i);
919 if (isAnimatableProperty(id))
920 properties.append(id);
922 propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, properties.begin(), properties.size());
924 return propertyShorthand;
927 } // namespace WebCore