Stop using PassRefPtr in platform/graphics
[WebKit-https.git] / Source / WebCore / platform / graphics / ca / cocoa / PlatformCAAnimationCocoa.mm
1 /*
2  * Copyright (C) 2014 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "PlatformCAAnimationCocoa.h"
28
29 #import "FloatConversion.h"
30 #import "PlatformCAFilters.h"
31 #import "QuartzCoreSPI.h"
32 #import "TimingFunction.h"
33 #import <QuartzCore/QuartzCore.h>
34 #import <wtf/text/WTFString.h>
35
36 using namespace WebCore;
37
38 static NSString * const WKExplicitBeginTimeFlag = @"WKPlatformCAAnimationExplicitBeginTimeFlag";
39
40 bool WebCore::hasExplicitBeginTime(CAAnimation *animation)
41 {
42     return [[animation valueForKey:WKExplicitBeginTimeFlag] boolValue];
43 }
44
45 void WebCore::setHasExplicitBeginTime(CAAnimation *animation, bool value)
46 {
47     [animation setValue:[NSNumber numberWithBool:value] forKey:WKExplicitBeginTimeFlag];
48 }
49     
50 NSString* WebCore::toCAFillModeType(PlatformCAAnimation::FillModeType type)
51 {
52     switch (type) {
53     case PlatformCAAnimation::NoFillMode:
54     case PlatformCAAnimation::Forwards: return kCAFillModeForwards;
55     case PlatformCAAnimation::Backwards: return kCAFillModeBackwards;
56     case PlatformCAAnimation::Both: return kCAFillModeBoth;
57     }
58     return @"";
59 }
60
61 static PlatformCAAnimation::FillModeType fromCAFillModeType(NSString* string)
62 {
63     if ([string isEqualToString:kCAFillModeBackwards])
64         return PlatformCAAnimation::Backwards;
65
66     if ([string isEqualToString:kCAFillModeBoth])
67         return PlatformCAAnimation::Both;
68
69     return PlatformCAAnimation::Forwards;
70 }
71
72 NSString* WebCore::toCAValueFunctionType(PlatformCAAnimation::ValueFunctionType type)
73 {
74     switch (type) {
75     case PlatformCAAnimation::NoValueFunction: return @"";
76     case PlatformCAAnimation::RotateX: return kCAValueFunctionRotateX;
77     case PlatformCAAnimation::RotateY: return kCAValueFunctionRotateY;
78     case PlatformCAAnimation::RotateZ: return kCAValueFunctionRotateZ;
79     case PlatformCAAnimation::ScaleX: return kCAValueFunctionScaleX;
80     case PlatformCAAnimation::ScaleY: return kCAValueFunctionScaleY;
81     case PlatformCAAnimation::ScaleZ: return kCAValueFunctionScaleZ;
82     case PlatformCAAnimation::Scale: return kCAValueFunctionScale;
83     case PlatformCAAnimation::TranslateX: return kCAValueFunctionTranslateX;
84     case PlatformCAAnimation::TranslateY: return kCAValueFunctionTranslateY;
85     case PlatformCAAnimation::TranslateZ: return kCAValueFunctionTranslateZ;
86     case PlatformCAAnimation::Translate: return kCAValueFunctionTranslate;
87     }
88     return @"";
89 }
90
91 static PlatformCAAnimation::ValueFunctionType fromCAValueFunctionType(NSString* string)
92 {
93     if ([string isEqualToString:kCAValueFunctionRotateX])
94         return PlatformCAAnimation::RotateX;
95
96     if ([string isEqualToString:kCAValueFunctionRotateY])
97         return PlatformCAAnimation::RotateY;
98
99     if ([string isEqualToString:kCAValueFunctionRotateZ])
100         return PlatformCAAnimation::RotateZ;
101
102     if ([string isEqualToString:kCAValueFunctionScaleX])
103         return PlatformCAAnimation::ScaleX;
104
105     if ([string isEqualToString:kCAValueFunctionScaleY])
106         return PlatformCAAnimation::ScaleY;
107
108     if ([string isEqualToString:kCAValueFunctionScaleZ])
109         return PlatformCAAnimation::ScaleZ;
110
111     if ([string isEqualToString:kCAValueFunctionScale])
112         return PlatformCAAnimation::Scale;
113
114     if ([string isEqualToString:kCAValueFunctionTranslateX])
115         return PlatformCAAnimation::TranslateX;
116
117     if ([string isEqualToString:kCAValueFunctionTranslateY])
118         return PlatformCAAnimation::TranslateY;
119
120     if ([string isEqualToString:kCAValueFunctionTranslateZ])
121         return PlatformCAAnimation::TranslateZ;
122
123     if ([string isEqualToString:kCAValueFunctionTranslate])
124         return PlatformCAAnimation::Translate;
125
126     return PlatformCAAnimation::NoValueFunction;
127 }
128
129 CAMediaTimingFunction* WebCore::toCAMediaTimingFunction(const TimingFunction* timingFunction, bool reverse)
130 {
131     ASSERT(timingFunction);
132     if (timingFunction->isCubicBezierTimingFunction()) {
133         RefPtr<CubicBezierTimingFunction> reversed;
134         const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
135
136         if (reverse) {
137             reversed = ctf->createReversed();
138             ctf = reversed.get();
139         }
140
141         float x1 = static_cast<float>(ctf->x1());
142         float y1 = static_cast<float>(ctf->y1());
143         float x2 = static_cast<float>(ctf->x2());
144         float y2 = static_cast<float>(ctf->y2());
145         return [CAMediaTimingFunction functionWithControlPoints: x1 : y1 : x2 : y2];
146     }
147     
148     ASSERT(timingFunction->type() == TimingFunction::LinearFunction);
149     return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
150 }
151
152 Ref<PlatformCAAnimation> PlatformCAAnimationCocoa::create(AnimationType type, const String& keyPath)
153 {
154     return adoptRef(*new PlatformCAAnimationCocoa(type, keyPath));
155 }
156
157 Ref<PlatformCAAnimation> PlatformCAAnimationCocoa::create(PlatformAnimationRef animation)
158 {
159     return adoptRef(*new PlatformCAAnimationCocoa(animation));
160 }
161
162 PlatformCAAnimationCocoa::PlatformCAAnimationCocoa(AnimationType type, const String& keyPath)
163     : PlatformCAAnimation(type)
164 {
165     switch (type) {
166     case Basic:
167         m_animation = [CABasicAnimation animationWithKeyPath:keyPath];
168         break;
169     case Keyframe:
170         m_animation = [CAKeyframeAnimation animationWithKeyPath:keyPath];
171         break;
172     case Spring:
173         m_animation = [CASpringAnimation animationWithKeyPath:keyPath];
174         break;
175     }
176 }
177
178 PlatformCAAnimationCocoa::PlatformCAAnimationCocoa(PlatformAnimationRef animation)
179 {
180     if ([static_cast<CAAnimation*>(animation) isKindOfClass:[CABasicAnimation class]]) {
181         if ([static_cast<CAAnimation*>(animation) isKindOfClass:[CASpringAnimation class]])
182             setType(Spring);
183         else
184             setType(Basic);
185     } else if ([static_cast<CAAnimation*>(animation) isKindOfClass:[CAKeyframeAnimation class]])
186         setType(Keyframe);
187     else {
188         ASSERT_NOT_REACHED();
189         return;
190     }
191     
192     m_animation = static_cast<CAPropertyAnimation*>(animation);
193 }
194
195 PlatformCAAnimationCocoa::~PlatformCAAnimationCocoa()
196 {
197 }
198
199 Ref<PlatformCAAnimation> PlatformCAAnimationCocoa::copy() const
200 {
201     auto animation = create(animationType(), keyPath());
202     
203     animation->setBeginTime(beginTime());
204     animation->setDuration(duration());
205     animation->setSpeed(speed());
206     animation->setTimeOffset(timeOffset());
207     animation->setRepeatCount(repeatCount());
208     animation->setAutoreverses(autoreverses());
209     animation->setFillMode(fillMode());
210     animation->setRemovedOnCompletion(isRemovedOnCompletion());
211     animation->setAdditive(isAdditive());
212     animation->copyTimingFunctionFrom(*this);
213     animation->setValueFunction(valueFunction());
214
215     setHasExplicitBeginTime(downcast<PlatformCAAnimationCocoa>(animation.get()).platformAnimation(), hasExplicitBeginTime(platformAnimation()));
216     
217     // Copy the specific Basic or Keyframe values.
218     if (animationType() == Keyframe) {
219         animation->copyValuesFrom(*this);
220         animation->copyKeyTimesFrom(*this);
221         animation->copyTimingFunctionsFrom(*this);
222     } else {
223         animation->copyFromValueFrom(*this);
224         animation->copyToValueFrom(*this);
225     }
226     
227     return animation;
228 }
229
230 PlatformAnimationRef PlatformCAAnimationCocoa::platformAnimation() const
231 {
232     return m_animation.get();
233 }
234
235 String PlatformCAAnimationCocoa::keyPath() const
236 {
237     return [m_animation keyPath];
238 }
239
240 CFTimeInterval PlatformCAAnimationCocoa::beginTime() const
241 {
242     return [m_animation beginTime];
243 }
244
245 void PlatformCAAnimationCocoa::setBeginTime(CFTimeInterval value)
246 {
247     [m_animation setBeginTime:value];
248     
249     // Also set a flag to tell us if we've passed in a 0 value. 
250     // The flag is needed because later beginTime will get changed
251     // to the time at which it fired and we need to know whether
252     // or not it was 0 to begin with.
253     if (value)
254         setHasExplicitBeginTime(m_animation.get(), true);
255 }
256
257 CFTimeInterval PlatformCAAnimationCocoa::duration() const
258 {
259     return [m_animation duration];
260 }
261
262 void PlatformCAAnimationCocoa::setDuration(CFTimeInterval value)
263 {
264     [m_animation setDuration:value];
265 }
266
267 float PlatformCAAnimationCocoa::speed() const
268 {
269     return [m_animation speed];
270 }
271
272 void PlatformCAAnimationCocoa::setSpeed(float value)
273 {
274     [m_animation setSpeed:value];
275 }
276
277 CFTimeInterval PlatformCAAnimationCocoa::timeOffset() const
278 {
279     return [m_animation timeOffset];
280 }
281
282 void PlatformCAAnimationCocoa::setTimeOffset(CFTimeInterval value)
283 {
284     [m_animation setTimeOffset:value];
285 }
286
287 float PlatformCAAnimationCocoa::repeatCount() const
288 {
289     return [m_animation repeatCount];
290 }
291
292 void PlatformCAAnimationCocoa::setRepeatCount(float value)
293 {
294     [m_animation setRepeatCount:value];
295 }
296
297 bool PlatformCAAnimationCocoa::autoreverses() const
298 {
299     return [m_animation autoreverses];
300 }
301
302 void PlatformCAAnimationCocoa::setAutoreverses(bool value)
303 {
304     [m_animation setAutoreverses:value];
305 }
306
307 PlatformCAAnimation::FillModeType PlatformCAAnimationCocoa::fillMode() const
308 {
309     return fromCAFillModeType([m_animation fillMode]);
310 }
311
312 void PlatformCAAnimationCocoa::setFillMode(FillModeType value)
313 {
314     [m_animation setFillMode:toCAFillModeType(value)];
315 }
316
317 void PlatformCAAnimationCocoa::setTimingFunction(const TimingFunction* value, bool reverse)
318 {
319     switch (animationType()) {
320     case Basic:
321     case Keyframe:
322         [m_animation setTimingFunction:toCAMediaTimingFunction(value, reverse)];
323         break;
324     case Spring:
325         if (value->isSpringTimingFunction()) {
326             // FIXME: Handle reverse.
327             auto& function = *static_cast<const SpringTimingFunction*>(value);
328             CASpringAnimation *springAnimation = (CASpringAnimation *)m_animation.get();
329             springAnimation.mass = function.mass();
330             springAnimation.stiffness = function.stiffness();
331             springAnimation.damping = function.damping();
332 #if PLATFORM(IOS) || PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
333             springAnimation.initialVelocity = function.initialVelocity();
334 #else
335             springAnimation.velocity = function.initialVelocity();
336 #endif
337         }
338         break;
339     }
340 }
341
342 void PlatformCAAnimationCocoa::copyTimingFunctionFrom(const PlatformCAAnimation& value)
343 {
344     [m_animation setTimingFunction:[downcast<PlatformCAAnimationCocoa>(value).m_animation.get() timingFunction]];
345 }
346
347 bool PlatformCAAnimationCocoa::isRemovedOnCompletion() const
348 {
349     return [m_animation isRemovedOnCompletion];
350 }
351
352 void PlatformCAAnimationCocoa::setRemovedOnCompletion(bool value)
353 {
354     [m_animation setRemovedOnCompletion:value];
355 }
356
357 bool PlatformCAAnimationCocoa::isAdditive() const
358 {
359     return [m_animation isAdditive];
360 }
361
362 void PlatformCAAnimationCocoa::setAdditive(bool value)
363 {
364     [m_animation setAdditive:value];
365 }
366
367 PlatformCAAnimation::ValueFunctionType PlatformCAAnimationCocoa::valueFunction() const
368 {
369     CAValueFunction* vf = [m_animation valueFunction];
370     return fromCAValueFunctionType([vf name]);
371 }
372
373 void PlatformCAAnimationCocoa::setValueFunction(ValueFunctionType value)
374 {
375     [m_animation setValueFunction:[CAValueFunction functionWithName:toCAValueFunctionType(value)]];
376 }
377
378 void PlatformCAAnimationCocoa::setFromValue(float value)
379 {
380     if (!isBasicAnimation())
381         return;
382     [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:[NSNumber numberWithDouble:value]];
383 }
384
385 void PlatformCAAnimationCocoa::setFromValue(const WebCore::TransformationMatrix& value)
386 {
387     if (!isBasicAnimation())
388         return;
389
390     [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:[NSValue valueWithCATransform3D:value]];
391 }
392
393 void PlatformCAAnimationCocoa::setFromValue(const FloatPoint3D& value)
394 {
395     if (!isBasicAnimation())
396         return;
397
398     NSArray* array = [NSArray arrayWithObjects:
399         [NSNumber numberWithDouble:value.x()],
400         [NSNumber numberWithDouble:value.y()],
401         [NSNumber numberWithDouble:value.z()],
402         nil];
403     [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:array];
404 }
405
406 void PlatformCAAnimationCocoa::setFromValue(const WebCore::Color& value)
407 {
408     if (!isBasicAnimation())
409         return;
410
411     NSArray* array = [NSArray arrayWithObjects:
412         [NSNumber numberWithDouble:value.red()],
413         [NSNumber numberWithDouble:value.green()],
414         [NSNumber numberWithDouble:value.blue()],
415         [NSNumber numberWithDouble:value.alpha()],
416         nil];
417     [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:array];
418 }
419
420 void PlatformCAAnimationCocoa::setFromValue(const FilterOperation* operation, int internalFilterPropertyIndex)
421 {
422     RetainPtr<id> value = PlatformCAFilters::filterValueForOperation(operation, internalFilterPropertyIndex);
423     [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:value.get()];
424 }
425
426 void PlatformCAAnimationCocoa::copyFromValueFrom(const PlatformCAAnimation& value)
427 {
428     if (!isBasicAnimation() || !value.isBasicAnimation())
429         return;
430
431     CABasicAnimation* otherAnimation = static_cast<CABasicAnimation*>(downcast<PlatformCAAnimationCocoa>(value).m_animation.get());
432     [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:[otherAnimation fromValue]];
433 }
434
435 void PlatformCAAnimationCocoa::setToValue(float value)
436 {
437     if (!isBasicAnimation())
438         return;
439     [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:[NSNumber numberWithDouble:value]];
440 }
441
442 void PlatformCAAnimationCocoa::setToValue(const WebCore::TransformationMatrix& value)
443 {
444     if (!isBasicAnimation())
445         return;
446
447     [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:[NSValue valueWithCATransform3D:value]];
448 }
449
450 void PlatformCAAnimationCocoa::setToValue(const FloatPoint3D& value)
451 {
452     if (!isBasicAnimation())
453         return;
454
455     NSArray* array = [NSArray arrayWithObjects:
456         [NSNumber numberWithDouble:value.x()],
457         [NSNumber numberWithDouble:value.y()],
458         [NSNumber numberWithDouble:value.z()],
459         nil];
460     [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:array];
461 }
462
463 void PlatformCAAnimationCocoa::setToValue(const WebCore::Color& value)
464 {
465     if (!isBasicAnimation())
466         return;
467
468     NSArray* array = [NSArray arrayWithObjects:
469         [NSNumber numberWithDouble:value.red()],
470         [NSNumber numberWithDouble:value.green()],
471         [NSNumber numberWithDouble:value.blue()],
472         [NSNumber numberWithDouble:value.alpha()],
473         nil];
474     [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:array];
475 }
476
477 void PlatformCAAnimationCocoa::setToValue(const FilterOperation* operation, int internalFilterPropertyIndex)
478 {
479     RetainPtr<id> value = PlatformCAFilters::filterValueForOperation(operation, internalFilterPropertyIndex);
480     [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:value.get()];
481 }
482
483 void PlatformCAAnimationCocoa::copyToValueFrom(const PlatformCAAnimation& value)
484 {
485     if (!isBasicAnimation() || !value.isBasicAnimation())
486         return;
487
488     CABasicAnimation* otherAnimation = static_cast<CABasicAnimation*>(downcast<PlatformCAAnimationCocoa>(value).m_animation.get());
489     [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:[otherAnimation toValue]];
490 }
491
492
493 // Keyframe-animation properties.
494 void PlatformCAAnimationCocoa::setValues(const Vector<float>& value)
495 {
496     if (animationType() != Keyframe)
497         return;
498
499     NSMutableArray* array = [NSMutableArray array];
500     for (size_t i = 0; i < value.size(); ++i)
501         [array addObject:[NSNumber numberWithDouble:value[i]]];
502     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array];
503 }
504
505 void PlatformCAAnimationCocoa::setValues(const Vector<WebCore::TransformationMatrix>& value)
506 {
507     if (animationType() != Keyframe)
508         return;
509
510     NSMutableArray* array = [NSMutableArray array];
511
512     for (size_t i = 0; i < value.size(); ++i)
513         [array addObject:[NSValue valueWithCATransform3D:value[i]]];
514
515     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array];
516 }
517
518 void PlatformCAAnimationCocoa::setValues(const Vector<FloatPoint3D>& value)
519 {
520     if (animationType() != Keyframe)
521         return;
522
523     NSMutableArray* array = [NSMutableArray array];
524
525     for (size_t i = 0; i < value.size(); ++i) {
526         NSArray* object = [NSArray arrayWithObjects:
527             [NSNumber numberWithDouble:value[i].x()],
528             [NSNumber numberWithDouble:value[i].y()],
529             [NSNumber numberWithDouble:value[i].z()],
530             nil];
531         [array addObject:object];
532     }
533     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array];
534 }
535
536 void PlatformCAAnimationCocoa::setValues(const Vector<WebCore::Color>& value)
537 {
538     if (animationType() != Keyframe)
539         return;
540
541     NSMutableArray* array = [NSMutableArray array];
542
543     for (size_t i = 0; i < value.size(); ++i) {
544         NSArray* object = [NSArray arrayWithObjects:
545             [NSNumber numberWithDouble:value[i].red()],
546             [NSNumber numberWithDouble:value[i].green()],
547             [NSNumber numberWithDouble:value[i].blue()],
548             [NSNumber numberWithDouble:value[i].alpha()],
549             nil];
550         [array addObject:object];
551     }
552     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array];
553 }
554
555 void PlatformCAAnimationCocoa::setValues(const Vector<RefPtr<FilterOperation>>& values, int internalFilterPropertyIndex)
556 {
557     if (animationType() != Keyframe)
558         return;
559
560     NSMutableArray* array = [NSMutableArray array];
561
562     for (size_t i = 0; i < values.size(); ++i) {
563         RetainPtr<id> value = PlatformCAFilters::filterValueForOperation(values[i].get(), internalFilterPropertyIndex);
564         [array addObject:value.get()];
565     }
566     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array];
567 }
568
569 void PlatformCAAnimationCocoa::copyValuesFrom(const PlatformCAAnimation& value)
570 {
571     if (animationType() != Keyframe || value.animationType() != Keyframe)
572         return;
573
574     CAKeyframeAnimation* otherAnimation = static_cast<CAKeyframeAnimation*>(downcast<PlatformCAAnimationCocoa>(value).m_animation.get());
575     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:[otherAnimation values]];
576 }
577
578 void PlatformCAAnimationCocoa::setKeyTimes(const Vector<float>& value)
579 {
580     NSMutableArray* array = [NSMutableArray array];
581
582     for (size_t i = 0; i < value.size(); ++i)
583         [array addObject:[NSNumber numberWithFloat:value[i]]];
584
585     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setKeyTimes:array];
586 }
587
588 void PlatformCAAnimationCocoa::copyKeyTimesFrom(const PlatformCAAnimation& value)
589 {
590     CAKeyframeAnimation* other = static_cast<CAKeyframeAnimation*>(downcast<PlatformCAAnimationCocoa>(value).m_animation.get());
591     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setKeyTimes:[other keyTimes]];
592 }
593
594 void PlatformCAAnimationCocoa::setTimingFunctions(const Vector<const TimingFunction*>& value, bool reverse)
595 {
596     NSMutableArray* array = [NSMutableArray array];
597
598     for (size_t i = 0; i < value.size(); ++i)
599         [array addObject:toCAMediaTimingFunction(value[i], reverse)];
600
601     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setTimingFunctions:array];
602 }
603
604 void PlatformCAAnimationCocoa::copyTimingFunctionsFrom(const PlatformCAAnimation& value)
605 {
606     CAKeyframeAnimation* other = static_cast<CAKeyframeAnimation*>(downcast<PlatformCAAnimationCocoa>(value).m_animation.get());
607     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setTimingFunctions:[other timingFunctions]];
608 }