Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(965)

Side by Side Diff: Source/core/animation/css/CSSAnimations.cpp

Issue 948583003: Use StringKeyframes to make CSS Animations responsive to style changes (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Rebase Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/core/animation/KeyframeEffectModel.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 case CSSPropertyWebkitTransformOriginY: 79 case CSSPropertyWebkitTransformOriginY:
80 case CSSPropertyWebkitTransformOriginZ: 80 case CSSPropertyWebkitTransformOriginZ:
81 case CSSPropertyWebkitTransformOrigin: 81 case CSSPropertyWebkitTransformOrigin:
82 return CSSPropertyTransformOrigin; 82 return CSSPropertyTransformOrigin;
83 default: 83 default:
84 break; 84 break;
85 } 85 }
86 return property; 86 return property;
87 } 87 }
88 88
89 static PassRefPtrWillBeRawPtr<AnimatableValueKeyframeEffectModel> createKeyframe Effect(StyleResolver* resolver, const Element* animatingElement, Element& elemen t, const LayoutStyle& style, LayoutStyle* parentStyle, 89 static PassRefPtrWillBeRawPtr<StringKeyframeEffectModel> createKeyframeEffect(St yleResolver* resolver, const Element* animatingElement, Element& element, const LayoutStyle* style, const LayoutStyle* parentStyle, const AtomicString& name, Ti mingFunction* defaultTimingFunction)
90 const AtomicString& name, TimingFunction* defaultTimingFunction)
91 { 90 {
92 // When the animating element is null, use its parent for scoping purposes. 91 // When the animating element is null, use its parent for scoping purposes.
93 const Element* elementForScoping = animatingElement ? animatingElement : &el ement; 92 const Element* elementForScoping = animatingElement ? animatingElement : &el ement;
94 const StyleRuleKeyframes* keyframesRule = resolver->findKeyframesRule(elemen tForScoping, name); 93 const StyleRuleKeyframes* keyframesRule = resolver->findKeyframesRule(elemen tForScoping, name);
95 ASSERT(keyframesRule); 94 ASSERT(keyframesRule);
96 95
97 AnimatableValueKeyframeVector keyframes; 96 StringKeyframeVector keyframes;
98 const WillBeHeapVector<RefPtrWillBeMember<StyleRuleKeyframe>>& styleKeyframe s = keyframesRule->keyframes(); 97 const WillBeHeapVector<RefPtrWillBeMember<StyleRuleKeyframe>>& styleKeyframe s = keyframesRule->keyframes();
99 98
100 // Construct and populate the style for each keyframe 99 // Construct and populate the style for each keyframe
101 PropertySet specifiedPropertiesForUseCounter; 100 PropertySet specifiedPropertiesForUseCounter;
102 for (size_t i = 0; i < styleKeyframes.size(); ++i) { 101 for (size_t i = 0; i < styleKeyframes.size(); ++i) {
103 const StyleRuleKeyframe* styleKeyframe = styleKeyframes[i].get(); 102 const StyleRuleKeyframe* styleKeyframe = styleKeyframes[i].get();
104 RefPtr<LayoutStyle> keyframeStyle = resolver->styleForKeyframe(element, style, parentStyle, styleKeyframe, name); 103 RefPtrWillBeRawPtr<StringKeyframe> keyframe = StringKeyframe::create();
105 RefPtrWillBeRawPtr<AnimatableValueKeyframe> keyframe = AnimatableValueKe yframe::create();
106 const Vector<double>& offsets = styleKeyframe->keys(); 104 const Vector<double>& offsets = styleKeyframe->keys();
107 ASSERT(!offsets.isEmpty()); 105 ASSERT(!offsets.isEmpty());
108 keyframe->setOffset(offsets[0]); 106 keyframe->setOffset(offsets[0]);
109 keyframe->setEasing(defaultTimingFunction); 107 keyframe->setEasing(defaultTimingFunction);
110 const StylePropertySet& properties = styleKeyframe->properties(); 108 const StylePropertySet& properties = styleKeyframe->properties();
111 for (unsigned j = 0; j < properties.propertyCount(); j++) { 109 for (unsigned j = 0; j < properties.propertyCount(); j++) {
112 specifiedPropertiesForUseCounter.add(properties.propertyAt(j).id()); 110 specifiedPropertiesForUseCounter.add(properties.propertyAt(j).id());
113 CSSPropertyID property = propertyForAnimation(properties.propertyAt( j).id()); 111 CSSPropertyID property = propertyForAnimation(properties.propertyAt( j).id());
114 if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) { 112 if (property == CSSPropertyWebkitAnimationTimingFunction || property == CSSPropertyAnimationTimingFunction) {
115 CSSValue* value = properties.propertyAt(j).value(); 113 CSSValue* value = properties.propertyAt(j).value();
116 RefPtr<TimingFunction> timingFunction; 114 RefPtr<TimingFunction> timingFunction;
117 if (value->isInheritedValue() && parentStyle->animations()) { 115 if (value->isInheritedValue() && parentStyle->animations()) {
118 timingFunction = parentStyle->animations()->timingFunctionLi st()[0]; 116 timingFunction = parentStyle->animations()->timingFunctionLi st()[0];
119 } else if (value->isValueList()) { 117 } else if (value->isValueList()) {
120 timingFunction = CSSToStyleMap::mapAnimationTimingFunction(t oCSSValueList(value)->item(0)); 118 timingFunction = CSSToStyleMap::mapAnimationTimingFunction(t oCSSValueList(value)->item(0));
121 } else { 119 } else {
122 ASSERT(value->isInheritedValue() || value->isInitialValue() || value->isUnsetValue()); 120 ASSERT(value->isInheritedValue() || value->isInitialValue() || value->isUnsetValue());
123 timingFunction = CSSTimingData::initialTimingFunction(); 121 timingFunction = CSSTimingData::initialTimingFunction();
124 } 122 }
125 keyframe->setEasing(timingFunction.release()); 123 keyframe->setEasing(timingFunction.release());
126 } else if (CSSPropertyMetadata::isAnimatableProperty(property)) { 124 } else if (CSSPropertyMetadata::isAnimatableProperty(property)) {
127 keyframe->setPropertyValue(property, CSSAnimatableValueFactory:: create(property, *keyframeStyle).get()); 125 keyframe->setPropertyValue(property, properties.propertyAt(j).va lue());
128 } 126 }
129 } 127 }
130 keyframes.append(keyframe); 128 keyframes.append(keyframe);
131 // The last keyframe specified at a given offset is used. 129 // The last keyframe specified at a given offset is used.
132 for (size_t j = 1; j < offsets.size(); ++j) { 130 for (size_t j = 1; j < offsets.size(); ++j) {
133 keyframes.append(toAnimatableValueKeyframe(keyframe->cloneWithOffset (offsets[j]).get())); 131 keyframes.append(toStringKeyframe(keyframe->cloneWithOffset(offsets[ j]).get()));
134 } 132 }
135 } 133 }
136 134
137 for (CSSPropertyID property : specifiedPropertiesForUseCounter) { 135 for (CSSPropertyID property : specifiedPropertiesForUseCounter) {
138 ASSERT(property != CSSPropertyInvalid); 136 ASSERT(property != CSSPropertyInvalid);
139 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProper ties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property)); 137 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProper ties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(property));
140 } 138 }
141 139
142 // Merge duplicate keyframes. 140 // Merge duplicate keyframes.
143 std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffset s); 141 std::stable_sort(keyframes.begin(), keyframes.end(), Keyframe::compareOffset s);
144 size_t targetIndex = 0; 142 size_t targetIndex = 0;
145 for (size_t i = 1; i < keyframes.size(); i++) { 143 for (size_t i = 1; i < keyframes.size(); i++) {
146 if (keyframes[i]->offset() == keyframes[targetIndex]->offset()) { 144 if (keyframes[i]->offset() == keyframes[targetIndex]->offset()) {
147 for (CSSPropertyID property : keyframes[i]->properties()) 145 for (CSSPropertyID property : keyframes[i]->properties())
148 keyframes[targetIndex]->setPropertyValue(property, keyframes[i]- >propertyValue(property)); 146 keyframes[targetIndex]->setPropertyValue(property, keyframes[i]- >propertyValue(property));
149 } else { 147 } else {
150 targetIndex++; 148 targetIndex++;
151 keyframes[targetIndex] = keyframes[i]; 149 keyframes[targetIndex] = keyframes[i];
152 } 150 }
153 } 151 }
154 if (!keyframes.isEmpty()) 152 if (!keyframes.isEmpty())
155 keyframes.shrink(targetIndex + 1); 153 keyframes.shrink(targetIndex + 1);
156 154
157 // Add 0% and 100% keyframes if absent. 155 // Add 0% and 100% keyframes if absent.
158 RefPtrWillBeRawPtr<AnimatableValueKeyframe> startKeyframe = keyframes.isEmpt y() ? nullptr : keyframes[0]; 156 RefPtrWillBeRawPtr<StringKeyframe> startKeyframe = keyframes.isEmpty() ? nul lptr : keyframes[0];
159 if (!startKeyframe || keyframes[0]->offset() != 0) { 157 if (!startKeyframe || keyframes[0]->offset() != 0) {
160 startKeyframe = AnimatableValueKeyframe::create(); 158 startKeyframe = StringKeyframe::create();
161 startKeyframe->setOffset(0); 159 startKeyframe->setOffset(0);
162 startKeyframe->setEasing(defaultTimingFunction); 160 startKeyframe->setEasing(defaultTimingFunction);
163 keyframes.prepend(startKeyframe); 161 keyframes.prepend(startKeyframe);
164 } 162 }
165 RefPtrWillBeRawPtr<AnimatableValueKeyframe> endKeyframe = keyframes[keyframe s.size() - 1]; 163 RefPtrWillBeRawPtr<StringKeyframe> endKeyframe = keyframes[keyframes.size() - 1];
166 if (endKeyframe->offset() != 1) { 164 if (endKeyframe->offset() != 1) {
167 endKeyframe = AnimatableValueKeyframe::create(); 165 endKeyframe = StringKeyframe::create();
168 endKeyframe->setOffset(1); 166 endKeyframe->setOffset(1);
169 endKeyframe->setEasing(defaultTimingFunction); 167 endKeyframe->setEasing(defaultTimingFunction);
170 keyframes.append(endKeyframe); 168 keyframes.append(endKeyframe);
171 } 169 }
172 ASSERT(keyframes.size() >= 2); 170 ASSERT(keyframes.size() >= 2);
173 ASSERT(!keyframes.first()->offset()); 171 ASSERT(!keyframes.first()->offset());
174 ASSERT(keyframes.last()->offset() == 1); 172 ASSERT(keyframes.last()->offset() == 1);
175 173
176 // Snapshot current property values for 0% and 100% if missing. 174 // FIXME: This is only used for use counting neutral keyframes running on th e compositor.
177 PropertySet allProperties; 175 PropertySet allProperties;
178 for (const auto& keyframe : keyframes) { 176 for (const auto& keyframe : keyframes) {
179 for (CSSPropertyID property : keyframe->properties()) 177 for (CSSPropertyID property : keyframe->properties())
180 allProperties.add(property); 178 allProperties.add(property);
181 } 179 }
182 const PropertySet& startKeyframeProperties = startKeyframe->properties(); 180 const PropertySet& startKeyframeProperties = startKeyframe->properties();
183 const PropertySet& endKeyframeProperties = endKeyframe->properties(); 181 const PropertySet& endKeyframeProperties = endKeyframe->properties();
184 bool missingStartValues = startKeyframeProperties.size() < allProperties.siz e(); 182 bool missingStartValues = startKeyframeProperties.size() < allProperties.siz e();
185 bool missingEndValues = endKeyframeProperties.size() < allProperties.size(); 183 bool missingEndValues = endKeyframeProperties.size() < allProperties.size();
186 if (missingStartValues || missingEndValues) { 184 if (missingStartValues || missingEndValues) {
187 for (CSSPropertyID property : allProperties) { 185 for (CSSPropertyID property : allProperties) {
188 bool startNeedsValue = missingStartValues && !startKeyframePropertie s.contains(property); 186 bool startNeedsValue = missingStartValues && !startKeyframePropertie s.contains(property);
189 bool endNeedsValue = missingEndValues && !endKeyframeProperties.cont ains(property); 187 bool endNeedsValue = missingEndValues && !endKeyframeProperties.cont ains(property);
190 if (!startNeedsValue && !endNeedsValue) 188 if (!startNeedsValue && !endNeedsValue)
191 continue; 189 continue;
192 RefPtrWillBeRawPtr<AnimatableValue> snapshotValue = CSSAnimatableVal ueFactory::create(property, style); 190 if (CompositorAnimations::isCompositableProperty(property))
193 if (startNeedsValue)
194 startKeyframe->setPropertyValue(property, snapshotValue.get());
195 if (endNeedsValue)
196 endKeyframe->setPropertyValue(property, snapshotValue.get());
197 if (property == CSSPropertyOpacity || property == CSSPropertyTransfo rm)
198 UseCounter::count(elementForScoping->document(), UseCounter::Syn theticKeyframesInCompositedCSSAnimation); 191 UseCounter::count(elementForScoping->document(), UseCounter::Syn theticKeyframesInCompositedCSSAnimation);
199 } 192 }
200 } 193 }
201 ASSERT(startKeyframe->properties().size() == allProperties.size());
202 ASSERT(endKeyframe->properties().size() == allProperties.size());
203 194
204 return AnimatableValueKeyframeEffectModel::create(keyframes, &keyframes[0]-> easing()); 195 RefPtrWillBeRawPtr<StringKeyframeEffectModel> effect = StringKeyframeEffectM odel::create(keyframes, &keyframes[0]->easing());
196 effect->forceConversionsToAnimatableValues(element, style);
197 return effect;
205 } 198 }
199
206 } // namespace 200 } // namespace
207 201
208 CSSAnimations::CSSAnimations() 202 CSSAnimations::CSSAnimations()
209 { 203 {
210 } 204 }
211 205
212 const AtomicString CSSAnimations::getAnimationNameForInspector(const AnimationPl ayer& player) 206 const AtomicString CSSAnimations::getAnimationNameForInspector(const AnimationPl ayer& player)
213 { 207 {
214 for (const auto& it : m_animations) { 208 for (const auto& it : m_animations) {
215 if (it.value->player->sequenceNumber() == player.sequenceNumber()) 209 if (it.value->player->sequenceNumber() == player.sequenceNumber())
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
281 AnimationMap::const_iterator existing(cssAnimations->m_animation s.find(animationName)); 275 AnimationMap::const_iterator existing(cssAnimations->m_animation s.find(animationName));
282 if (existing != cssAnimations->m_animations.end()) { 276 if (existing != cssAnimations->m_animations.end()) {
283 inactive.remove(animationName); 277 inactive.remove(animationName);
284 278
285 const RunningAnimation* runningAnimation = existing->value.g et(); 279 const RunningAnimation* runningAnimation = existing->value.g et();
286 AnimationPlayer* player = runningAnimation->player.get(); 280 AnimationPlayer* player = runningAnimation->player.get();
287 281
288 if (keyframesRule != runningAnimation->styleRule || keyframe sRule->version() != runningAnimation->styleRuleVersion || runningAnimation->spec ifiedTiming != specifiedTiming) { 282 if (keyframesRule != runningAnimation->styleRule || keyframe sRule->version() != runningAnimation->styleRuleVersion || runningAnimation->spec ifiedTiming != specifiedTiming) {
289 ASSERT(!isAnimationStyleChange); 283 ASSERT(!isAnimationStyleChange);
290 update->updateAnimation(animationName, player, InertAnim ation::create( 284 update->updateAnimation(animationName, player, InertAnim ation::create(
291 createKeyframeEffect(resolver, animatingElement, ele ment, style, parentStyle, animationName, keyframeTimingFunction.get()), 285 createKeyframeEffect(resolver, animatingElement, ele ment, &style, parentStyle, animationName, keyframeTimingFunction.get()),
292 timing, isPaused, player->unlimitedCurrentTimeIntern al()), specifiedTiming, keyframesRule); 286 timing, isPaused, player->unlimitedCurrentTimeIntern al()), specifiedTiming, keyframesRule);
287 } else if (!isAnimationStyleChange && player->source() && pl ayer->source()->isAnimation()) {
288 AnimationEffect* effect = toAnimation(player->source())- >effect();
289 if (effect && effect->isKeyframeEffectModel()) {
290 KeyframeEffectModelBase* keyframeEffect = toKeyframe EffectModelBase(effect);
291 if (keyframeEffect->hasSyntheticKeyframes())
292 update->updateAnimationStyle(player, keyframeEff ect, animatingElement->layoutObject(), style);
293 }
293 } 294 }
294 295
295 if (isPaused != player->paused()) { 296 if (isPaused != player->paused()) {
296 ASSERT(!isAnimationStyleChange); 297 ASSERT(!isAnimationStyleChange);
297 update->toggleAnimationPaused(animationName); 298 update->toggleAnimationPaused(animationName);
298 } 299 }
299 300
300 continue; 301 continue;
301 } 302 }
302 } 303 }
303 304
304 ASSERT(!isAnimationStyleChange); 305 ASSERT(!isAnimationStyleChange);
305 update->startAnimation(animationName, InertAnimation::create( 306 update->startAnimation(animationName, InertAnimation::create(
306 createKeyframeEffect(resolver, animatingElement, element, style, parentStyle, animationName, keyframeTimingFunction.get()), 307 createKeyframeEffect(resolver, animatingElement, element, &style , parentStyle, animationName, keyframeTimingFunction.get()),
307 timing, isPaused, 0), specifiedTiming, keyframesRule); 308 timing, isPaused, 0), specifiedTiming, keyframesRule);
308 } 309 }
309 } 310 }
310 311
311 ASSERT(inactive.isEmpty() || cssAnimations); 312 ASSERT(inactive.isEmpty() || cssAnimations);
312 for (const AtomicString& animationName : inactive) { 313 for (const AtomicString& animationName : inactive) {
313 ASSERT(!isAnimationStyleChange); 314 ASSERT(!isAnimationStyleChange);
314 update->cancelAnimation(animationName, *cssAnimations->m_animations.get( animationName)->player); 315 update->cancelAnimation(animationName, *cssAnimations->m_animations.get( animationName)->player);
315 } 316 }
316 } 317 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
349 350
350 for (const auto& entry : update->animationsWithUpdates()) { 351 for (const auto& entry : update->animationsWithUpdates()) {
351 Animation* animation = toAnimation(entry.player->source()); 352 Animation* animation = toAnimation(entry.player->source());
352 353
353 animation->setEffect(entry.animation->effect()); 354 animation->setEffect(entry.animation->effect());
354 animation->updateSpecifiedTiming(entry.animation->specifiedTiming()); 355 animation->updateSpecifiedTiming(entry.animation->specifiedTiming());
355 356
356 m_animations.find(entry.name)->value->update(entry); 357 m_animations.find(entry.name)->value->update(entry);
357 } 358 }
358 359
360 for (const auto& styleUpdate : update->animationsWithStyleUpdates()) {
361 styleUpdate.effect->forEachInterpolation([](Interpolation& interpolation ) {
362 if (interpolation.isStyleInterpolation() && toStyleInterpolation(int erpolation).isDeferredLegacyStyleInterpolation())
363 toDeferredLegacyStyleInterpolation(toStyleInterpolation(interpol ation)).underlyingStyleChanged();
364 });
365
366 bool updated = false;
367 if (styleUpdate.snapshot.opacity)
368 updated |= styleUpdate.effect->updateNeutralKeyframeAnimatableValues (CSSPropertyOpacity, styleUpdate.snapshot.opacity);
369 if (styleUpdate.snapshot.transform)
370 updated |= styleUpdate.effect->updateNeutralKeyframeAnimatableValues (CSSPropertyTransform, styleUpdate.snapshot.transform);
371 if (styleUpdate.snapshot.webkitFilter)
372 updated |= styleUpdate.effect->updateNeutralKeyframeAnimatableValues (CSSPropertyWebkitFilter, styleUpdate.snapshot.webkitFilter);
373 if (updated) {
374 styleUpdate.player->setOutdated();
375 styleUpdate.player->setCompositorPending(true);
376 }
377 }
378
359 for (const auto& entry : update->newAnimations()) { 379 for (const auto& entry : update->newAnimations()) {
360 const InertAnimation* inertAnimation = entry.animation.get(); 380 const InertAnimation* inertAnimation = entry.animation.get();
361 OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillB eNoop(new AnimationEventDelegate(element, entry.name)); 381 OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillB eNoop(new AnimationEventDelegate(element, entry.name));
362 RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, ine rtAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPrio rity, eventDelegate.release()); 382 RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, ine rtAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPrio rity, eventDelegate.release());
363 animation->setName(inertAnimation->name()); 383 animation->setName(inertAnimation->name());
364 RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timelin e().play(animation.get()); 384 RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timelin e().play(animation.get());
365 if (inertAnimation->paused()) 385 if (inertAnimation->paused())
366 player->pause(); 386 player->pause();
367 player->update(TimingUpdateOnDemand); 387 player->update(TimingUpdateOnDemand);
368 388
(...skipping 433 matching lines...) Expand 10 before | Expand all | Expand 10 after
802 visitor->trace(m_activeInterpolationsForAnimations); 822 visitor->trace(m_activeInterpolationsForAnimations);
803 visitor->trace(m_activeInterpolationsForTransitions); 823 visitor->trace(m_activeInterpolationsForTransitions);
804 visitor->trace(m_newAnimations); 824 visitor->trace(m_newAnimations);
805 visitor->trace(m_suppressedAnimationPlayers); 825 visitor->trace(m_suppressedAnimationPlayers);
806 visitor->trace(m_animationsWithUpdates); 826 visitor->trace(m_animationsWithUpdates);
807 visitor->trace(m_animationsWithStyleUpdates); 827 visitor->trace(m_animationsWithStyleUpdates);
808 #endif 828 #endif
809 } 829 }
810 830
811 } // namespace blink 831 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/animation/KeyframeEffectModel.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698