1/*
2 * Copyright (C) Research In Motion Limited 2011-2012. All rights reserved.
3 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "core/svg/SVGAnimatedTypeAnimator.h"
23
24#include "core/css/parser/CSSParser.h"
25#include "core/svg/SVGAnimateTransformElement.h"
26#include "core/svg/SVGAnimatedColor.h"
27#include "core/svg/SVGAnimationElement.h"
28#include "core/svg/SVGElement.h"
29#include "core/svg/SVGLength.h"
30#include "core/svg/SVGLengthList.h"
31#include "core/svg/SVGNumber.h"
32#include "core/svg/SVGPointList.h"
33#include "core/svg/SVGString.h"
34#include "core/svg/SVGTransformList.h"
35
36namespace blink {
37
38SVGAnimatedTypeAnimator::SVGAnimatedTypeAnimator(SVGAnimationElement* animationElement, SVGElement* contextElement)
39    : m_animationElement(animationElement)
40    , m_contextElement(contextElement)
41{
42    ASSERT(m_animationElement);
43    ASSERT(m_contextElement);
44
45    const QualifiedName& attributeName = m_animationElement->attributeName();
46    m_animatedProperty = m_contextElement->propertyFromAttribute(attributeName);
47    m_type = m_animatedProperty ? m_animatedProperty->type()
48        : SVGElement::animatedPropertyTypeForCSSAttribute(attributeName);
49
50    // Only <animateTransform> is allowed to animate AnimatedTransformList.
51    // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
52    if (m_type == AnimatedTransformList && !isSVGAnimateTransformElement(*animationElement))
53        m_type = AnimatedUnknown;
54
55    ASSERT(m_type != AnimatedPoint
56        && m_type != AnimatedStringList
57        && m_type != AnimatedTransform);
58}
59
60SVGAnimatedTypeAnimator::~SVGAnimatedTypeAnimator()
61{
62}
63
64PassRefPtr<SVGPropertyBase> SVGAnimatedTypeAnimator::createPropertyForAnimation(const String& value)
65{
66    if (isAnimatingSVGDom()) {
67        // SVG DOM animVal animation code-path.
68
69        if (m_type == AnimatedTransformList) {
70            // TransformList must be animated via <animateTransform>,
71            // and its {from,by,to} attribute values needs to be parsed w.r.t. its "type" attribute.
72            // Spec: http://www.w3.org/TR/SVG/single-page.html#animate-AnimateTransformElement
73            ASSERT(m_animationElement);
74            SVGTransformType transformType = toSVGAnimateTransformElement(m_animationElement)->transformType();
75            return SVGTransformList::create(transformType, value);
76        }
77
78        ASSERT(m_animatedProperty);
79        return m_animatedProperty->currentValueBase()->cloneForAnimation(value);
80    }
81
82    ASSERT(isAnimatingCSSProperty());
83
84    // CSS properties animation code-path.
85    // Create a basic instance of the corresponding SVG property.
86    // The instance will not have full context info. (e.g. SVGLengthMode)
87
88    switch (m_type) {
89    case AnimatedColor:
90        return SVGColorProperty::create(value.isEmpty() ? StyleColor::currentColor() : CSSParser::colorFromRGBColorString(value));
91    case AnimatedNumber: {
92        RefPtr<SVGNumber> property = SVGNumber::create();
93        property->setValueAsString(value, IGNORE_EXCEPTION);
94        return property.release();
95    }
96    case AnimatedLength: {
97        RefPtr<SVGLength> property = SVGLength::create(LengthModeOther);
98        property->setValueAsString(value, IGNORE_EXCEPTION);
99        return property.release();
100    }
101    case AnimatedLengthList: {
102        RefPtr<SVGLengthList> property = SVGLengthList::create(LengthModeOther);
103        property->setValueAsString(value, IGNORE_EXCEPTION);
104        return property.release();
105    }
106    case AnimatedString: {
107        RefPtr<SVGString> property = SVGString::create();
108        property->setValueAsString(value, IGNORE_EXCEPTION);
109        return property.release();
110    }
111
112    // These types don't appear in the table in SVGElement::animatedPropertyTypeForCSSAttribute() and thus don't need support.
113    case AnimatedAngle:
114    case AnimatedBoolean:
115    case AnimatedEnumeration:
116    case AnimatedInteger:
117    case AnimatedIntegerOptionalInteger:
118    case AnimatedNumberList:
119    case AnimatedNumberOptionalNumber:
120    case AnimatedPath:
121    case AnimatedPoint:
122    case AnimatedPoints:
123    case AnimatedPreserveAspectRatio:
124    case AnimatedRect:
125    case AnimatedStringList:
126    case AnimatedTransform:
127    case AnimatedTransformList:
128        ASSERT_NOT_REACHED();
129
130    case AnimatedUnknown:
131        ASSERT_NOT_REACHED();
132    };
133
134    ASSERT_NOT_REACHED();
135    return nullptr;
136}
137
138PassRefPtr<SVGPropertyBase> SVGAnimatedTypeAnimator::constructFromString(const String& value)
139{
140    return createPropertyForAnimation(value);
141}
142
143void SVGAnimatedTypeAnimator::calculateFromAndToValues(RefPtr<SVGPropertyBase>& from, RefPtr<SVGPropertyBase>& to, const String& fromString, const String& toString)
144{
145    from = constructFromString(fromString);
146    to = constructFromString(toString);
147}
148
149void SVGAnimatedTypeAnimator::calculateFromAndByValues(RefPtr<SVGPropertyBase>& from, RefPtr<SVGPropertyBase>& to, const String& fromString, const String& byString)
150{
151    from = constructFromString(fromString);
152    to = constructFromString(byString);
153    // FIXME(oilpan): Below .get() should be removed after transition types are gone.
154    to->add(from.get(), m_contextElement);
155}
156
157namespace {
158
159void setAnimatedValueOnAllTargetProperties(const WillBeHeapVector<RawPtrWillBeMember<SVGElement> >& list, const QualifiedName& attributeName, PassRefPtr<SVGPropertyBase> passValue)
160{
161    RefPtr<SVGPropertyBase> value = passValue;
162
163    WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator it = list.begin();
164    WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator itEnd = list.end();
165    for (; it != itEnd; ++it) {
166        RefPtr<SVGAnimatedPropertyBase> animatedProperty = (*it)->propertyFromAttribute(attributeName);
167        if (animatedProperty)
168            animatedProperty->setAnimatedValue(value);
169    }
170}
171
172}
173
174PassRefPtr<SVGPropertyBase> SVGAnimatedTypeAnimator::resetAnimation(const WillBeHeapVector<RawPtrWillBeMember<SVGElement> >& list)
175{
176    ASSERT(isAnimatingSVGDom());
177    RefPtr<SVGPropertyBase> animatedValue = m_animatedProperty->createAnimatedValue();
178    ASSERT(animatedValue->type() == m_type);
179    setAnimatedValueOnAllTargetProperties(list, m_animatedProperty->attributeName(), animatedValue);
180
181    return animatedValue.release();
182}
183
184PassRefPtr<SVGPropertyBase> SVGAnimatedTypeAnimator::startAnimValAnimation(const WillBeHeapVector<RawPtrWillBeMember<SVGElement> >& list)
185{
186    ASSERT(isAnimatingSVGDom());
187    SVGElement::InstanceUpdateBlocker blocker(m_contextElement);
188
189    return resetAnimation(list);
190}
191
192void SVGAnimatedTypeAnimator::stopAnimValAnimation(const WillBeHeapVector<RawPtrWillBeMember<SVGElement> >& list)
193{
194    ASSERT(isAnimatingSVGDom());
195    SVGElement::InstanceUpdateBlocker blocker(m_contextElement);
196
197    WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator it = list.begin();
198    WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator itEnd = list.end();
199    for (; it != itEnd; ++it) {
200        RefPtr<SVGAnimatedPropertyBase> animatedProperty = (*it)->propertyFromAttribute(m_animatedProperty->attributeName());
201        if (animatedProperty)
202            animatedProperty->animationEnded();
203    }
204}
205
206PassRefPtr<SVGPropertyBase> SVGAnimatedTypeAnimator::resetAnimValToBaseVal(const WillBeHeapVector<RawPtrWillBeMember<SVGElement> >& list)
207{
208    SVGElement::InstanceUpdateBlocker blocker(m_contextElement);
209
210    return resetAnimation(list);
211}
212
213class ParsePropertyFromString {
214public:
215    explicit ParsePropertyFromString(SVGAnimatedTypeAnimator* animator)
216        : m_animator(animator)
217    {
218    }
219
220    PassRefPtr<SVGPropertyBase> operator()(SVGAnimationElement*, const String& value)
221    {
222        return m_animator->createPropertyForAnimation(value);
223    }
224
225private:
226    SVGAnimatedTypeAnimator* m_animator;
227};
228
229void SVGAnimatedTypeAnimator::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGPropertyBase* from, SVGPropertyBase* to, SVGPropertyBase* toAtEndOfDuration, SVGPropertyBase* animated)
230{
231    ASSERT(m_animationElement);
232    ASSERT(m_contextElement);
233
234    RefPtr<SVGPropertyBase> fromValue = m_animationElement->animationMode() == ToAnimation ? animated : from;
235    RefPtr<SVGPropertyBase> toValue = to;
236    RefPtr<SVGPropertyBase> toAtEndOfDurationValue = toAtEndOfDuration;
237    RefPtr<SVGPropertyBase> animatedValue = animated;
238
239    // Apply CSS inheritance rules.
240    ParsePropertyFromString parsePropertyFromString(this);
241    m_animationElement->adjustForInheritance<RefPtr<SVGPropertyBase>, ParsePropertyFromString>(parsePropertyFromString, m_animationElement->fromPropertyValueType(), fromValue, m_contextElement);
242    m_animationElement->adjustForInheritance<RefPtr<SVGPropertyBase>, ParsePropertyFromString>(parsePropertyFromString, m_animationElement->toPropertyValueType(), toValue, m_contextElement);
243
244    animatedValue->calculateAnimatedValue(m_animationElement, percentage, repeatCount, fromValue, toValue, toAtEndOfDurationValue, m_contextElement);
245}
246
247float SVGAnimatedTypeAnimator::calculateDistance(const String& fromString, const String& toString)
248{
249    ASSERT(m_animationElement);
250    ASSERT(m_contextElement);
251    RefPtr<SVGPropertyBase> fromValue = createPropertyForAnimation(fromString);
252    RefPtr<SVGPropertyBase> toValue = createPropertyForAnimation(toString);
253    return fromValue->calculateDistance(toValue, m_contextElement);
254}
255
256void SVGAnimatedTypeAnimator::trace(Visitor* visitor)
257{
258    visitor->trace(m_animationElement);
259    visitor->trace(m_contextElement);
260}
261
262}
263