Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / svg / SVGAnimateElement.cpp
1 /*
2  * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4  * Copyright (C) 2008 Apple Inc. All rights reserved.
5  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #include "config.h"
24
25 #include "core/svg/SVGAnimateElement.h"
26
27 #include "CSSPropertyNames.h"
28 #include "core/css/parser/BisonCSSParser.h"
29 #include "core/css/StylePropertySet.h"
30 #include "core/dom/QualifiedName.h"
31 #include "core/svg/SVGAnimatedTypeAnimator.h"
32 #include "core/svg/SVGDocumentExtensions.h"
33 #include "core/svg/SVGElementInstance.h"
34
35 namespace WebCore {
36
37 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document& document)
38     : SVGAnimationElement(tagName, document)
39     , m_animatedPropertyType(AnimatedString)
40 {
41     ASSERT(isSVGAnimateElement(*this));
42     ScriptWrappable::init(this);
43 }
44
45 PassRefPtr<SVGAnimateElement> SVGAnimateElement::create(Document& document)
46 {
47     return adoptRef(new SVGAnimateElement(SVGNames::animateTag, document));
48 }
49
50 SVGAnimateElement::~SVGAnimateElement()
51 {
52 #if !ENABLE(OILPAN)
53     if (targetElement())
54         clearAnimatedType(targetElement());
55 #endif
56 }
57
58 bool SVGAnimateElement::hasValidAttributeType()
59 {
60     SVGElement* targetElement = this->targetElement();
61     if (!targetElement)
62         return false;
63
64     return m_animatedPropertyType != AnimatedUnknown && !hasInvalidCSSAttributeType();
65 }
66
67 void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement* resultElement)
68 {
69     ASSERT(resultElement);
70     SVGElement* targetElement = this->targetElement();
71     if (!targetElement || !isSVGAnimateElement(*resultElement))
72         return;
73
74     ASSERT(m_animatedPropertyType == determineAnimatedPropertyType());
75
76     ASSERT(percentage >= 0 && percentage <= 1);
77     ASSERT(m_animatedPropertyType != AnimatedTransformList || isSVGAnimateTransformElement(*this));
78     ASSERT(m_animatedPropertyType != AnimatedUnknown);
79     ASSERT(m_animator);
80     ASSERT(m_animator->type() == m_animatedPropertyType);
81     ASSERT(m_fromProperty);
82     ASSERT(m_fromProperty->type() == m_animatedPropertyType);
83     ASSERT(m_toProperty);
84
85     SVGAnimateElement* resultAnimationElement = toSVGAnimateElement(resultElement);
86     ASSERT(resultAnimationElement->m_animatedProperty);
87     ASSERT(resultAnimationElement->m_animatedPropertyType == m_animatedPropertyType);
88
89     if (isSVGSetElement(*this))
90         percentage = 1;
91
92     if (calcMode() == CalcModeDiscrete)
93         percentage = percentage < 0.5 ? 0 : 1;
94
95     // Target element might have changed.
96     m_animator->setContextElement(targetElement);
97
98     // Values-animation accumulates using the last values entry corresponding to the end of duration time.
99     SVGPropertyBase* toAtEndOfDurationProperty = m_toAtEndOfDurationProperty ? m_toAtEndOfDurationProperty.get() : m_toProperty.get();
100     m_animator->calculateAnimatedValue(percentage, repeatCount, m_fromProperty.get(), m_toProperty.get(), toAtEndOfDurationProperty, resultAnimationElement->m_animatedProperty.get());
101 }
102
103 bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
104 {
105     if (toAtEndOfDurationString.isEmpty())
106         return false;
107     m_toAtEndOfDurationProperty = ensureAnimator()->constructFromString(toAtEndOfDurationString);
108     return true;
109 }
110
111 bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
112 {
113     SVGElement* targetElement = this->targetElement();
114     if (!targetElement)
115         return false;
116
117     determinePropertyValueTypes(fromString, toString);
118     ensureAnimator()->calculateFromAndToValues(m_fromProperty, m_toProperty, fromString, toString);
119     ASSERT(m_animatedPropertyType == m_animator->type());
120     return true;
121 }
122
123 bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
124 {
125     SVGElement* targetElement = this->targetElement();
126     if (!targetElement)
127         return false;
128
129     if (animationMode() == ByAnimation && !isAdditive())
130         return false;
131
132     // from-by animation may only be used with attributes that support addition (e.g. most numeric attributes).
133     if (animationMode() == FromByAnimation && !animatedPropertyTypeSupportsAddition())
134         return false;
135
136     ASSERT(!isSVGSetElement(*this));
137
138     determinePropertyValueTypes(fromString, byString);
139     ensureAnimator()->calculateFromAndByValues(m_fromProperty, m_toProperty, fromString, byString);
140     ASSERT(m_animatedPropertyType == m_animator->type());
141     return true;
142 }
143
144 namespace {
145
146 Vector<SVGElement*> findElementInstances(SVGElement* targetElement)
147 {
148     ASSERT(targetElement);
149     Vector<SVGElement*> animatedElements;
150
151     animatedElements.append(targetElement);
152
153     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
154     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
155     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
156         if (SVGElement* shadowTreeElement = *it)
157             animatedElements.append(shadowTreeElement);
158     }
159
160     return animatedElements;
161 }
162
163 }
164
165 void SVGAnimateElement::resetAnimatedType()
166 {
167     SVGAnimatedTypeAnimator* animator = ensureAnimator();
168     ASSERT(m_animatedPropertyType == animator->type());
169
170     SVGElement* targetElement = this->targetElement();
171     const QualifiedName& attributeName = this->attributeName();
172     ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName);
173
174     if (shouldApply == DontApplyAnimation)
175         return;
176
177     if (shouldApply == ApplyXMLAnimation) {
178         // SVG DOM animVal animation code-path.
179         m_animatedElements = findElementInstances(targetElement);
180         ASSERT(!m_animatedElements.isEmpty());
181
182         Vector<SVGElement*>::const_iterator end = m_animatedElements.end();
183         for (Vector<SVGElement*>::const_iterator it = m_animatedElements.begin(); it != end; ++it)
184             document().accessSVGExtensions().addElementReferencingTarget(this, *it);
185
186         if (!m_animatedProperty)
187             m_animatedProperty = animator->startAnimValAnimation(m_animatedElements);
188         else
189             m_animatedProperty = animator->resetAnimValToBaseVal(m_animatedElements);
190
191         return;
192     }
193
194     // CSS properties animation code-path.
195     ASSERT(m_animatedElements.isEmpty());
196     String baseValue;
197
198     if (shouldApply == ApplyCSSAnimation) {
199         ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement, attributeName));
200         computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.localName()), baseValue);
201     }
202
203     m_animatedProperty = animator->constructFromString(baseValue);
204 }
205
206 static inline void applyCSSPropertyToTarget(SVGElement* targetElement, CSSPropertyID id, const String& value)
207 {
208 #if !ENABLE(OILPAN)
209     ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
210 #endif
211
212     MutableStylePropertySet* propertySet = targetElement->ensureAnimatedSMILStyleProperties();
213     if (!propertySet->setProperty(id, value, false, 0))
214         return;
215
216     targetElement->setNeedsStyleRecalc(LocalStyleChange);
217 }
218
219 static inline void removeCSSPropertyFromTarget(SVGElement* targetElement, CSSPropertyID id)
220 {
221 #if !ENABLE(OILPAN)
222     ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
223 #endif
224     targetElement->ensureAnimatedSMILStyleProperties()->removeProperty(id);
225     targetElement->setNeedsStyleRecalc(LocalStyleChange);
226 }
227
228 static inline void applyCSSPropertyToTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName, const String& valueAsString)
229 {
230     ASSERT(targetElement);
231     if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
232         return;
233
234     CSSPropertyID id = cssPropertyID(attributeName.localName());
235
236     SVGElement::InstanceUpdateBlocker blocker(targetElement);
237     applyCSSPropertyToTarget(targetElement, id, valueAsString);
238
239     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
240     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
241     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
242     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
243         if (SVGElement* shadowTreeElement = *it)
244             applyCSSPropertyToTarget(shadowTreeElement, id, valueAsString);
245     }
246 }
247
248 static inline void removeCSSPropertyFromTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName)
249 {
250     ASSERT(targetElement);
251     if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
252         return;
253
254     CSSPropertyID id = cssPropertyID(attributeName.localName());
255
256     SVGElement::InstanceUpdateBlocker blocker(targetElement);
257     removeCSSPropertyFromTarget(targetElement, id);
258
259     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
260     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
261     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
262     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
263         if (SVGElement* shadowTreeElement = *it)
264             removeCSSPropertyFromTarget(shadowTreeElement, id);
265     }
266 }
267
268 static inline void notifyTargetAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
269 {
270 #if !ENABLE(OILPAN)
271     ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
272 #endif
273     targetElement->invalidateSVGAttributes();
274     targetElement->svgAttributeChanged(attributeName);
275 }
276
277 static inline void notifyTargetAndInstancesAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
278 {
279     ASSERT(targetElement);
280     if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
281         return;
282
283     SVGElement::InstanceUpdateBlocker blocker(targetElement);
284     notifyTargetAboutAnimValChange(targetElement, attributeName);
285
286     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
287     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
288     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
289     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
290         if (SVGElement* shadowTreeElement = *it)
291             notifyTargetAboutAnimValChange(shadowTreeElement, attributeName);
292     }
293 }
294
295 void SVGAnimateElement::clearAnimatedType(SVGElement* targetElement)
296 {
297     if (!m_animatedProperty)
298         return;
299
300     if (!targetElement) {
301         m_animatedProperty.clear();
302         return;
303     }
304
305     if (m_animatedElements.isEmpty()) {
306         // CSS properties animation code-path.
307         removeCSSPropertyFromTargetAndInstances(targetElement, attributeName());
308         m_animatedProperty.clear();
309         return;
310     }
311
312     // SVG DOM animVal animation code-path.
313     if (m_animator) {
314         m_animator->stopAnimValAnimation(m_animatedElements);
315         notifyTargetAndInstancesAboutAnimValChange(targetElement, attributeName());
316     }
317
318     m_animatedElements.clear();
319     m_animatedProperty.clear();
320 }
321
322 void SVGAnimateElement::applyResultsToTarget()
323 {
324     ASSERT(m_animatedPropertyType != AnimatedTransformList || isSVGAnimateTransformElement(*this));
325     ASSERT(m_animatedPropertyType != AnimatedUnknown);
326     ASSERT(m_animator);
327
328     // Early exit if our animated type got destructed by a previous endedActiveInterval().
329     if (!m_animatedProperty)
330         return;
331
332     if (m_animatedElements.isEmpty()) {
333         // CSS properties animation code-path.
334         // Convert the result of the animation to a String and apply it as CSS property on the target & all instances.
335         applyCSSPropertyToTargetAndInstances(targetElement(), attributeName(), m_animatedProperty->valueAsString());
336         return;
337     }
338
339     // SVG DOM animVal animation code-path.
340     // At this point the SVG DOM values are already changed, unlike for CSS.
341     // We only have to trigger update notifications here.
342     notifyTargetAndInstancesAboutAnimValChange(targetElement(), attributeName());
343 }
344
345 bool SVGAnimateElement::animatedPropertyTypeSupportsAddition() const
346 {
347     // Spec: http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties.
348     switch (m_animatedPropertyType) {
349     case AnimatedBoolean:
350     case AnimatedEnumeration:
351     case AnimatedPreserveAspectRatio:
352     case AnimatedString:
353     case AnimatedUnknown:
354         return false;
355     default:
356         return true;
357     }
358 }
359
360 bool SVGAnimateElement::isAdditive() const
361 {
362     if (animationMode() == ByAnimation || animationMode() == FromByAnimation)
363         if (!animatedPropertyTypeSupportsAddition())
364             return false;
365
366     return SVGAnimationElement::isAdditive();
367 }
368
369 float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString)
370 {
371     // FIXME: A return value of float is not enough to support paced animations on lists.
372     SVGElement* targetElement = this->targetElement();
373     if (!targetElement)
374         return -1;
375
376     return ensureAnimator()->calculateDistance(fromString, toString);
377 }
378
379 void SVGAnimateElement::setTargetElement(SVGElement* target)
380 {
381     SVGAnimationElement::setTargetElement(target);
382     resetAnimatedPropertyType();
383 }
384
385 void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName)
386 {
387     SVGAnimationElement::setAttributeName(attributeName);
388     resetAnimatedPropertyType();
389 }
390
391 void SVGAnimateElement::resetAnimatedPropertyType()
392 {
393     ASSERT(!m_animatedProperty);
394     m_fromProperty.clear();
395     m_toProperty.clear();
396     m_toAtEndOfDurationProperty.clear();
397     m_animator.clear();
398     m_animatedPropertyType = determineAnimatedPropertyType();
399 }
400
401 SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator()
402 {
403     if (!m_animator)
404         m_animator = SVGAnimatedTypeAnimator::create(m_animatedPropertyType, this, targetElement());
405     ASSERT(m_animatedPropertyType == m_animator->type());
406     return m_animator.get();
407 }
408
409 }