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 "core/StylePropertyShorthand.h"
35 #include "core/animation/ActiveAnimations.h"
36 #include "core/animation/AnimationTimeline.h"
37 #include "core/animation/CompositorAnimations.h"
38 #include "core/animation/KeyframeEffectModel.h"
39 #include "core/animation/LegacyStyleInterpolation.h"
40 #include "core/animation/css/CSSAnimatableValueFactory.h"
41 #include "core/animation/css/CSSPropertyEquality.h"
42 #include "core/css/CSSKeyframeRule.h"
43 #include "core/css/CSSPropertyMetadata.h"
44 #include "core/css/resolver/CSSToStyleMap.h"
45 #include "core/css/resolver/StyleResolver.h"
46 #include "core/dom/Element.h"
47 #include "core/dom/PseudoElement.h"
48 #include "core/dom/StyleEngine.h"
49 #include "core/events/TransitionEvent.h"
50 #include "core/events/WebKitAnimationEvent.h"
51 #include "core/frame/UseCounter.h"
52 #include "core/rendering/RenderLayer.h"
53 #include "core/rendering/RenderObject.h"
54 #include "core/rendering/style/KeyframeList.h"
55 #include "platform/animation/TimingFunction.h"
56 #include "public/platform/Platform.h"
57 #include "wtf/BitArray.h"
58 #include "wtf/HashSet.h"
64 CSSPropertyID propertyForAnimation(CSSPropertyID property)
67 case CSSPropertyWebkitPerspective:
68 return CSSPropertyPerspective;
69 case CSSPropertyWebkitTransform:
70 return CSSPropertyTransform;
71 case CSSPropertyWebkitPerspectiveOriginX:
72 case CSSPropertyWebkitPerspectiveOriginY:
73 return CSSPropertyPerspectiveOrigin;
74 case CSSPropertyWebkitTransformOriginX:
75 case CSSPropertyWebkitTransformOriginY:
76 case CSSPropertyWebkitTransformOriginZ:
77 return CSSPropertyTransformOrigin;
84 static void resolveKeyframes(StyleResolver* resolver, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, const AtomicString& name, TimingFunction* defaultTimingFunction,
85 AnimatableValueKeyframeVector& keyframes)
87 // When the element is null, use its parent for scoping purposes.
88 const Element* elementForScoping = element ? element : &parentElement;
89 const StyleRuleKeyframes* keyframesRule = CSSAnimations::matchScopedKeyframesRule(resolver, elementForScoping, name.impl());
93 const WillBeHeapVector<RefPtrWillBeMember<StyleKeyframe> >& styleKeyframes = keyframesRule->keyframes();
94 if (styleKeyframes.isEmpty())
97 // Construct and populate the style for each keyframe
98 PropertySet specifiedPropertiesForUseCounter;
99 for (size_t i = 0; i < styleKeyframes.size(); ++i) {
100 const StyleKeyframe* styleKeyframe = styleKeyframes[i].get();
101 // It's OK to pass a null element here.
102 RefPtr<RenderStyle> keyframeStyle = resolver->styleForKeyframe(element, style, parentStyle, styleKeyframe, name);
103 RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframe = AnimatableValueKeyframe::create();
104 const Vector<double>& offsets = styleKeyframe->keys();
105 ASSERT(!offsets.isEmpty());
106 keyframe->setOffset(offsets[0]);
107 keyframe->setEasing(defaultTimingFunction);
108 const StylePropertySet& properties = styleKeyframe->properties();
109 for (unsigned j = 0; j < properties.propertyCount(); j++) {
110 specifiedPropertiesForUseCounter.add(properties.propertyAt(j).id());
111 CSSPropertyID property = propertyForAnimation(properties.propertyAt(j).id());
112 if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) {
113 CSSValue* value = properties.propertyAt(j).value();
114 RefPtr<TimingFunction> timingFunction;
115 if (value->isInheritedValue() && parentStyle->animations())
116 timingFunction = parentStyle->animations()->timingFunctionList()[0];
117 else if (value->isInheritedValue() || value->isInitialValue())
118 timingFunction = CSSTimingData::initialTimingFunction();
120 timingFunction = CSSToStyleMap::mapAnimationTimingFunction(toCSSValueList(value)->item(0));
121 keyframe->setEasing(timingFunction.release());
122 } else if (CSSPropertyMetadata::isAnimatableProperty(property)) {
123 keyframe->setPropertyValue(property, CSSAnimatableValueFactory::create(property, *keyframeStyle).get());
126 keyframes.append(keyframe);
127 // The last keyframe specified at a given offset is used.
128 for (size_t j = 1; j < offsets.size(); ++j) {
129 keyframes.append(toAnimatableValueKeyframe(keyframe->cloneWithOffset(offsets[j]).get()));
132 ASSERT(!keyframes.isEmpty());
134 for (PropertySet::const_iterator iter = specifiedPropertiesForUseCounter.begin(); iter != specifiedPropertiesForUseCounter.end(); ++iter) {
135 const CSSPropertyID property = *iter;
136 ASSERT(property != CSSPropertyInvalid);
137 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property));
140 // Remove duplicate keyframes. In CSS the last keyframe at a given offset takes priority.
141 std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffsets);
142 size_t targetIndex = 0;
143 for (size_t i = 1; i < keyframes.size(); i++) {
144 if (keyframes[i]->offset() != keyframes[targetIndex]->offset())
146 if (targetIndex != i)
147 keyframes[targetIndex] = keyframes[i];
149 keyframes.shrink(targetIndex + 1);
151 // Add 0% and 100% keyframes if absent.
152 RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = keyframes[0];
153 if (startKeyframe->offset()) {
154 startKeyframe = AnimatableValueKeyframe::create();
155 startKeyframe->setOffset(0);
156 startKeyframe->setEasing(defaultTimingFunction);
157 keyframes.prepend(startKeyframe);
159 RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = keyframes[keyframes.size() - 1];
160 if (endKeyframe->offset() != 1) {
161 endKeyframe = AnimatableValueKeyframe::create();
162 endKeyframe->setOffset(1);
163 endKeyframe->setEasing(defaultTimingFunction);
164 keyframes.append(endKeyframe);
166 ASSERT(keyframes.size() >= 2);
167 ASSERT(!keyframes.first()->offset());
168 ASSERT(keyframes.last()->offset() == 1);
170 // Snapshot current property values for 0% and 100% if missing.
171 PropertySet allProperties;
172 size_t numKeyframes = keyframes.size();
173 for (size_t i = 0; i < numKeyframes; i++) {
174 const PropertySet& keyframeProperties = keyframes[i]->properties();
175 for (PropertySet::const_iterator iter = keyframeProperties.begin(); iter != keyframeProperties.end(); ++iter)
176 allProperties.add(*iter);
178 const PropertySet& startKeyframeProperties = startKeyframe->properties();
179 const PropertySet& endKeyframeProperties = endKeyframe->properties();
180 bool missingStartValues = startKeyframeProperties.size() < allProperties.size();
181 bool missingEndValues = endKeyframeProperties.size() < allProperties.size();
182 if (missingStartValues || missingEndValues) {
183 for (PropertySet::const_iterator iter = allProperties.begin(); iter != allProperties.end(); ++iter) {
184 const CSSPropertyID property = *iter;
185 bool startNeedsValue = missingStartValues && !startKeyframeProperties.contains(property);
186 bool endNeedsValue = missingEndValues && !endKeyframeProperties.contains(property);
187 if (!startNeedsValue && !endNeedsValue)
189 RefPtrWillBeRawPtr<AnimatableValue> snapshotValue = CSSAnimatableValueFactory::create(property, style);
191 startKeyframe->setPropertyValue(property, snapshotValue.get());
193 endKeyframe->setPropertyValue(property, snapshotValue.get());
196 ASSERT(startKeyframe->properties().size() == allProperties.size());
197 ASSERT(endKeyframe->properties().size() == allProperties.size());
202 const StyleRuleKeyframes* CSSAnimations::matchScopedKeyframesRule(StyleResolver* resolver, const Element* element, const StringImpl* animationName)
204 // FIXME: This is all implementation detail of style resolver, CSSAnimations shouldn't be reaching into any of it.
205 if (resolver->document().styleEngine()->hasOnlyScopedResolverForDocument())
206 return element->document().scopedStyleResolver()->keyframeStylesForAnimation(animationName);
208 WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8> stack;
209 resolver->styleTreeResolveScopedKeyframesRules(element, stack);
213 for (size_t i = 0; i < stack.size(); ++i) {
214 if (const StyleRuleKeyframes* keyframesRule = stack.at(i)->keyframeStylesForAnimation(animationName))
215 return keyframesRule;
220 CSSAnimations::CSSAnimations()
224 PassOwnPtrWillBeRawPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
226 OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = adoptPtrWillBeNoop(new CSSAnimationUpdate());
227 calculateAnimationUpdate(update.get(), element, parentElement, style, parentStyle, resolver);
228 calculateAnimationActiveInterpolations(update.get(), element, parentElement.document().timeline().currentTimeInternal());
229 calculateTransitionUpdate(update.get(), element, style);
230 calculateTransitionActiveInterpolations(update.get(), element, parentElement.document().timeline().currentTimeInternal());
231 return update->isEmpty() ? nullptr : update.release();
234 void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element* element, const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver)
236 const ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
239 // If we're in an animation style change, no animations can have started, been cancelled or changed play state.
240 // When ASSERT is enabled, we verify this optimization.
241 if (activeAnimations && activeAnimations->isAnimationStyleChange())
245 const CSSAnimationData* animationData = style.animations();
246 const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : 0;
248 HashSet<AtomicString> inactive;
250 for (AnimationMap::const_iterator iter = cssAnimations->m_animations.begin(); iter != cssAnimations->m_animations.end(); ++iter)
251 inactive.add(iter->key);
253 if (style.display() != NONE) {
254 for (size_t i = 0; animationData && i < animationData->nameList().size(); ++i) {
255 AtomicString animationName(animationData->nameList()[i]);
256 if (animationName == CSSAnimationData::initialName())
259 bool isPaused = CSSTimingData::getRepeated(animationData->playStateList(), i) == AnimPlayStatePaused;
261 // Keyframes and animation properties are snapshotted when the
262 // animation starts, so we don't need to track changes to these,
263 // with the exception of play-state.
265 AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName));
266 if (existing != cssAnimations->m_animations.end()) {
267 inactive.remove(animationName);
268 AnimationPlayer* player = existing->value.get();
269 if (isPaused != player->paused()) {
270 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
271 update->toggleAnimationPaused(animationName);
277 Timing timing = animationData->convertToTiming(i);
278 RefPtr<TimingFunction> keyframeTimingFunction = timing.timingFunction;
279 timing.timingFunction = Timing::defaults().timingFunction;
280 AnimatableValueKeyframeVector resolvedKeyframes;
281 resolveKeyframes(resolver, element, parentElement, style, parentStyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes);
282 if (!resolvedKeyframes.isEmpty()) {
283 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
284 update->startAnimation(animationName, InertAnimation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused));
289 ASSERT(inactive.isEmpty() || cssAnimations);
290 for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter != inactive.end(); ++iter) {
291 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange());
292 update->cancelAnimation(*iter, *cssAnimations->m_animations.get(*iter));
296 void CSSAnimations::maybeApplyPendingUpdate(Element* element)
298 if (!m_pendingUpdate) {
299 m_previousActiveInterpolationsForAnimations.clear();
303 OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release();
305 m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations());
307 // FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here
308 // since we call this from recalc style.
309 // https://code.google.com/p/chromium/issues/detail?id=339847
310 DisableCompositingQueryAsserts disabler;
312 for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationNames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) {
313 RefPtr<AnimationPlayer> player = m_animations.take(*iter);
315 player->update(TimingUpdateOnDemand);
318 for (Vector<AtomicString>::const_iterator iter = update->animationsWithPauseToggled().begin(); iter != update->animationsWithPauseToggled().end(); ++iter) {
319 AnimationPlayer* player = m_animations.get(*iter);
320 if (player->paused())
324 if (player->outdated())
325 player->update(TimingUpdateOnDemand);
328 for (WillBeHeapVector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) {
329 const InertAnimation* inertAnimation = iter->animation.get();
330 OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillBeNoop(new AnimationEventDelegate(element, iter->name));
331 RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release());
332 RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(animation.get());
333 element->document().compositorPendingAnimations().add(player.get());
334 if (inertAnimation->paused())
336 player->update(TimingUpdateOnDemand);
337 m_animations.set(iter->name, player.get());
340 // Transitions that are run on the compositor only update main-thread state
341 // lazily. However, we need the new state to know what the from state shoud
342 // be when transitions are retargeted. Instead of triggering complete style
343 // recalculation, we find these cases by searching for new transitions that
344 // have matching cancelled animation property IDs on the compositor.
345 WillBeHeapHashMap<CSSPropertyID, std::pair<RefPtrWillBeMember<Animation>, double> > retargetedCompositorTransitions;
346 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
347 CSSPropertyID id = *iter;
348 ASSERT(m_transitions.contains(id));
350 RefPtrWillBeRawPtr<AnimationPlayer> player = m_transitions.take(id).player;
351 Animation* animation = toAnimation(player->source());
352 if (animation->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end())
353 retargetedCompositorTransitions.add(id, std::pair<RefPtrWillBeMember<Animation>, double>(animation, player->startTimeInternal()));
355 player->update(TimingUpdateOnDemand);
358 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter) {
359 const CSSAnimationUpdate::NewTransition& newTransition = iter->value;
361 RunningTransition runningTransition;
362 runningTransition.from = newTransition.from;
363 runningTransition.to = newTransition.to;
365 CSSPropertyID id = newTransition.id;
366 InertAnimation* inertAnimation = newTransition.animation.get();
367 OwnPtrWillBeRawPtr<TransitionEventDelegate> eventDelegate = adoptPtrWillBeNoop(new TransitionEventDelegate(element, newTransition.eventId));
369 RefPtrWillBeRawPtr<AnimationEffect> effect = inertAnimation->effect();
371 if (retargetedCompositorTransitions.contains(id)) {
372 const std::pair<RefPtrWillBeMember<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id);
373 RefPtrWillBeRawPtr<Animation> oldAnimation = oldTransition.first;
374 double oldStartTime = oldTransition.second;
375 double inheritedTime = isNull(oldStartTime) ? 0 : element->document().timeline().currentTimeInternal() - oldStartTime;
377 AnimatableValueKeyframeEffectModel* oldEffect = toAnimatableValueKeyframeEffectModel(inertAnimation->effect());
378 const KeyframeVector& frames = oldEffect->getFrames();
380 AnimatableValueKeyframeVector newFrames;
381 newFrames.append(toAnimatableValueKeyframe(frames[0]->clone().get()));
382 newFrames.append(toAnimatableValueKeyframe(frames[1]->clone().get()));
384 newFrames[0]->clearPropertyValue(id);
385 RefPtrWillBeRawPtr<InertAnimation> inertAnimationForSampling = InertAnimation::create(oldAnimation->effect(), oldAnimation->specifiedTiming(), false);
386 OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > sample = inertAnimationForSampling->sample(inheritedTime);
387 ASSERT(sample->size() == 1);
388 newFrames[0]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue());
390 effect = AnimatableValueKeyframeEffectModel::create(newFrames);
393 RefPtrWillBeRawPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release());
394 RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(transition.get());
395 element->document().compositorPendingAnimations().add(player.get());
396 player->update(TimingUpdateOnDemand);
397 runningTransition.player = player;
398 m_transitions.set(id, runningTransition);
399 ASSERT(id != CSSPropertyInvalid);
400 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id));
404 void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, CSSPropertyID eventId, const CSSTransitionData& transitionData, size_t transitionIndex, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* activeTransitions, CSSAnimationUpdate* update, const Element* element)
406 RefPtrWillBeRawPtr<AnimatableValue> to = nullptr;
407 if (activeTransitions) {
408 TransitionMap::const_iterator activeTransitionIter = activeTransitions->find(id);
409 if (activeTransitionIter != activeTransitions->end()) {
410 to = CSSAnimatableValueFactory::create(id, style);
411 const AnimatableValue* activeTo = activeTransitionIter->value.to;
412 if (to->equals(activeTo))
414 update->cancelTransition(id);
415 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
419 if (CSSPropertyEquality::propertiesEqual(id, oldStyle, style))
422 to = CSSAnimatableValueFactory::create(id, style);
424 RefPtrWillBeRawPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyle);
425 // If we have multiple transitions on the same property, we will use the
426 // last one since we iterate over them in order.
427 if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get()))
430 Timing timing = transitionData.convertToTiming(transitionIndex);
431 if (timing.startDelay + timing.iterationDuration <= 0)
434 AnimatableValueKeyframeVector keyframes;
436 RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = AnimatableValueKeyframe::create();
437 startKeyframe->setPropertyValue(id, from.get());
438 startKeyframe->setOffset(0);
439 startKeyframe->setEasing(timing.timingFunction.release());
440 timing.timingFunction = LinearTimingFunction::shared();
441 keyframes.append(startKeyframe);
443 RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = AnimatableValueKeyframe::create();
444 endKeyframe->setPropertyValue(id, to.get());
445 endKeyframe->setOffset(1);
446 keyframes.append(endKeyframe);
448 RefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeEffectModel::create(keyframes);
449 update->startTransition(id, eventId, from.get(), to.get(), InertAnimation::create(effect, timing, false));
450 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnimationStyleChange());
453 void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const Element* element, const RenderStyle& style)
458 ActiveAnimations* activeAnimations = element->activeAnimations();
459 const TransitionMap* activeTransitions = activeAnimations ? &activeAnimations->cssAnimations().m_transitions : 0;
460 const CSSTransitionData* transitionData = style.transitions();
463 // 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.
464 const bool animationStyleRecalc = false;
466 // In release builds we avoid the cost of checking for new and interrupted transitions if the style recalc is due to animation.
467 const bool animationStyleRecalc = activeAnimations && activeAnimations->isAnimationStyleChange();
470 BitArray<numCSSProperties> listedProperties;
471 bool anyTransitionHadTransitionAll = false;
472 const RenderObject* renderer = element->renderer();
473 if (!animationStyleRecalc && style.display() != NONE && renderer && renderer->style() && transitionData) {
474 const RenderStyle& oldStyle = *renderer->style();
476 for (size_t i = 0; i < transitionData->propertyList().size(); ++i) {
477 const CSSTransitionData::TransitionProperty& transitionProperty = transitionData->propertyList()[i];
478 CSSTransitionData::TransitionPropertyType mode = transitionProperty.propertyType;
479 CSSPropertyID property = transitionProperty.propertyId;
480 if (mode == CSSTransitionData::TransitionNone || mode == CSSTransitionData::TransitionUnknown)
483 bool animateAll = mode == CSSTransitionData::TransitionAll;
484 ASSERT(animateAll || mode == CSSTransitionData::TransitionSingleProperty);
486 anyTransitionHadTransitionAll = true;
487 const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations::animatableProperties() : shorthandForProperty(property);
488 // If not a shorthand we only execute one iteration of this loop, and refer to the property directly.
489 for (unsigned j = 0; !j || j < propertyList.length(); ++j) {
490 CSSPropertyID id = propertyList.length() ? propertyList.properties()[j] : property;
491 CSSPropertyID eventId = id;
494 id = propertyForAnimation(id);
495 if (CSSPropertyMetadata::isAnimatableProperty(id))
496 listedProperties.set(id);
501 // FIXME: We should transition if an !important property changes even when an animation is running,
502 // but this is a bit hard to do with the current applyMatchedProperties system.
503 if (!update->activeInterpolationsForAnimations().contains(id)
504 && (!activeAnimations || !activeAnimations->cssAnimations().m_previousActiveInterpolationsForAnimations.contains(id))) {
505 calculateTransitionUpdateForProperty(id, eventId, *transitionData, i, oldStyle, style, activeTransitions, update, element);
511 if (activeTransitions) {
512 for (TransitionMap::const_iterator iter = activeTransitions->begin(); iter != activeTransitions->end(); ++iter) {
513 const AnimationPlayer& player = *iter->value.player;
514 CSSPropertyID id = iter->key;
515 if (player.finishedInternal() || (!anyTransitionHadTransitionAll && !animationStyleRecalc && !listedProperties.get(id))) {
516 // TODO: Figure out why this fails on Chrome OS login page. crbug.com/365507
517 // ASSERT(player.finishedInternal() || !(activeAnimations && activeAnimations->isAnimationStyleChange()));
518 update->cancelTransition(id);
524 void CSSAnimations::cancel()
526 for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animations.end(); ++iter) {
527 iter->value->cancel();
528 iter->value->update(TimingUpdateOnDemand);
531 for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transitions.end(); ++iter) {
532 iter->value.player->cancel();
533 iter->value.player->update(TimingUpdateOnDemand);
536 m_animations.clear();
537 m_transitions.clear();
538 m_pendingUpdate = nullptr;
541 void CSSAnimations::calculateAnimationActiveInterpolations(CSSAnimationUpdate* update, const Element* element, double timelineCurrentTime)
543 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
544 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
546 if (update->newAnimations().isEmpty() && update->cancelledAnimationAnimationPlayers().isEmpty()) {
547 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::DefaultPriority, timelineCurrentTime));
548 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations);
552 WillBeHeapVector<RawPtrWillBeMember<InertAnimation> > newAnimations;
553 for (size_t i = 0; i < update->newAnimations().size(); ++i) {
554 newAnimations.append(update->newAnimations()[i].animation.get());
556 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, &newAnimations, &update->cancelledAnimationAnimationPlayers(), Animation::DefaultPriority, timelineCurrentTime));
557 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimations);
560 void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate* update, const Element* element, double timelineCurrentTime)
562 ActiveAnimations* activeAnimations = element ? element->activeAnimations() : 0;
563 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defaultStack() : 0;
565 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> > activeInterpolationsForTransitions;
566 if (update->newTransitions().isEmpty() && update->cancelledTransitions().isEmpty()) {
567 activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::TransitionPriority, timelineCurrentTime);
569 WillBeHeapVector<RawPtrWillBeMember<InertAnimation> > newTransitions;
570 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter)
571 newTransitions.append(iter->value.animation.get());
573 WillBeHeapHashSet<RawPtrWillBeMember<const AnimationPlayer> > cancelledAnimationPlayers;
574 if (!update->cancelledTransitions().isEmpty()) {
575 ASSERT(activeAnimations);
576 const TransitionMap& transitionMap = activeAnimations->cssAnimations().m_transitions;
577 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) {
578 ASSERT(transitionMap.contains(*iter));
579 cancelledAnimationPlayers.add(transitionMap.get(*iter).player.get());
583 activeInterpolationsForTransitions = AnimationStack::activeInterpolations(animationStack, &newTransitions, &cancelledAnimationPlayers, Animation::TransitionPriority, timelineCurrentTime);
586 // Properties being animated by animations don't get values from transitions applied.
587 if (!update->activeInterpolationsForAnimations().isEmpty() && !activeInterpolationsForTransitions.isEmpty()) {
588 for (WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation> >::const_iterator iter = update->activeInterpolationsForAnimations().begin(); iter != update->activeInterpolationsForAnimations().end(); ++iter)
589 activeInterpolationsForTransitions.remove(iter->key);
591 update->adoptActiveInterpolationsForTransitions(activeInterpolationsForTransitions);
594 void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType listenerType, const AtomicString& eventName, double elapsedTime)
596 if (m_target->document().hasListenerType(listenerType)) {
597 RefPtrWillBeRawPtr<WebKitAnimationEvent> event = WebKitAnimationEvent::create(eventName, m_name, elapsedTime);
598 event->setTarget(m_target);
599 m_target->document().enqueueAnimationFrameEvent(event);
603 void CSSAnimations::AnimationEventDelegate::onEventCondition(const AnimationNode* animationNode)
605 const AnimationNode::Phase currentPhase = animationNode->phase();
606 const double currentIteration = animationNode->currentIteration();
608 if (m_previousPhase != currentPhase
609 && (currentPhase == AnimationNode::PhaseActive || currentPhase == AnimationNode::PhaseAfter)
610 && (m_previousPhase == AnimationNode::PhaseNone || m_previousPhase == AnimationNode::PhaseBefore)) {
611 // The spec states that the elapsed time should be
612 // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing
613 // implementation. See crbug.com/279611
614 maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animationstart, 0);
617 if (currentPhase == AnimationNode::PhaseActive && m_previousPhase == currentPhase && m_previousIteration != currentIteration) {
618 // We fire only a single event for all iterations thast terminate
619 // between a single pair of samples. See http://crbug.com/275263. For
620 // compatibility with the existing implementation, this event uses
621 // the elapsedTime for the first iteration in question.
622 ASSERT(!std::isnan(animationNode->specifiedTiming().iterationDuration));
623 const double elapsedTime = animationNode->specifiedTiming().iterationDuration * (m_previousIteration + 1);
624 maybeDispatch(Document::ANIMATIONITERATION_LISTENER, EventTypeNames::animationiteration, elapsedTime);
627 if (currentPhase == AnimationNode::PhaseAfter && m_previousPhase != AnimationNode::PhaseAfter)
628 maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animationend, animationNode->activeDurationInternal());
630 m_previousPhase = currentPhase;
631 m_previousIteration = currentIteration;
634 void CSSAnimations::AnimationEventDelegate::trace(Visitor* visitor)
636 visitor->trace(m_target);
637 AnimationNode::EventDelegate::trace(visitor);
640 void CSSAnimations::TransitionEventDelegate::onEventCondition(const AnimationNode* animationNode)
642 const AnimationNode::Phase currentPhase = animationNode->phase();
643 if (currentPhase == AnimationNode::PhaseAfter && currentPhase != m_previousPhase && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER)) {
644 String propertyName = getPropertyNameString(m_property);
645 const Timing& timing = animationNode->specifiedTiming();
646 double elapsedTime = timing.iterationDuration;
647 const AtomicString& eventType = EventTypeNames::transitionend;
648 String pseudoElement = PseudoElement::pseudoElementNameForEvents(m_target->pseudoId());
649 RefPtrWillBeRawPtr<TransitionEvent> event = TransitionEvent::create(eventType, propertyName, elapsedTime, pseudoElement);
650 event->setTarget(m_target);
651 m_target->document().enqueueAnimationFrameEvent(event);
654 m_previousPhase = currentPhase;
657 void CSSAnimations::TransitionEventDelegate::trace(Visitor* visitor)
659 visitor->trace(m_target);
660 AnimationNode::EventDelegate::trace(visitor);
663 const StylePropertyShorthand& CSSAnimations::animatableProperties()
665 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
666 DEFINE_STATIC_LOCAL(StylePropertyShorthand, propertyShorthand, ());
667 if (properties.isEmpty()) {
668 for (int i = firstCSSProperty; i < lastCSSProperty; ++i) {
669 CSSPropertyID id = convertToCSSPropertyID(i);
670 if (CSSPropertyMetadata::isAnimatableProperty(id))
671 properties.append(id);
673 propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, properties.begin(), properties.size());
675 return propertyShorthand;
678 // Animation properties are not allowed to be affected by Web Animations.
679 // http://dev.w3.org/fxtf/web-animations/#not-animatable
680 bool CSSAnimations::isAllowedAnimation(CSSPropertyID property)
683 case CSSPropertyAnimation:
684 case CSSPropertyAnimationDelay:
685 case CSSPropertyAnimationDirection:
686 case CSSPropertyAnimationDuration:
687 case CSSPropertyAnimationFillMode:
688 case CSSPropertyAnimationIterationCount:
689 case CSSPropertyAnimationName:
690 case CSSPropertyAnimationPlayState:
691 case CSSPropertyAnimationTimingFunction:
692 case CSSPropertyDisplay:
693 case CSSPropertyTransition:
694 case CSSPropertyTransitionDelay:
695 case CSSPropertyTransitionDuration:
696 case CSSPropertyTransitionProperty:
697 case CSSPropertyTransitionTimingFunction:
698 case CSSPropertyWebkitAnimation:
699 case CSSPropertyWebkitAnimationDelay:
700 case CSSPropertyWebkitAnimationDirection:
701 case CSSPropertyWebkitAnimationDuration:
702 case CSSPropertyWebkitAnimationFillMode:
703 case CSSPropertyWebkitAnimationIterationCount:
704 case CSSPropertyWebkitAnimationName:
705 case CSSPropertyWebkitAnimationPlayState:
706 case CSSPropertyWebkitAnimationTimingFunction:
707 case CSSPropertyWebkitTransition:
708 case CSSPropertyWebkitTransitionDelay:
709 case CSSPropertyWebkitTransitionDuration:
710 case CSSPropertyWebkitTransitionProperty:
711 case CSSPropertyWebkitTransitionTimingFunction:
718 void CSSAnimations::trace(Visitor* visitor)
721 visitor->trace(m_transitions);
722 visitor->trace(m_pendingUpdate);
723 visitor->trace(m_animations);
724 visitor->trace(m_previousActiveInterpolationsForAnimations);
728 void CSSAnimationUpdate::trace(Visitor* visitor)
731 visitor->trace(m_newTransitions);
732 visitor->trace(m_activeInterpolationsForAnimations);
733 visitor->trace(m_activeInterpolationsForTransitions);
734 visitor->trace(m_newAnimations);
735 visitor->trace(m_cancelledAnimationPlayers);