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

Side by Side Diff: Source/core/frame/animation/KeyframeAnimation.cpp

Issue 139273007: Web Animations: Remove legacy animations engine. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Fix TestExpectations. Created 6 years, 10 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
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2007, 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "core/frame/animation/KeyframeAnimation.h"
31
32 #include "CSSPropertyNames.h"
33 #include "core/css/resolver/StyleResolver.h"
34 #include "core/events/ThreadLocalEventNames.h"
35 #include "core/frame/UseCounter.h"
36 #include "core/frame/animation/AnimationControllerPrivate.h"
37 #include "core/frame/animation/CSSPropertyAnimation.h"
38 #include "core/frame/animation/CompositeAnimation.h"
39 #include "core/rendering/RenderBoxModelObject.h"
40 #include "core/rendering/style/RenderStyle.h"
41 #include "public/platform/Platform.h"
42
43 using namespace std;
44
45 namespace WebCore {
46
47 KeyframeAnimation::KeyframeAnimation(const CSSAnimationData* animation, RenderOb ject& renderer, int index, CompositeAnimation* compAnim, RenderStyle& unanimated Style)
48 : AnimationBase(animation, renderer, compAnim)
49 , m_keyframes(renderer, animation->name())
50 , m_index(index)
51 , m_startEventDispatched(false)
52 , m_unanimatedStyle(unanimatedStyle)
53 {
54 // Get the keyframe RenderStyles
55 if (m_object && m_object->node() && m_object->node()->isElementNode())
56 m_object->document().ensureStyleResolver().keyframeStylesForAnimation(to Element(m_object->node()), unanimatedStyle, m_keyframes);
57
58 // Update the m_transformFunctionListValid flag based on whether the functio n lists in the keyframes match.
59 validateTransformFunctionList();
60 checkForMatchingFilterFunctionLists();
61 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProper ties();
62 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties (); it != endProperties; ++it)
63 blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProper ties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(*it));
64 }
65
66 KeyframeAnimation::~KeyframeAnimation()
67 {
68 // Make sure to tell the renderer that we are ending. This will make sure an y accelerated animations are removed.
69 if (!postActive())
70 endAnimation();
71 }
72
73 void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property , const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) cons t
74 {
75 // Find the first key
76 double elapsedTime = getElapsedTime();
77 if (m_animation->duration() && m_animation->iterationCount() != CSSAnimation Data::IterationCountInfinite)
78 elapsedTime = min(elapsedTime, m_animation->duration() * m_animation->it erationCount());
79
80 const double fractionalTime = this->fractionalTime(1, elapsedTime, 0);
81
82 size_t numKeyframes = m_keyframes.size();
83 if (!numKeyframes)
84 return;
85
86 ASSERT(!m_keyframes[0].key());
87 ASSERT(m_keyframes[m_keyframes.size() - 1].key() == 1);
88
89 size_t currentIndex = 0;
90 size_t firstIndex = 0;
91 size_t lastIndex = numKeyframes - 1;
92 size_t distance = numKeyframes;
93
94 // Find keyframe that is closest to elapsed time.
95 while (distance > 1) {
96 currentIndex = (lastIndex + firstIndex) >> 1;
97 double key = m_keyframes[currentIndex].key();
98 distance = lastIndex - currentIndex;
99
100 if (key < fractionalTime) {
101 if (distance < 2)
102 currentIndex++;
103 firstIndex = currentIndex;
104 } else {
105 lastIndex = currentIndex;
106 }
107 }
108
109 int prevIndex = -1;
110 int nextIndex = -1;
111
112 // Iterate forward to find next keyframe that is used to animate CSS propert y.
113 for (size_t i = currentIndex; i < numKeyframes; ++i) {
114 const KeyframeValue& keyFrame = m_keyframes[i];
115 if (keyFrame.key() > fractionalTime && keyFrame.containsProperty(propert y)) {
116 nextIndex = i;
117 break;
118 }
119 }
120
121 // Iterate backward to find previous keyframe.
122 for (int i = currentIndex; i >= 0; --i) {
123 const KeyframeValue& keyFrame = m_keyframes[i];
124 if (keyFrame.key() <= fractionalTime && keyFrame.containsProperty(proper ty)) {
125 prevIndex = i;
126 break;
127 }
128 }
129
130 double scale = 1;
131 double offset = 0;
132
133 if (prevIndex == -1)
134 prevIndex = 0;
135
136 if (nextIndex == -1)
137 nextIndex = numKeyframes - 1;
138
139 const KeyframeValue& prevKeyframe = m_keyframes[prevIndex];
140 const KeyframeValue& nextKeyframe = m_keyframes[nextIndex];
141
142 fromStyle = prevKeyframe.style();
143 toStyle = nextKeyframe.style();
144
145 offset = prevKeyframe.key();
146 scale = 1.0 / (nextKeyframe.key() - prevKeyframe.key());
147 // A scale of infinity is handled in AnimationBase::fractionalTime().
148 ASSERT(scale >= 0 && (!std::isinf(scale) || prevIndex == nextIndex));
149
150 prog = progress(scale, offset, KeyframeValue::timingFunction(*prevKeyframe.s tyle()));
151 }
152
153 void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const Render Style*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle)
154 {
155 // Fire the start timeout if needed
156 fireAnimationEventsIfNeeded();
157
158 // If we have not yet started, we will not have a valid start time, so just start the animation if needed.
159 if (isNew() && m_animation->playState() == AnimPlayStatePlaying)
160 updateStateMachine(AnimationStateInputStartAnimation, -1);
161
162 // If we get this far and the animation is done, it means we are cleaning up a just finished animation.
163 // If so, we need to send back the targetStyle.
164 if (postActive()) {
165 if (!animatedStyle)
166 animatedStyle = const_cast<RenderStyle*>(targetStyle);
167 return;
168 }
169
170 // If we are waiting for the start timer, we don't want to change the style yet.
171 // Special case 1 - if the delay time is 0, then we do want to set the first frame of the
172 // animation right away. This avoids a flash when the animation starts.
173 // Special case 2 - if there is a backwards fill mode, then we want to conti nue
174 // through to the style blend so that we get the fromStyle.
175 if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackw ards())
176 return;
177
178 // If we have no keyframes, don't animate.
179 if (!m_keyframes.size()) {
180 updateStateMachine(AnimationStateInputEndAnimation, -1);
181 return;
182 }
183
184 // Run a cycle of animation.
185 // We know we will need a new render style, so make one if needed.
186 if (!animatedStyle)
187 animatedStyle = RenderStyle::clone(targetStyle);
188
189 // FIXME: we need to be more efficient about determining which keyframes we are animating between.
190 // We should cache the last pair or something.
191 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProper ties();
192 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties (); it != endProperties; ++it) {
193 // Get the from/to styles and progress between
194 const RenderStyle* fromStyle = 0;
195 const RenderStyle* toStyle = 0;
196 double progress = 0.0;
197 fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress);
198
199 bool needsAnim = CSSPropertyAnimation::blendProperties(this, *it, animat edStyle.get(), fromStyle, toStyle, progress);
200 if (!needsAnim)
201 // If we are running an accelerated animation, set a flag in the sty le
202 // to indicate it. This can be used to make sure we get an updated
203 // style for hit testing, etc.
204 animatedStyle->setIsRunningAcceleratedAnimation();
205 }
206 }
207
208 void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle)
209 {
210 // If we're in the delay phase and we're not backwards filling, tell the cal ler
211 // to use the current style.
212 if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackw ards())
213 return;
214
215 if (!m_keyframes.size())
216 return;
217
218 if (!animatedStyle)
219 animatedStyle = RenderStyle::clone(m_object->style());
220
221 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProper ties();
222 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties (); it != endProperties; ++it) {
223 // Get the from/to styles and progress between
224 const RenderStyle* fromStyle = 0;
225 const RenderStyle* toStyle = 0;
226 double progress = 0.0;
227 fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress);
228
229 CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fr omStyle, toStyle, progress);
230 }
231 }
232
233 bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const
234 {
235 return m_keyframes.containsProperty(property);
236 }
237
238 void KeyframeAnimation::startAnimation(double timeOffset)
239 {
240 if (m_object && m_object->compositingState() == PaintsIntoOwnBacking)
241 m_isAccelerated = toRenderBoxModelObject(m_object)->startAnimation(timeO ffset, m_animation.get(), m_keyframes);
242 }
243
244 void KeyframeAnimation::pauseAnimation(double timeOffset)
245 {
246 if (!m_object)
247 return;
248
249 if (m_object && m_object->compositingState() == PaintsIntoOwnBacking && isAc celerated())
250 toRenderBoxModelObject(m_object)->animationPaused(timeOffset, m_keyframe s.animationName());
251
252 // Restore the original (unanimated) style
253 if (!paused())
254 setNeedsStyleRecalc(m_object->node());
255 }
256
257 void KeyframeAnimation::endAnimation()
258 {
259 if (!m_object)
260 return;
261
262 if (m_object && m_object->compositingState() == PaintsIntoOwnBacking && isAc celerated())
263 toRenderBoxModelObject(m_object)->animationFinished(m_keyframes.animatio nName());
264 m_isAccelerated = false;
265
266 // Restore the original (unanimated) style
267 if (!paused())
268 setNeedsStyleRecalc(m_object->node());
269 }
270
271 bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listen erType) const
272 {
273 return m_object->document().hasListenerType(listenerType);
274 }
275
276 void KeyframeAnimation::onAnimationStart(double elapsedTime)
277 {
278 sendAnimationEvent(EventTypeNames::animationstart, elapsedTime);
279 }
280
281 void KeyframeAnimation::onAnimationIteration(double elapsedTime)
282 {
283 sendAnimationEvent(EventTypeNames::animationiteration, elapsedTime);
284 }
285
286 void KeyframeAnimation::onAnimationEnd(double elapsedTime)
287 {
288 sendAnimationEvent(EventTypeNames::animationend, elapsedTime);
289 // End the animation if we don't fill forwards. Forward filling
290 // animations are ended properly in the class destructor.
291 if (!m_animation->fillsForwards())
292 endAnimation();
293 }
294
295 bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime)
296 {
297 Document::ListenerType listenerType;
298 if (eventType == EventTypeNames::animationiteration)
299 listenerType = Document::ANIMATIONITERATION_LISTENER;
300 else if (eventType == EventTypeNames::animationend)
301 listenerType = Document::ANIMATIONEND_LISTENER;
302 else {
303 ASSERT(eventType == EventTypeNames::animationstart);
304 if (m_startEventDispatched)
305 return false;
306 m_startEventDispatched = true;
307 listenerType = Document::ANIMATIONSTART_LISTENER;
308 }
309
310 if (shouldSendEventForListener(listenerType)) {
311 // Dispatch the event
312 RefPtr<Element> element;
313 if (m_object->node() && m_object->node()->isElementNode())
314 element = toElement(m_object->node());
315
316 if (!element)
317 return false;
318
319 // Schedule event handling
320 m_compAnim->animationController()->addEventToDispatch(element, eventType , m_keyframes.animationName(), elapsedTime);
321
322 // Restore the original (unanimated) style
323 if (eventType == EventTypeNames::animationend && element->renderer())
324 setNeedsStyleRecalc(element.get());
325
326 return true; // Did dispatch an event
327 }
328
329 return false; // Did not dispatch an event
330 }
331
332 void KeyframeAnimation::overrideAnimations()
333 {
334 // This will override implicit animations that match the properties in the k eyframe animation
335 HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties();
336 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties (); it != end; ++it)
337 compositeAnimation()->overrideImplicitAnimations(*it);
338 }
339
340 void KeyframeAnimation::resumeOverriddenAnimations()
341 {
342 // This will resume overridden implicit animations
343 HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties();
344 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties (); it != end; ++it)
345 compositeAnimation()->resumeOverriddenImplicitAnimations(*it);
346 }
347
348 bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const
349 {
350 return m_keyframes.containsProperty(property);
351 }
352
353 void KeyframeAnimation::validateTransformFunctionList()
354 {
355 m_transformFunctionListValid = false;
356
357 if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebki tTransform))
358 return;
359
360 // Empty transforms match anything, so find the first non-empty entry as the reference
361 size_t numKeyframes = m_keyframes.size();
362 size_t firstNonEmptyTransformKeyframeIndex = numKeyframes;
363
364 for (size_t i = 0; i < numKeyframes; ++i) {
365 const KeyframeValue& currentKeyframe = m_keyframes[i];
366 if (currentKeyframe.style()->transform().operations().size()) {
367 firstNonEmptyTransformKeyframeIndex = i;
368 break;
369 }
370 }
371
372 if (firstNonEmptyTransformKeyframeIndex == numKeyframes)
373 return;
374
375 const TransformOperations* firstVal = &m_keyframes[firstNonEmptyTransformKey frameIndex].style()->transform();
376
377 // See if the keyframes are valid
378 for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; + +i) {
379 const KeyframeValue& currentKeyframe = m_keyframes[i];
380 const TransformOperations* val = &currentKeyframe.style()->transform();
381
382 // An emtpy transform list matches anything.
383 if (val->operations().isEmpty())
384 continue;
385
386 if (!firstVal->operationsMatch(*val))
387 return;
388 }
389
390 // Keyframes are valid
391 m_transformFunctionListValid = true;
392 }
393
394 void KeyframeAnimation::checkForMatchingFilterFunctionLists()
395 {
396 m_filterFunctionListsMatch = false;
397
398 if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebki tFilter))
399 return;
400
401 // Empty filters match anything, so find the first non-empty entry as the re ference
402 size_t numKeyframes = m_keyframes.size();
403 size_t firstNonEmptyFilterKeyframeIndex = numKeyframes;
404
405 for (size_t i = 0; i < numKeyframes; ++i) {
406 const KeyframeValue& currentKeyframe = m_keyframes[i];
407 if (currentKeyframe.style()->filter().operations().size()) {
408 firstNonEmptyFilterKeyframeIndex = i;
409 break;
410 }
411 }
412
413 if (firstNonEmptyFilterKeyframeIndex == numKeyframes)
414 return;
415
416 const FilterOperations* firstVal = &m_keyframes[firstNonEmptyFilterKeyframeI ndex].style()->filter();
417
418 for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) {
419 const KeyframeValue& currentKeyframe = m_keyframes[i];
420 const FilterOperations* val = &currentKeyframe.style()->filter();
421
422 if (!firstVal->canInterpolateWith(*val))
423 return;
424 }
425
426 m_filterFunctionListsMatch = true;
427 }
428
429 double KeyframeAnimation::timeToNextService()
430 {
431 double t = AnimationBase::timeToNextService();
432 if (t != 0 || preActive())
433 return t;
434
435 // A return value of 0 means we need service. But if we only have accelerate d animations we
436 // only need service at the end of the transition
437 HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProper ties();
438 bool acceleratedPropertiesOnly = true;
439
440 for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties (); it != endProperties; ++it) {
441 if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(*it) || !isA ccelerated()) {
442 acceleratedPropertiesOnly = false;
443 break;
444 }
445 }
446
447 if (acceleratedPropertiesOnly) {
448 bool isLooping;
449 getTimeToNextEvent(t, isLooping);
450 }
451
452 return t;
453 }
454
455 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/core/frame/animation/KeyframeAnimation.h ('k') | Source/core/rendering/CompositedLayerMapping.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698