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