1/*
2 * Copyright (C) 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2012, 2013 Adobe Systems Incorporated. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1.  Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15 *     its contributors may be used to endorse or promote products derived
16 *     from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "CSSPropertyAnimation.h"
32
33#include "AnimationBase.h"
34#include "CSSComputedStyleDeclaration.h"
35#include "CSSCrossfadeValue.h"
36#include "CSSFilterImageValue.h"
37#include "CSSImageGeneratorValue.h"
38#include "CSSImageValue.h"
39#include "CSSPrimitiveValue.h"
40#include "CSSPropertyNames.h"
41#include "CachedImage.h"
42#include "ClipPathOperation.h"
43#include "FloatConversion.h"
44#include "IdentityTransformOperation.h"
45#include "Matrix3DTransformOperation.h"
46#include "MatrixTransformOperation.h"
47#include "RenderBox.h"
48#include "RenderStyle.h"
49#include "StyleCachedImage.h"
50#include "StyleGeneratedImage.h"
51#include "StylePropertyShorthand.h"
52#include "StyleResolver.h"
53#include <algorithm>
54#include <memory>
55#include <wtf/MathExtras.h>
56#include <wtf/Noncopyable.h>
57#include <wtf/RefCounted.h>
58
59namespace WebCore {
60
61static inline int blendFunc(const AnimationBase*, int from, int to, double progress)
62{
63    return blend(from, to, progress);
64}
65
66static inline double blendFunc(const AnimationBase*, double from, double to, double progress)
67{
68    return blend(from, to, progress);
69}
70
71static inline float blendFunc(const AnimationBase*, float from, float to, double progress)
72{
73    return narrowPrecisionToFloat(from + (to - from) * progress);
74}
75
76static inline Color blendFunc(const AnimationBase*, const Color& from, const Color& to, double progress)
77{
78    return blend(from, to, progress);
79}
80
81static inline Length blendFunc(const AnimationBase*, const Length& from, const Length& to, double progress)
82{
83    return to.blend(from, narrowPrecisionToFloat(progress));
84}
85
86static inline LengthSize blendFunc(const AnimationBase* anim, const LengthSize& from, const LengthSize& to, double progress)
87{
88    return LengthSize(blendFunc(anim, from.width(), to.width(), progress),
89                      blendFunc(anim, from.height(), to.height(), progress));
90}
91
92static inline ShadowStyle blendFunc(const AnimationBase* anim, ShadowStyle from, ShadowStyle to, double progress)
93{
94    if (from == to)
95        return to;
96
97    double fromVal = from == Normal ? 1 : 0;
98    double toVal = to == Normal ? 1 : 0;
99    double result = blendFunc(anim, fromVal, toVal, progress);
100    return result > 0 ? Normal : Inset;
101}
102
103static inline std::unique_ptr<ShadowData> blendFunc(const AnimationBase* anim, const ShadowData* from, const ShadowData* to, double progress)
104{
105    ASSERT(from && to);
106    if (from->style() != to->style())
107        return std::make_unique<ShadowData>(*to);
108
109    return std::make_unique<ShadowData>(blend(from->location(), to->location(), progress),
110        blend(from->radius(), to->radius(), progress),
111        blend(from->spread(), to->spread(), progress),
112        blendFunc(anim, from->style(), to->style(), progress),
113        from->isWebkitBoxShadow(),
114        blend(from->color(), to->color(), progress));
115}
116
117static inline TransformOperations blendFunc(const AnimationBase* anim, const TransformOperations& from, const TransformOperations& to, double progress)
118{
119    if (anim->isTransformFunctionListValid())
120        return to.blendByMatchingOperations(from, progress);
121    return to.blendByUsingMatrixInterpolation(from, progress, anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : LayoutSize());
122}
123
124static inline PassRefPtr<ClipPathOperation> blendFunc(const AnimationBase*, ClipPathOperation* from, ClipPathOperation* to, double progress)
125{
126    if (!from || !to)
127        return to;
128
129    // Other clip-path operations than BasicShapes can not be animated.
130    if (from->type() != ClipPathOperation::Shape || to->type() != ClipPathOperation::Shape)
131        return to;
132
133    const BasicShape* fromShape = static_cast<ShapeClipPathOperation*>(from)->basicShape();
134    const BasicShape* toShape = static_cast<ShapeClipPathOperation*>(to)->basicShape();
135
136    if (!fromShape->canBlend(toShape))
137        return to;
138
139    return ShapeClipPathOperation::create(toShape->blend(fromShape, progress));
140}
141
142#if ENABLE(CSS_SHAPES)
143static inline PassRefPtr<ShapeValue> blendFunc(const AnimationBase*, ShapeValue* from, ShapeValue* to, double progress)
144{
145    if (!from || !to)
146        return to;
147
148    if (from->type() != ShapeValue::Type::Shape || to->type() != ShapeValue::Type::Shape)
149        return to;
150
151    if (from->cssBox() != to->cssBox())
152        return to;
153
154    const BasicShape* fromShape = from->shape();
155    const BasicShape* toShape = to->shape();
156
157    if (!fromShape->canBlend(toShape))
158        return to;
159
160    return ShapeValue::createShapeValue(toShape->blend(fromShape, progress), to->cssBox());
161}
162#endif
163
164#if ENABLE(CSS_FILTERS)
165static inline PassRefPtr<FilterOperation> blendFunc(const AnimationBase* anim, FilterOperation* fromOp, FilterOperation* toOp, double progress, bool blendToPassthrough = false)
166{
167    ASSERT(toOp);
168    if (toOp->blendingNeedsRendererSize()) {
169        LayoutSize size = anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : LayoutSize();
170        return toOp->blend(fromOp, progress, size, blendToPassthrough);
171    }
172    return toOp->blend(fromOp, progress, blendToPassthrough);
173}
174
175static inline FilterOperations blendFilterOperations(const AnimationBase* anim,  const FilterOperations& from, const FilterOperations& to, double progress)
176{
177    FilterOperations result;
178    size_t fromSize = from.operations().size();
179    size_t toSize = to.operations().size();
180    size_t size = std::max(fromSize, toSize);
181    for (size_t i = 0; i < size; i++) {
182        RefPtr<FilterOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0;
183        RefPtr<FilterOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0;
184        RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(anim, fromOp.get(), toOp.get(), progress) : (fromOp ? blendFunc(anim, 0, fromOp.get(), progress, true) : 0);
185        if (blendedOp)
186            result.operations().append(blendedOp);
187        else {
188            RefPtr<FilterOperation> identityOp = PassthroughFilterOperation::create();
189            if (progress > 0.5)
190                result.operations().append(toOp ? toOp : identityOp);
191            else
192                result.operations().append(fromOp ? fromOp : identityOp);
193        }
194    }
195    return result;
196}
197
198static inline FilterOperations blendFunc(const AnimationBase* anim, const FilterOperations& from, const FilterOperations& to, double progress)
199{
200    FilterOperations result;
201
202    // If we have a filter function list, use that to do a per-function animation.
203    if (anim->filterFunctionListsMatch())
204        result = blendFilterOperations(anim, from, to, progress);
205    else {
206        // If the filter function lists don't match, we could try to cross-fade, but don't yet have a way to represent that in CSS.
207        // For now we'll just fail to animate.
208        result = to;
209    }
210
211    return result;
212}
213
214static inline PassRefPtr<StyleImage> blendFilter(const AnimationBase* anim, CachedImage* image, const FilterOperations& from, const FilterOperations& to, double progress)
215{
216    ASSERT(image);
217    FilterOperations filterResult = blendFilterOperations(anim, from, to, progress);
218
219    RefPtr<StyleCachedImage> styledImage = StyleCachedImage::create(image);
220    auto imageValue = CSSImageValue::create(image->url(), styledImage.get());
221    auto filterValue = ComputedStyleExtractor::valueForFilter(&anim->renderer()->style(), filterResult, DoNotAdjustPixelValues);
222
223    auto result = CSSFilterImageValue::create(WTF::move(imageValue), WTF::move(filterValue));
224    result.get().setFilterOperations(filterResult);
225    return StyleGeneratedImage::create(WTF::move(result));
226}
227#endif // ENABLE(CSS_FILTERS)
228
229static inline EVisibility blendFunc(const AnimationBase* anim, EVisibility from, EVisibility to, double progress)
230{
231    // Any non-zero result means we consider the object to be visible. Only at 0 do we consider the object to be
232    // invisible. The invisible value we use (HIDDEN vs. COLLAPSE) depends on the specified from/to values.
233    double fromVal = from == VISIBLE ? 1. : 0.;
234    double toVal = to == VISIBLE ? 1. : 0.;
235    if (fromVal == toVal)
236        return to;
237    double result = blendFunc(anim, fromVal, toVal, progress);
238    return result > 0. ? VISIBLE : (to != VISIBLE ? to : from);
239}
240
241static inline LengthBox blendFunc(const AnimationBase* anim, const LengthBox& from, const LengthBox& to, double progress)
242{
243    LengthBox result(blendFunc(anim, from.top(), to.top(), progress),
244                     blendFunc(anim, from.right(), to.right(), progress),
245                     blendFunc(anim, from.bottom(), to.bottom(), progress),
246                     blendFunc(anim, from.left(), to.left(), progress));
247    return result;
248}
249
250static inline SVGLength blendFunc(const AnimationBase*, const SVGLength& from, const SVGLength& to, double progress)
251{
252    return to.blend(from, narrowPrecisionToFloat(progress));
253}
254static inline Vector<SVGLength> blendFunc(const AnimationBase*, const Vector<SVGLength>& from, const Vector<SVGLength>& to, double progress)
255{
256    size_t fromLength = from.size();
257    size_t toLength = to.size();
258    if (!fromLength)
259        return !progress ? from : to;
260    if (!toLength)
261        return progress == 1 ? from : to;
262    size_t resultLength = fromLength;
263    if (fromLength != toLength) {
264        if (!remainder(std::max(fromLength, toLength), std::min(fromLength, toLength)))
265            resultLength = std::max(fromLength, toLength);
266        else
267            resultLength = fromLength * toLength;
268    }
269    Vector<SVGLength> result(resultLength);
270    for (size_t i = 0; i < resultLength; ++i)
271        result[i] = to[i % toLength].blend(from[i % fromLength], narrowPrecisionToFloat(progress));
272    return result;
273}
274
275static inline PassRefPtr<StyleImage> crossfadeBlend(const AnimationBase*, StyleCachedImage* fromStyleImage, StyleCachedImage* toStyleImage, double progress)
276{
277    // If progress is at one of the extremes, we want getComputedStyle to show the image,
278    // not a completed cross-fade, so we hand back one of the existing images.
279    if (!progress)
280        return fromStyleImage;
281    if (progress == 1)
282        return toStyleImage;
283
284    auto fromImageValue = CSSImageValue::create(fromStyleImage->cachedImage()->url(), fromStyleImage);
285    auto toImageValue = CSSImageValue::create(toStyleImage->cachedImage()->url(), toStyleImage);
286
287    auto crossfadeValue = CSSCrossfadeValue::create(WTF::move(fromImageValue), WTF::move(toImageValue));
288    crossfadeValue.get().setPercentage(CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER));
289    return StyleGeneratedImage::create(WTF::move(crossfadeValue));
290}
291
292static inline PassRefPtr<StyleImage> blendFunc(const AnimationBase* anim, StyleImage* from, StyleImage* to, double progress)
293{
294    if (!from || !to)
295        return to;
296
297    // Animation between two generated images. Cross fade for all other cases.
298    if (from->isGeneratedImage() && to->isGeneratedImage()) {
299        CSSImageGeneratorValue& fromGenerated = toStyleGeneratedImage(from)->imageValue();
300        CSSImageGeneratorValue& toGenerated = toStyleGeneratedImage(to)->imageValue();
301
302#if ENABLE(CSS_FILTERS)
303        if (fromGenerated.isFilterImageValue() && toGenerated.isFilterImageValue()) {
304            // Animation of generated images just possible if input images are equal.
305            // Otherwise fall back to cross fade animation.
306            CSSFilterImageValue& fromFilter = toCSSFilterImageValue(fromGenerated);
307            CSSFilterImageValue& toFilter = toCSSFilterImageValue(toGenerated);
308            if (fromFilter.equalInputImages(toFilter) && fromFilter.cachedImage())
309                return blendFilter(anim, fromFilter.cachedImage(), fromFilter.filterOperations(), toFilter.filterOperations(), progress);
310        }
311#endif
312
313        if (fromGenerated.isCrossfadeValue() && toGenerated.isCrossfadeValue()) {
314            CSSCrossfadeValue& fromCrossfade = toCSSCrossfadeValue(fromGenerated);
315            CSSCrossfadeValue& toCrossfade = toCSSCrossfadeValue(toGenerated);
316            if (fromCrossfade.equalInputImages(toCrossfade))
317                return StyleGeneratedImage::create(*toCrossfade.blend(fromCrossfade, progress));
318        }
319
320        // FIXME: Add support for animation between two *gradient() functions.
321        // https://bugs.webkit.org/show_bug.cgi?id=119956
322#if ENABLE(CSS_FILTERS)
323    } else if (from->isGeneratedImage() && to->isCachedImage()) {
324        CSSImageGeneratorValue& fromGenerated = toStyleGeneratedImage(from)->imageValue();
325        if (fromGenerated.isFilterImageValue()) {
326            CSSFilterImageValue& fromFilter = toCSSFilterImageValue(fromGenerated);
327            if (fromFilter.cachedImage() && toStyleCachedImage(to)->cachedImage() == fromFilter.cachedImage())
328                return blendFilter(anim, fromFilter.cachedImage(), fromFilter.filterOperations(), FilterOperations(), progress);
329        }
330        // FIXME: Add interpolation between cross-fade and image source.
331    } else if (from->isCachedImage() && to->isGeneratedImage()) {
332        CSSImageGeneratorValue& toGenerated = toStyleGeneratedImage(to)->imageValue();
333        if (toGenerated.isFilterImageValue()) {
334            CSSFilterImageValue& toFilter = toCSSFilterImageValue(toGenerated);
335            if (toFilter.cachedImage() && toStyleCachedImage(from)->cachedImage() == toFilter.cachedImage())
336                return blendFilter(anim, toFilter.cachedImage(), FilterOperations(), toFilter.filterOperations(), progress);
337        }
338#endif
339        // FIXME: Add interpolation between image source and cross-fade.
340    }
341
342    // FIXME: Add support cross fade between cached and generated images.
343    // https://bugs.webkit.org/show_bug.cgi?id=78293
344    if (from->isCachedImage() && to->isCachedImage())
345        return crossfadeBlend(anim, toStyleCachedImage(from), toStyleCachedImage(to), progress);
346
347    return to;
348}
349
350static inline NinePieceImage blendFunc(const AnimationBase* anim, const NinePieceImage& from, const NinePieceImage& to, double progress)
351{
352    if (!from.hasImage() || !to.hasImage())
353        return to;
354
355    // FIXME (74112): Support transitioning between NinePieceImages that differ by more than image content.
356
357    if (from.imageSlices() != to.imageSlices() || from.borderSlices() != to.borderSlices() || from.outset() != to.outset() || from.fill() != to.fill() || from.horizontalRule() != to.horizontalRule() || from.verticalRule() != to.verticalRule())
358        return to;
359
360    if (from.image()->imageSize(anim->renderer(), 1.0) != to.image()->imageSize(anim->renderer(), 1.0))
361        return to;
362
363    RefPtr<StyleImage> newContentImage = blendFunc(anim, from.image(), to.image(), progress);
364
365    return NinePieceImage(newContentImage, from.imageSlices(), from.fill(), from.borderSlices(), from.outset(), from.horizontalRule(), from.verticalRule());
366}
367
368class AnimationPropertyWrapperBase {
369    WTF_MAKE_NONCOPYABLE(AnimationPropertyWrapperBase);
370    WTF_MAKE_FAST_ALLOCATED;
371public:
372    AnimationPropertyWrapperBase(CSSPropertyID prop)
373        : m_prop(prop)
374    {
375    }
376
377    virtual ~AnimationPropertyWrapperBase() { }
378
379    virtual bool isShorthandWrapper() const { return false; }
380    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0;
381    virtual void blend(const AnimationBase*, RenderStyle*, const RenderStyle*, const RenderStyle*, double) const = 0;
382
383    CSSPropertyID property() const { return m_prop; }
384
385    virtual bool animationIsAccelerated() const { return false; }
386
387private:
388    CSSPropertyID m_prop;
389};
390
391template <typename T>
392class PropertyWrapperGetter : public AnimationPropertyWrapperBase {
393public:
394    PropertyWrapperGetter(CSSPropertyID prop, T (RenderStyle::*getter)() const)
395        : AnimationPropertyWrapperBase(prop)
396        , m_getter(getter)
397    {
398    }
399
400    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
401    {
402        // If the style pointers are the same, don't bother doing the test.
403        // If either is null, return false. If both are null, return true.
404        if ((!a && !b) || a == b)
405            return true;
406        if (!a || !b)
407            return false;
408        return (a->*m_getter)() == (b->*m_getter)();
409    }
410
411protected:
412    T (RenderStyle::*m_getter)() const;
413};
414
415template <typename T>
416class PropertyWrapper : public PropertyWrapperGetter<T> {
417public:
418    PropertyWrapper(CSSPropertyID prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T))
419        : PropertyWrapperGetter<T>(prop, getter)
420        , m_setter(setter)
421    {
422    }
423
424    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
425    {
426        (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), progress));
427    }
428
429protected:
430    void (RenderStyle::*m_setter)(T);
431};
432
433template <typename T>
434class RefCountedPropertyWrapper : public PropertyWrapperGetter<T*> {
435public:
436    RefCountedPropertyWrapper(CSSPropertyID prop, T* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<T>))
437        : PropertyWrapperGetter<T*>(prop, getter)
438        , m_setter(setter)
439    {
440    }
441
442    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
443    {
444        (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T*>::m_getter)(), (b->*PropertyWrapperGetter<T*>::m_getter)(), progress));
445    }
446
447protected:
448    void (RenderStyle::*m_setter)(PassRefPtr<T>);
449};
450
451template <typename T>
452class LengthPropertyWrapper : public PropertyWrapperGetter<const T&> {
453public:
454    LengthPropertyWrapper(CSSPropertyID prop, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T))
455        : PropertyWrapperGetter<const T&>(prop, getter)
456        , m_setter(setter)
457    {
458    }
459
460    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
461    {
462        (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const T&>::m_getter)(), (b->*PropertyWrapperGetter<const T&>::m_getter)(), progress));
463    }
464
465protected:
466    void (RenderStyle::*m_setter)(T);
467};
468
469class PropertyWrapperClipPath : public RefCountedPropertyWrapper<ClipPathOperation> {
470public:
471    PropertyWrapperClipPath(CSSPropertyID prop, ClipPathOperation* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<ClipPathOperation>))
472        : RefCountedPropertyWrapper<ClipPathOperation>(prop, getter, setter)
473    {
474    }
475};
476
477#if ENABLE(CSS_SHAPES)
478class PropertyWrapperShape : public RefCountedPropertyWrapper<ShapeValue> {
479public:
480    PropertyWrapperShape(CSSPropertyID prop, ShapeValue* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<ShapeValue>))
481        : RefCountedPropertyWrapper<ShapeValue>(prop, getter, setter)
482    {
483    }
484};
485#endif
486
487class StyleImagePropertyWrapper : public RefCountedPropertyWrapper<StyleImage> {
488public:
489    StyleImagePropertyWrapper(CSSPropertyID prop, StyleImage* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<StyleImage>))
490        : RefCountedPropertyWrapper<StyleImage>(prop, getter, setter)
491    {
492    }
493
494    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
495    {
496       // If the style pointers are the same, don't bother doing the test.
497       // If either is null, return false. If both are null, return true.
498       if (a == b)
499           return true;
500       if (!a || !b)
501            return false;
502
503        StyleImage* imageA = (a->*m_getter)();
504        StyleImage* imageB = (b->*m_getter)();
505        return StyleImage::imagesEquivalent(imageA, imageB);
506    }
507};
508
509class PropertyWrapperColor : public PropertyWrapperGetter<Color> {
510public:
511    PropertyWrapperColor(CSSPropertyID prop, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
512        : PropertyWrapperGetter<Color>(prop, getter)
513        , m_setter(setter)
514    {
515    }
516
517    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
518    {
519        (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<Color>::m_getter)(), (b->*PropertyWrapperGetter<Color>::m_getter)(), progress));
520    }
521
522protected:
523    void (RenderStyle::*m_setter)(const Color&);
524};
525
526
527class PropertyWrapperAcceleratedOpacity : public PropertyWrapper<float> {
528public:
529    PropertyWrapperAcceleratedOpacity()
530        : PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity)
531    {
532    }
533
534    virtual bool animationIsAccelerated() const { return true; }
535
536    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
537    {
538        float fromOpacity = a->opacity();
539
540        // This makes sure we put the object being animated into a RenderLayer during the animation
541        dst->setOpacity(blendFunc(anim, (fromOpacity == 1) ? 0.999999f : fromOpacity, b->opacity(), progress));
542    }
543};
544
545class PropertyWrapperAcceleratedTransform : public PropertyWrapper<const TransformOperations&> {
546public:
547    PropertyWrapperAcceleratedTransform()
548        : PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform)
549    {
550    }
551
552    virtual bool animationIsAccelerated() const { return true; }
553
554    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
555    {
556        dst->setTransform(blendFunc(anim, a->transform(), b->transform(), progress));
557    }
558};
559
560#if ENABLE(CSS_FILTERS)
561class PropertyWrapperAcceleratedFilter : public PropertyWrapper<const FilterOperations&> {
562public:
563    PropertyWrapperAcceleratedFilter()
564        : PropertyWrapper<const FilterOperations&>(CSSPropertyWebkitFilter, &RenderStyle::filter, &RenderStyle::setFilter)
565    {
566    }
567
568    virtual bool animationIsAccelerated() const { return true; }
569
570    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
571    {
572        dst->setFilter(blendFunc(anim, a->filter(), b->filter(), progress));
573    }
574};
575#endif
576
577static inline size_t shadowListLength(const ShadowData* shadow)
578{
579    size_t count;
580    for (count = 0; shadow; shadow = shadow->next())
581        ++count;
582    return count;
583}
584
585static inline const ShadowData* shadowForBlending(const ShadowData* srcShadow, const ShadowData* otherShadow)
586{
587    DEPRECATED_DEFINE_STATIC_LOCAL(ShadowData, defaultShadowData, (IntPoint(), 0, 0, Normal, false, Color::transparent));
588    DEPRECATED_DEFINE_STATIC_LOCAL(ShadowData, defaultInsetShadowData, (IntPoint(), 0, 0, Inset, false, Color::transparent));
589
590    DEPRECATED_DEFINE_STATIC_LOCAL(ShadowData, defaultWebKitBoxShadowData, (IntPoint(), 0, 0, Normal, true, Color::transparent));
591    DEPRECATED_DEFINE_STATIC_LOCAL(ShadowData, defaultInsetWebKitBoxShadowData, (IntPoint(), 0, 0, Inset, true, Color::transparent));
592
593    if (srcShadow)
594        return srcShadow;
595
596    if (otherShadow->style() == Inset)
597        return otherShadow->isWebkitBoxShadow() ? &defaultInsetWebKitBoxShadowData : &defaultInsetShadowData;
598
599    return otherShadow->isWebkitBoxShadow() ? &defaultWebKitBoxShadowData : &defaultShadowData;
600}
601
602class PropertyWrapperShadow : public AnimationPropertyWrapperBase {
603public:
604    PropertyWrapperShadow(CSSPropertyID prop, const ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(std::unique_ptr<ShadowData>, bool))
605        : AnimationPropertyWrapperBase(prop)
606        , m_getter(getter)
607        , m_setter(setter)
608    {
609    }
610
611    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
612    {
613        const ShadowData* shadowA = (a->*m_getter)();
614        const ShadowData* shadowB = (b->*m_getter)();
615
616        while (true) {
617            // end of both lists
618            if (!shadowA && !shadowB)
619                return true;
620
621            // end of just one of the lists
622            if (!shadowA || !shadowB)
623                return false;
624
625            if (*shadowA != *shadowB)
626                return false;
627
628            shadowA = shadowA->next();
629            shadowB = shadowB->next();
630        }
631
632        return true;
633    }
634
635    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
636    {
637        const ShadowData* shadowA = (a->*m_getter)();
638        const ShadowData* shadowB = (b->*m_getter)();
639
640        int fromLength = shadowListLength(shadowA);
641        int toLength = shadowListLength(shadowB);
642
643        if (fromLength == toLength || (fromLength <= 1 && toLength <= 1)) {
644            (dst->*m_setter)(blendSimpleOrMatchedShadowLists(anim, progress, shadowA, shadowB), false);
645            return;
646        }
647
648        (dst->*m_setter)(blendMismatchedShadowLists(anim, progress, shadowA, shadowB, fromLength, toLength), false);
649    }
650
651private:
652    std::unique_ptr<ShadowData> blendSimpleOrMatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB) const
653    {
654        std::unique_ptr<ShadowData> newShadowData;
655        ShadowData* lastShadow = 0;
656
657        while (shadowA || shadowB) {
658            const ShadowData* srcShadow = shadowForBlending(shadowA, shadowB);
659            const ShadowData* dstShadow = shadowForBlending(shadowB, shadowA);
660
661            std::unique_ptr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress);
662            ShadowData* blendedShadowPtr = blendedShadow.get();
663
664            if (!lastShadow)
665                newShadowData = WTF::move(blendedShadow);
666            else
667                lastShadow->setNext(WTF::move(blendedShadow));
668
669            lastShadow = blendedShadowPtr;
670
671            shadowA = shadowA ? shadowA->next() : 0;
672            shadowB = shadowB ? shadowB->next() : 0;
673        }
674
675        return newShadowData;
676    }
677
678    std::unique_ptr<ShadowData> blendMismatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB, int fromLength, int toLength) const
679    {
680        // The shadows in ShadowData are stored in reverse order, so when animating mismatched lists,
681        // reverse them and match from the end.
682        Vector<const ShadowData*, 4> fromShadows(fromLength);
683        for (int i = fromLength - 1; i >= 0; --i) {
684            fromShadows[i] = shadowA;
685            shadowA = shadowA->next();
686        }
687
688        Vector<const ShadowData*, 4> toShadows(toLength);
689        for (int i = toLength - 1; i >= 0; --i) {
690            toShadows[i] = shadowB;
691            shadowB = shadowB->next();
692        }
693
694        std::unique_ptr<ShadowData> newShadowData;
695
696        int maxLength = std::max(fromLength, toLength);
697        for (int i = 0; i < maxLength; ++i) {
698            const ShadowData* fromShadow = i < fromLength ? fromShadows[i] : 0;
699            const ShadowData* toShadow = i < toLength ? toShadows[i] : 0;
700
701            const ShadowData* srcShadow = shadowForBlending(fromShadow, toShadow);
702            const ShadowData* dstShadow = shadowForBlending(toShadow, fromShadow);
703
704            std::unique_ptr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress);
705            // Insert at the start of the list to preserve the order.
706            blendedShadow->setNext(WTF::move(newShadowData));
707            newShadowData = WTF::move(blendedShadow);
708        }
709
710        return newShadowData;
711    }
712
713    const ShadowData* (RenderStyle::*m_getter)() const;
714    void (RenderStyle::*m_setter)(std::unique_ptr<ShadowData>, bool);
715};
716
717class PropertyWrapperMaybeInvalidColor : public AnimationPropertyWrapperBase {
718public:
719    PropertyWrapperMaybeInvalidColor(CSSPropertyID prop, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
720        : AnimationPropertyWrapperBase(prop)
721        , m_getter(getter)
722        , m_setter(setter)
723    {
724    }
725
726    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
727    {
728        Color fromColor = (a->*m_getter)();
729        Color toColor = (b->*m_getter)();
730
731        if (!fromColor.isValid() && !toColor.isValid())
732            return true;
733
734        if (!fromColor.isValid())
735            fromColor = a->color();
736        if (!toColor.isValid())
737            toColor = b->color();
738
739        return fromColor == toColor;
740    }
741
742    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
743    {
744        Color fromColor = (a->*m_getter)();
745        Color toColor = (b->*m_getter)();
746
747        if (!fromColor.isValid() && !toColor.isValid())
748            return;
749
750        if (!fromColor.isValid())
751            fromColor = a->color();
752        if (!toColor.isValid())
753            toColor = b->color();
754        (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress));
755    }
756
757private:
758    Color (RenderStyle::*m_getter)() const;
759    void (RenderStyle::*m_setter)(const Color&);
760};
761
762
763enum MaybeInvalidColorTag { MaybeInvalidColor };
764class PropertyWrapperVisitedAffectedColor : public AnimationPropertyWrapperBase {
765public:
766    PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&),
767                                        Color (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&))
768        : AnimationPropertyWrapperBase(prop)
769        , m_wrapper(std::make_unique<PropertyWrapperColor>(prop, getter, setter))
770        , m_visitedWrapper(std::make_unique<PropertyWrapperColor>(prop, visitedGetter, visitedSetter))
771    {
772    }
773    PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, MaybeInvalidColorTag, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&),
774                                        Color (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&))
775        : AnimationPropertyWrapperBase(prop)
776        , m_wrapper(std::make_unique<PropertyWrapperMaybeInvalidColor>(prop, getter, setter))
777        , m_visitedWrapper(std::make_unique<PropertyWrapperMaybeInvalidColor>(prop, visitedGetter, visitedSetter))
778    {
779    }
780    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
781    {
782        return m_wrapper->equals(a, b) && m_visitedWrapper->equals(a, b);
783    }
784    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
785    {
786        m_wrapper->blend(anim, dst, a, b, progress);
787        m_visitedWrapper->blend(anim, dst, a, b, progress);
788    }
789
790private:
791    std::unique_ptr<AnimationPropertyWrapperBase> m_wrapper;
792    std::unique_ptr<AnimationPropertyWrapperBase> m_visitedWrapper;
793};
794
795// Wrapper base class for an animatable property in a FillLayer
796class FillLayerAnimationPropertyWrapperBase {
797public:
798    FillLayerAnimationPropertyWrapperBase()
799    {
800    }
801
802    virtual ~FillLayerAnimationPropertyWrapperBase() { }
803
804    virtual bool equals(const FillLayer*, const FillLayer*) const = 0;
805    virtual void blend(const AnimationBase*, FillLayer*, const FillLayer*, const FillLayer*, double) const = 0;
806};
807
808template <typename T>
809class FillLayerPropertyWrapperGetter : public FillLayerAnimationPropertyWrapperBase {
810    WTF_MAKE_NONCOPYABLE(FillLayerPropertyWrapperGetter);
811public:
812    FillLayerPropertyWrapperGetter(T (FillLayer::*getter)() const)
813        : m_getter(getter)
814    {
815    }
816
817    virtual bool equals(const FillLayer* a, const FillLayer* b) const
818    {
819       // If the style pointers are the same, don't bother doing the test.
820       // If either is null, return false. If both are null, return true.
821       if ((!a && !b) || a == b)
822           return true;
823       if (!a || !b)
824            return false;
825        return (a->*m_getter)() == (b->*m_getter)();
826    }
827
828protected:
829    T (FillLayer::*m_getter)() const;
830};
831
832template <typename T>
833class FillLayerPropertyWrapper : public FillLayerPropertyWrapperGetter<const T&> {
834public:
835    FillLayerPropertyWrapper(const T& (FillLayer::*getter)() const, void (FillLayer::*setter)(T))
836        : FillLayerPropertyWrapperGetter<const T&>(getter)
837        , m_setter(setter)
838    {
839    }
840
841    virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const
842    {
843        (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<const T&>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<const T&>::m_getter)(), progress));
844    }
845
846protected:
847    void (FillLayer::*m_setter)(T);
848};
849
850template <typename T>
851class FillLayerRefCountedPropertyWrapper : public FillLayerPropertyWrapperGetter<T*> {
852public:
853    FillLayerRefCountedPropertyWrapper(T* (FillLayer::*getter)() const, void (FillLayer::*setter)(PassRefPtr<T>))
854        : FillLayerPropertyWrapperGetter<T*>(getter)
855        , m_setter(setter)
856    {
857    }
858
859    virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const
860    {
861        (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<T*>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<T*>::m_getter)(), progress));
862    }
863
864protected:
865    void (FillLayer::*m_setter)(PassRefPtr<T>);
866};
867
868class FillLayerStyleImagePropertyWrapper : public FillLayerRefCountedPropertyWrapper<StyleImage> {
869public:
870    FillLayerStyleImagePropertyWrapper(StyleImage* (FillLayer::*getter)() const, void (FillLayer::*setter)(PassRefPtr<StyleImage>))
871        : FillLayerRefCountedPropertyWrapper<StyleImage>(getter, setter)
872    {
873    }
874
875    virtual bool equals(const FillLayer* a, const FillLayer* b) const
876    {
877       // If the style pointers are the same, don't bother doing the test.
878       // If either is null, return false. If both are null, return true.
879       if (a == b)
880           return true;
881       if (!a || !b)
882            return false;
883
884        StyleImage* imageA = (a->*m_getter)();
885        StyleImage* imageB = (b->*m_getter)();
886        return StyleImage::imagesEquivalent(imageA, imageB);
887    }
888};
889
890class FillLayersPropertyWrapper : public AnimationPropertyWrapperBase {
891public:
892    typedef const FillLayer* (RenderStyle::*LayersGetter)() const;
893    typedef FillLayer* (RenderStyle::*LayersAccessor)();
894
895    FillLayersPropertyWrapper(CSSPropertyID prop, LayersGetter getter, LayersAccessor accessor)
896        : AnimationPropertyWrapperBase(prop)
897        , m_layersGetter(getter)
898        , m_layersAccessor(accessor)
899    {
900        switch (prop) {
901        case CSSPropertyBackgroundPositionX:
902        case CSSPropertyWebkitMaskPositionX:
903            m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::xPosition, &FillLayer::setXPosition);
904            break;
905        case CSSPropertyBackgroundPositionY:
906        case CSSPropertyWebkitMaskPositionY:
907            m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::yPosition, &FillLayer::setYPosition);
908            break;
909        case CSSPropertyBackgroundSize:
910        case CSSPropertyWebkitBackgroundSize:
911        case CSSPropertyWebkitMaskSize:
912            m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<LengthSize>(&FillLayer::sizeLength, &FillLayer::setSizeLength);
913            break;
914        case CSSPropertyBackgroundImage:
915            m_fillLayerPropertyWrapper = new FillLayerStyleImagePropertyWrapper(&FillLayer::image, &FillLayer::setImage);
916            break;
917        default:
918            break;
919        }
920    }
921
922    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
923    {
924        const FillLayer* fromLayer = (a->*m_layersGetter)();
925        const FillLayer* toLayer = (b->*m_layersGetter)();
926
927        while (fromLayer && toLayer) {
928            if (!m_fillLayerPropertyWrapper->equals(fromLayer, toLayer))
929                return false;
930
931            fromLayer = fromLayer->next();
932            toLayer = toLayer->next();
933        }
934
935        return true;
936    }
937
938    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
939    {
940        const FillLayer* aLayer = (a->*m_layersGetter)();
941        const FillLayer* bLayer = (b->*m_layersGetter)();
942        FillLayer* dstLayer = (dst->*m_layersAccessor)();
943
944        while (aLayer && bLayer && dstLayer) {
945            m_fillLayerPropertyWrapper->blend(anim, dstLayer, aLayer, bLayer, progress);
946            aLayer = aLayer->next();
947            bLayer = bLayer->next();
948            dstLayer = dstLayer->next();
949        }
950    }
951
952private:
953    FillLayerAnimationPropertyWrapperBase* m_fillLayerPropertyWrapper;
954
955    LayersGetter m_layersGetter;
956    LayersAccessor m_layersAccessor;
957};
958
959class ShorthandPropertyWrapper : public AnimationPropertyWrapperBase {
960public:
961    ShorthandPropertyWrapper(CSSPropertyID property, Vector<AnimationPropertyWrapperBase*> longhandWrappers)
962        : AnimationPropertyWrapperBase(property)
963        , m_propertyWrappers(WTF::move(longhandWrappers))
964    {
965    }
966
967    virtual bool isShorthandWrapper() const { return true; }
968
969    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
970    {
971        Vector<AnimationPropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end();
972        for (Vector<AnimationPropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) {
973            if (!(*it)->equals(a, b))
974                return false;
975        }
976        return true;
977    }
978
979    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
980    {
981        Vector<AnimationPropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end();
982        for (Vector<AnimationPropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it)
983            (*it)->blend(anim, dst, a, b, progress);
984    }
985
986    const Vector<AnimationPropertyWrapperBase*>& propertyWrappers() const { return m_propertyWrappers; }
987
988private:
989    Vector<AnimationPropertyWrapperBase*> m_propertyWrappers;
990};
991
992class PropertyWrapperFlex : public AnimationPropertyWrapperBase {
993public:
994    PropertyWrapperFlex() : AnimationPropertyWrapperBase(CSSPropertyWebkitFlex)
995    {
996    }
997
998    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
999    {
1000        // If the style pointers are the same, don't bother doing the test.
1001        // If either is null, return false. If both are null, return true.
1002        if ((!a && !b) || a == b)
1003            return true;
1004        if (!a || !b)
1005            return false;
1006
1007        return a->flexBasis() == b->flexBasis() && a->flexGrow() == b->flexGrow() && a->flexShrink() == b->flexShrink();
1008    }
1009
1010    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
1011    {
1012        dst->setFlexBasis(blendFunc(anim, a->flexBasis(), b->flexBasis(), progress));
1013        dst->setFlexGrow(blendFunc(anim, a->flexGrow(), b->flexGrow(), progress));
1014        dst->setFlexShrink(blendFunc(anim, a->flexShrink(), b->flexShrink(), progress));
1015    }
1016};
1017
1018class PropertyWrapperSVGPaint : public AnimationPropertyWrapperBase {
1019public:
1020    PropertyWrapperSVGPaint(CSSPropertyID prop, const SVGPaint::SVGPaintType& (RenderStyle::*paintTypeGetter)() const, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
1021        : AnimationPropertyWrapperBase(prop)
1022        , m_paintTypeGetter(paintTypeGetter)
1023        , m_getter(getter)
1024        , m_setter(setter)
1025    {
1026    }
1027
1028    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
1029    {
1030        if ((a->*m_paintTypeGetter)() != (b->*m_paintTypeGetter)())
1031            return false;
1032
1033        // We only support animations between SVGPaints that are pure Color values.
1034        // For everything else we must return true for this method, otherwise
1035        // we will try to animate between values forever.
1036        if ((a->*m_paintTypeGetter)() == SVGPaint::SVG_PAINTTYPE_RGBCOLOR) {
1037            Color fromColor = (a->*m_getter)();
1038            Color toColor = (b->*m_getter)();
1039
1040            if (!fromColor.isValid() && !toColor.isValid())
1041                return true;
1042
1043            if (!fromColor.isValid())
1044                fromColor = Color();
1045            if (!toColor.isValid())
1046                toColor = Color();
1047
1048            return fromColor == toColor;
1049        }
1050        return true;
1051    }
1052
1053    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
1054    {
1055        if ((a->*m_paintTypeGetter)() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR
1056            || (b->*m_paintTypeGetter)() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR)
1057            return;
1058
1059        Color fromColor = (a->*m_getter)();
1060        Color toColor = (b->*m_getter)();
1061
1062        if (!fromColor.isValid() && !toColor.isValid())
1063            return;
1064
1065        if (!fromColor.isValid())
1066            fromColor = Color();
1067        if (!toColor.isValid())
1068            toColor = Color();
1069        (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress));
1070    }
1071
1072private:
1073    const SVGPaint::SVGPaintType& (RenderStyle::*m_paintTypeGetter)() const;
1074    Color (RenderStyle::*m_getter)() const;
1075    void (RenderStyle::*m_setter)(const Color&);
1076};
1077
1078class CSSPropertyAnimationWrapperMap {
1079public:
1080    static CSSPropertyAnimationWrapperMap& instance()
1081    {
1082        // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed?
1083        static NeverDestroyed<CSSPropertyAnimationWrapperMap> map;
1084        return map;
1085    }
1086
1087    AnimationPropertyWrapperBase* wrapperForProperty(CSSPropertyID propertyID)
1088    {
1089        if (propertyID < firstCSSProperty || propertyID > lastCSSProperty)
1090            return 0;
1091
1092        unsigned wrapperIndex = indexFromPropertyID(propertyID);
1093        if (wrapperIndex == cInvalidPropertyWrapperIndex)
1094            return 0;
1095
1096        return m_propertyWrappers[wrapperIndex].get();
1097    }
1098
1099    AnimationPropertyWrapperBase* wrapperForIndex(unsigned index)
1100    {
1101        ASSERT(index < m_propertyWrappers.size());
1102        return m_propertyWrappers[index].get();
1103    }
1104
1105    unsigned size()
1106    {
1107        return m_propertyWrappers.size();
1108    }
1109
1110private:
1111    CSSPropertyAnimationWrapperMap();
1112    unsigned char& indexFromPropertyID(CSSPropertyID propertyID)
1113    {
1114        return m_propertyToIdMap[propertyID - firstCSSProperty];
1115    }
1116
1117    Vector<std::unique_ptr<AnimationPropertyWrapperBase>> m_propertyWrappers;
1118    unsigned char m_propertyToIdMap[numCSSProperties];
1119
1120    static const unsigned char cInvalidPropertyWrapperIndex = UCHAR_MAX;
1121
1122    friend class WTF::NeverDestroyed<CSSPropertyAnimationWrapperMap>;
1123};
1124
1125CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap()
1126{
1127    // build the list of property wrappers to do the comparisons and blends
1128    AnimationPropertyWrapperBase* animatableLonghandPropertyWrappers[] = {
1129        new LengthPropertyWrapper<Length>(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft),
1130        new LengthPropertyWrapper<Length>(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight),
1131        new LengthPropertyWrapper<Length>(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop),
1132        new LengthPropertyWrapper<Length>(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom),
1133
1134        new LengthPropertyWrapper<Length>(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth),
1135        new LengthPropertyWrapper<Length>(CSSPropertyMinWidth, &RenderStyle::minWidth, &RenderStyle::setMinWidth),
1136        new LengthPropertyWrapper<Length>(CSSPropertyMaxWidth, &RenderStyle::maxWidth, &RenderStyle::setMaxWidth),
1137
1138        new LengthPropertyWrapper<Length>(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight),
1139        new LengthPropertyWrapper<Length>(CSSPropertyMinHeight, &RenderStyle::minHeight, &RenderStyle::setMinHeight),
1140        new LengthPropertyWrapper<Length>(CSSPropertyMaxHeight, &RenderStyle::maxHeight, &RenderStyle::setMaxHeight),
1141
1142        new PropertyWrapperFlex(),
1143
1144        new PropertyWrapper<float>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth),
1145        new PropertyWrapper<float>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth),
1146        new PropertyWrapper<float>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth),
1147        new PropertyWrapper<float>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth),
1148        new LengthPropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft),
1149        new LengthPropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight),
1150        new LengthPropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop),
1151        new LengthPropertyWrapper<Length>(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom),
1152        new LengthPropertyWrapper<Length>(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft),
1153        new LengthPropertyWrapper<Length>(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight),
1154        new LengthPropertyWrapper<Length>(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop),
1155        new LengthPropertyWrapper<Length>(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom),
1156        new PropertyWrapperVisitedAffectedColor(CSSPropertyColor, &RenderStyle::color, &RenderStyle::setColor, &RenderStyle::visitedLinkColor, &RenderStyle::setVisitedLinkColor),
1157
1158        new PropertyWrapperVisitedAffectedColor(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor, &RenderStyle::visitedLinkBackgroundColor, &RenderStyle::setVisitedLinkBackgroundColor),
1159
1160        new FillLayersPropertyWrapper(CSSPropertyBackgroundImage, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers),
1161        new StyleImagePropertyWrapper(CSSPropertyListStyleImage, &RenderStyle::listStyleImage, &RenderStyle::setListStyleImage),
1162        new StyleImagePropertyWrapper(CSSPropertyWebkitMaskImage, &RenderStyle::maskImage, &RenderStyle::setMaskImage),
1163
1164        new StyleImagePropertyWrapper(CSSPropertyBorderImageSource, &RenderStyle::borderImageSource, &RenderStyle::setBorderImageSource),
1165        new LengthPropertyWrapper<LengthBox>(CSSPropertyBorderImageSlice, &RenderStyle::borderImageSlices, &RenderStyle::setBorderImageSlices),
1166        new LengthPropertyWrapper<LengthBox>(CSSPropertyBorderImageWidth, &RenderStyle::borderImageWidth, &RenderStyle::setBorderImageWidth),
1167        new LengthPropertyWrapper<LengthBox>(CSSPropertyBorderImageOutset, &RenderStyle::borderImageOutset, &RenderStyle::setBorderImageOutset),
1168
1169        new StyleImagePropertyWrapper(CSSPropertyWebkitMaskBoxImageSource, &RenderStyle::maskBoxImageSource, &RenderStyle::setMaskBoxImageSource),
1170        new PropertyWrapper<const NinePieceImage&>(CSSPropertyWebkitMaskBoxImage, &RenderStyle::maskBoxImage, &RenderStyle::setMaskBoxImage),
1171
1172        new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers),
1173        new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers),
1174        new FillLayersPropertyWrapper(CSSPropertyBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers),
1175        new FillLayersPropertyWrapper(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers),
1176
1177        new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers),
1178        new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers),
1179        new FillLayersPropertyWrapper(CSSPropertyWebkitMaskSize, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers),
1180
1181        new PropertyWrapper<float>(CSSPropertyFontSize,
1182            // Must pass a specified size to setFontSize if Text Autosizing is enabled, but a computed size
1183            // if text zoom is enabled (if neither is enabled it's irrelevant as they're probably the same).
1184            // FIXME: Find some way to assert that text zoom isn't activated when Text Autosizing is compiled in.
1185#if ENABLE(TEXT_AUTOSIZING)
1186            &RenderStyle::specifiedFontSize,
1187#else
1188            &RenderStyle::computedFontSize,
1189#endif
1190            &RenderStyle::setFontSize),
1191        new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth),
1192        new PropertyWrapper<float>(CSSPropertyWebkitColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap),
1193        new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount),
1194        new PropertyWrapper<float>(CSSPropertyWebkitColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth),
1195        new PropertyWrapper<short>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing),
1196        new PropertyWrapper<short>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing),
1197        new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex),
1198        new PropertyWrapper<short>(CSSPropertyOrphans, &RenderStyle::orphans, &RenderStyle::setOrphans),
1199        new PropertyWrapper<short>(CSSPropertyWidows, &RenderStyle::widows, &RenderStyle::setWidows),
1200        new LengthPropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::specifiedLineHeight, &RenderStyle::setLineHeight),
1201        new PropertyWrapper<int>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset),
1202        new PropertyWrapper<unsigned short>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth),
1203        new PropertyWrapper<float>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing),
1204        new LengthPropertyWrapper<Length>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing),
1205        new LengthPropertyWrapper<Length>(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent),
1206
1207        new PropertyWrapper<float>(CSSPropertyWebkitPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective),
1208        new LengthPropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX),
1209        new LengthPropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY),
1210        new LengthPropertyWrapper<Length>(CSSPropertyWebkitTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX),
1211        new LengthPropertyWrapper<Length>(CSSPropertyWebkitTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY),
1212        new PropertyWrapper<float>(CSSPropertyWebkitTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ),
1213        new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius),
1214        new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius),
1215        new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius),
1216        new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius),
1217        new PropertyWrapper<EVisibility>(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility),
1218        new PropertyWrapper<float>(CSSPropertyZoom, &RenderStyle::zoom, &RenderStyle::setZoomWithoutReturnValue),
1219
1220        new LengthPropertyWrapper<LengthBox>(CSSPropertyClip, &RenderStyle::clip, &RenderStyle::setClip),
1221
1222        new PropertyWrapperAcceleratedOpacity(),
1223        new PropertyWrapperAcceleratedTransform(),
1224#if ENABLE(CSS_FILTERS)
1225        new PropertyWrapperAcceleratedFilter(),
1226#endif
1227
1228        new PropertyWrapperClipPath(CSSPropertyWebkitClipPath, &RenderStyle::clipPath, &RenderStyle::setClipPath),
1229
1230#if ENABLE(CSS_SHAPES)
1231        new PropertyWrapperShape(CSSPropertyWebkitShapeOutside, &RenderStyle::shapeOutside, &RenderStyle::setShapeOutside),
1232        new LengthPropertyWrapper<Length>(CSSPropertyWebkitShapeMargin, &RenderStyle::shapeMargin, &RenderStyle::setShapeMargin),
1233        new PropertyWrapper<float>(CSSPropertyWebkitShapeImageThreshold, &RenderStyle::shapeImageThreshold, &RenderStyle::setShapeImageThreshold),
1234#endif
1235
1236        new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitColumnRuleColor, MaybeInvalidColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor, &RenderStyle::visitedLinkColumnRuleColor, &RenderStyle::setVisitedLinkColumnRuleColor),
1237        new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitTextStrokeColor, MaybeInvalidColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor, &RenderStyle::visitedLinkTextStrokeColor, &RenderStyle::setVisitedLinkTextStrokeColor),
1238        new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitTextFillColor, MaybeInvalidColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor, &RenderStyle::visitedLinkTextFillColor, &RenderStyle::setVisitedLinkTextFillColor),
1239        new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderLeftColor, MaybeInvalidColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor, &RenderStyle::visitedLinkBorderLeftColor, &RenderStyle::setVisitedLinkBorderLeftColor),
1240        new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderRightColor, MaybeInvalidColor, &RenderStyle::borderRightColor, &RenderStyle::setBorderRightColor, &RenderStyle::visitedLinkBorderRightColor, &RenderStyle::setVisitedLinkBorderRightColor),
1241        new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderTopColor, MaybeInvalidColor, &RenderStyle::borderTopColor, &RenderStyle::setBorderTopColor, &RenderStyle::visitedLinkBorderTopColor, &RenderStyle::setVisitedLinkBorderTopColor),
1242        new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderBottomColor, MaybeInvalidColor, &RenderStyle::borderBottomColor, &RenderStyle::setBorderBottomColor, &RenderStyle::visitedLinkBorderBottomColor, &RenderStyle::setVisitedLinkBorderBottomColor),
1243        new PropertyWrapperVisitedAffectedColor(CSSPropertyOutlineColor, MaybeInvalidColor, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor, &RenderStyle::visitedLinkOutlineColor, &RenderStyle::setVisitedLinkOutlineColor),
1244
1245        new PropertyWrapperShadow(CSSPropertyBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow),
1246        new PropertyWrapperShadow(CSSPropertyWebkitBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow),
1247        new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow),
1248
1249        new PropertyWrapperSVGPaint(CSSPropertyFill, &RenderStyle::fillPaintType, &RenderStyle::fillPaintColor, &RenderStyle::setFillPaintColor),
1250        new PropertyWrapper<float>(CSSPropertyFillOpacity, &RenderStyle::fillOpacity, &RenderStyle::setFillOpacity),
1251
1252        new PropertyWrapperSVGPaint(CSSPropertyStroke, &RenderStyle::strokePaintType, &RenderStyle::strokePaintColor, &RenderStyle::setStrokePaintColor),
1253        new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity),
1254        new PropertyWrapper<SVGLength>(CSSPropertyStrokeWidth, &RenderStyle::strokeWidth, &RenderStyle::setStrokeWidth),
1255        new PropertyWrapper< Vector<SVGLength>>(CSSPropertyStrokeDasharray, &RenderStyle::strokeDashArray, &RenderStyle::setStrokeDashArray),
1256        new PropertyWrapper<SVGLength>(CSSPropertyStrokeDashoffset, &RenderStyle::strokeDashOffset, &RenderStyle::setStrokeDashOffset),
1257        new PropertyWrapper<float>(CSSPropertyStrokeMiterlimit, &RenderStyle::strokeMiterLimit, &RenderStyle::setStrokeMiterLimit),
1258
1259        new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity),
1260        new PropertyWrapperMaybeInvalidColor(CSSPropertyFloodColor, &RenderStyle::floodColor, &RenderStyle::setFloodColor),
1261
1262        new PropertyWrapper<float>(CSSPropertyStopOpacity, &RenderStyle::stopOpacity, &RenderStyle::setStopOpacity),
1263        new PropertyWrapperMaybeInvalidColor(CSSPropertyStopColor, &RenderStyle::stopColor, &RenderStyle::setStopColor),
1264
1265        new PropertyWrapperMaybeInvalidColor(CSSPropertyLightingColor, &RenderStyle::lightingColor, &RenderStyle::setLightingColor),
1266
1267        new PropertyWrapper<SVGLength>(CSSPropertyBaselineShift, &RenderStyle::baselineShiftValue, &RenderStyle::setBaselineShiftValue),
1268        new PropertyWrapper<SVGLength>(CSSPropertyKerning, &RenderStyle::kerning, &RenderStyle::setKerning),
1269    };
1270    const unsigned animatableLonghandPropertiesCount = WTF_ARRAY_LENGTH(animatableLonghandPropertyWrappers);
1271
1272    static const CSSPropertyID animatableShorthandProperties[] = {
1273        CSSPropertyBackground, // for background-color, background-position, background-image
1274        CSSPropertyBackgroundPosition,
1275        CSSPropertyFont, // for font-size, font-weight
1276        CSSPropertyWebkitMask, // for mask-position
1277        CSSPropertyWebkitMaskPosition,
1278        CSSPropertyBorderTop, CSSPropertyBorderRight, CSSPropertyBorderBottom, CSSPropertyBorderLeft,
1279        CSSPropertyBorderColor,
1280        CSSPropertyBorderRadius,
1281        CSSPropertyBorderWidth,
1282        CSSPropertyBorder,
1283        CSSPropertyBorderImage,
1284        CSSPropertyBorderSpacing,
1285        CSSPropertyListStyle, // for list-style-image
1286        CSSPropertyMargin,
1287        CSSPropertyOutline,
1288        CSSPropertyPadding,
1289        CSSPropertyWebkitTextStroke,
1290        CSSPropertyWebkitColumnRule,
1291        CSSPropertyWebkitBorderRadius,
1292        CSSPropertyWebkitTransformOrigin
1293    };
1294    const unsigned animatableShorthandPropertiesCount = WTF_ARRAY_LENGTH(animatableShorthandProperties);
1295
1296    // TODO:
1297    //
1298    //  CSSPropertyVerticalAlign
1299    //
1300    // Compound properties that have components that should be animatable:
1301    //
1302    //  CSSPropertyWebkitColumns
1303    //  CSSPropertyWebkitBoxReflect
1304
1305    // Make sure unused slots have a value
1306    for (int i = 0; i < numCSSProperties; ++i)
1307        m_propertyToIdMap[i] = cInvalidPropertyWrapperIndex;
1308
1309    COMPILE_ASSERT(animatableLonghandPropertiesCount + animatableShorthandPropertiesCount < UCHAR_MAX, numberOfAnimatablePropertiesMustBeLessThanUCharMax);
1310    m_propertyWrappers.reserveInitialCapacity(animatableLonghandPropertiesCount + animatableShorthandPropertiesCount);
1311
1312    // First we put the non-shorthand property wrappers into the map, so the shorthand-building
1313    // code can find them.
1314
1315    for (unsigned i = 0; i < animatableLonghandPropertiesCount; ++i) {
1316        AnimationPropertyWrapperBase* wrapper = animatableLonghandPropertyWrappers[i];
1317        m_propertyWrappers.uncheckedAppend(std::unique_ptr<AnimationPropertyWrapperBase>(wrapper));
1318        indexFromPropertyID(wrapper->property()) = i;
1319    }
1320
1321    for (size_t i = 0; i < animatableShorthandPropertiesCount; ++i) {
1322        CSSPropertyID propertyID = animatableShorthandProperties[i];
1323        StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
1324        if (!shorthand.length())
1325            continue;
1326
1327        Vector<AnimationPropertyWrapperBase*> longhandWrappers;
1328        longhandWrappers.reserveInitialCapacity(shorthand.length());
1329        const CSSPropertyID* properties = shorthand.properties();
1330        for (unsigned j = 0; j < shorthand.length(); ++j) {
1331            unsigned wrapperIndex = indexFromPropertyID(properties[j]);
1332            if (wrapperIndex == cInvalidPropertyWrapperIndex)
1333                continue;
1334            ASSERT(m_propertyWrappers[wrapperIndex]);
1335            longhandWrappers.uncheckedAppend(m_propertyWrappers[wrapperIndex].get());
1336        }
1337
1338        m_propertyWrappers.uncheckedAppend(std::make_unique<ShorthandPropertyWrapper>(propertyID, WTF::move(longhandWrappers)));
1339        indexFromPropertyID(propertyID) = animatableLonghandPropertiesCount + i;
1340    }
1341}
1342
1343static bool gatherEnclosingShorthandProperties(CSSPropertyID property, AnimationPropertyWrapperBase* wrapper, HashSet<CSSPropertyID>& propertySet)
1344{
1345    if (!wrapper->isShorthandWrapper())
1346        return false;
1347
1348    ShorthandPropertyWrapper* shorthandWrapper = static_cast<ShorthandPropertyWrapper*>(wrapper);
1349    bool contained = false;
1350    for (auto& currWrapper : shorthandWrapper->propertyWrappers()) {
1351        if (gatherEnclosingShorthandProperties(property, currWrapper, propertySet) || currWrapper->property() == property)
1352            contained = true;
1353    }
1354
1355    if (contained)
1356        propertySet.add(wrapper->property());
1357
1358    return contained;
1359}
1360
1361// Returns true if we need to start animation timers
1362bool CSSPropertyAnimation::blendProperties(const AnimationBase* anim, CSSPropertyID prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress)
1363{
1364    ASSERT(prop != CSSPropertyInvalid);
1365
1366    AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::instance().wrapperForProperty(prop);
1367    if (wrapper) {
1368        wrapper->blend(anim, dst, a, b, progress);
1369        return !wrapper->animationIsAccelerated() || !anim->isAccelerated();
1370    }
1371
1372    return false;
1373}
1374
1375bool CSSPropertyAnimation::animationOfPropertyIsAccelerated(CSSPropertyID prop)
1376{
1377    AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::instance().wrapperForProperty(prop);
1378    return wrapper ? wrapper->animationIsAccelerated() : false;
1379}
1380
1381// Note: this is inefficient. It's only called from pauseTransitionAtTime().
1382HashSet<CSSPropertyID> CSSPropertyAnimation::animatableShorthandsAffectingProperty(CSSPropertyID property)
1383{
1384    CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::instance();
1385
1386    HashSet<CSSPropertyID> foundProperties;
1387    for (unsigned i = 0; i < map.size(); ++i)
1388        gatherEnclosingShorthandProperties(property, map.wrapperForIndex(i), foundProperties);
1389
1390    return foundProperties;
1391}
1392
1393bool CSSPropertyAnimation::propertiesEqual(CSSPropertyID prop, const RenderStyle* a, const RenderStyle* b)
1394{
1395    AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::instance().wrapperForProperty(prop);
1396    if (wrapper)
1397        return wrapper->equals(a, b);
1398    return true;
1399}
1400
1401CSSPropertyID CSSPropertyAnimation::getPropertyAtIndex(int i, bool& isShorthand)
1402{
1403    CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::instance();
1404
1405    if (i < 0 || static_cast<unsigned>(i) >= map.size())
1406        return CSSPropertyInvalid;
1407
1408    AnimationPropertyWrapperBase* wrapper = map.wrapperForIndex(i);
1409    isShorthand = wrapper->isShorthandWrapper();
1410    return wrapper->property();
1411}
1412
1413int CSSPropertyAnimation::getNumProperties()
1414{
1415    return CSSPropertyAnimationWrapperMap::instance().size();
1416}
1417
1418}
1419