• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/CSSPropertyNames.h"
28 #include "core/css/parser/BisonCSSParser.h"
29 #include "core/css/StylePropertySet.h"
30 #include "core/dom/Document.h"
31 #include "core/dom/QualifiedName.h"
32 #include "core/svg/SVGAnimatedTypeAnimator.h"
33 #include "core/svg/SVGDocumentExtensions.h"
34 
35 namespace WebCore {
36 
SVGAnimateElement(const QualifiedName & tagName,Document & document)37 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document& document)
38     : SVGAnimationElement(tagName, document)
39 {
40     ASSERT(isSVGAnimateElement(*this));
41     ScriptWrappable::init(this);
42 }
43 
create(Document & document)44 PassRefPtrWillBeRawPtr<SVGAnimateElement> SVGAnimateElement::create(Document& document)
45 {
46     return adoptRefWillBeNoop(new SVGAnimateElement(SVGNames::animateTag, document));
47 }
48 
~SVGAnimateElement()49 SVGAnimateElement::~SVGAnimateElement()
50 {
51 }
52 
animatedPropertyType()53 AnimatedPropertyType SVGAnimateElement::animatedPropertyType()
54 {
55     return ensureAnimator()->type();
56 }
57 
hasValidAttributeType()58 bool SVGAnimateElement::hasValidAttributeType()
59 {
60     SVGElement* targetElement = this->targetElement();
61     if (!targetElement)
62         return false;
63 
64     return animatedPropertyType() != AnimatedUnknown && !hasInvalidCSSAttributeType();
65 }
66 
calculateAnimatedValue(float percentage,unsigned repeatCount,SVGSMILElement * resultElement)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(percentage >= 0 && percentage <= 1);
75     ASSERT(m_animator);
76     ASSERT(animatedPropertyType() != AnimatedTransformList || isSVGAnimateTransformElement(*this));
77     ASSERT(animatedPropertyType() != AnimatedUnknown);
78     ASSERT(m_fromProperty);
79     ASSERT(m_fromProperty->type() == animatedPropertyType());
80     ASSERT(m_toProperty);
81 
82     SVGAnimateElement* resultAnimationElement = toSVGAnimateElement(resultElement);
83     ASSERT(resultAnimationElement->m_animatedProperty);
84     ASSERT(resultAnimationElement->animatedPropertyType() == animatedPropertyType());
85 
86     if (isSVGSetElement(*this))
87         percentage = 1;
88 
89     if (calcMode() == CalcModeDiscrete)
90         percentage = percentage < 0.5 ? 0 : 1;
91 
92     // Target element might have changed.
93     m_animator->setContextElement(targetElement);
94 
95     // Values-animation accumulates using the last values entry corresponding to the end of duration time.
96     SVGPropertyBase* toAtEndOfDurationProperty = m_toAtEndOfDurationProperty ? m_toAtEndOfDurationProperty.get() : m_toProperty.get();
97     m_animator->calculateAnimatedValue(percentage, repeatCount, m_fromProperty.get(), m_toProperty.get(), toAtEndOfDurationProperty, resultAnimationElement->m_animatedProperty.get());
98 }
99 
calculateToAtEndOfDurationValue(const String & toAtEndOfDurationString)100 bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
101 {
102     if (toAtEndOfDurationString.isEmpty())
103         return false;
104     m_toAtEndOfDurationProperty = ensureAnimator()->constructFromString(toAtEndOfDurationString);
105     return true;
106 }
107 
calculateFromAndToValues(const String & fromString,const String & toString)108 bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
109 {
110     SVGElement* targetElement = this->targetElement();
111     if (!targetElement)
112         return false;
113 
114     determinePropertyValueTypes(fromString, toString);
115     ensureAnimator()->calculateFromAndToValues(m_fromProperty, m_toProperty, fromString, toString);
116     return true;
117 }
118 
calculateFromAndByValues(const String & fromString,const String & byString)119 bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
120 {
121     SVGElement* targetElement = this->targetElement();
122     if (!targetElement)
123         return false;
124 
125     if (animationMode() == ByAnimation && !isAdditive())
126         return false;
127 
128     // from-by animation may only be used with attributes that support addition (e.g. most numeric attributes).
129     if (animationMode() == FromByAnimation && !animatedPropertyTypeSupportsAddition())
130         return false;
131 
132     ASSERT(!isSVGSetElement(*this));
133 
134     determinePropertyValueTypes(fromString, byString);
135     ensureAnimator()->calculateFromAndByValues(m_fromProperty, m_toProperty, fromString, byString);
136     return true;
137 }
138 
139 namespace {
140 
findElementInstances(SVGElement * targetElement)141 WillBeHeapVector<RawPtrWillBeMember<SVGElement> > findElementInstances(SVGElement* targetElement)
142 {
143     ASSERT(targetElement);
144     WillBeHeapVector<RawPtrWillBeMember<SVGElement> > animatedElements;
145 
146     animatedElements.append(targetElement);
147 
148     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
149     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
150     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
151         if (SVGElement* shadowTreeElement = *it)
152             animatedElements.append(shadowTreeElement);
153     }
154 
155     return animatedElements;
156 }
157 
158 }
159 
resetAnimatedType()160 void SVGAnimateElement::resetAnimatedType()
161 {
162     SVGAnimatedTypeAnimator* animator = ensureAnimator();
163 
164     SVGElement* targetElement = this->targetElement();
165     const QualifiedName& attributeName = this->attributeName();
166     ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName);
167 
168     if (shouldApply == DontApplyAnimation)
169         return;
170 
171     if (shouldApply == ApplyXMLAnimation) {
172         // SVG DOM animVal animation code-path.
173         WillBeHeapVector<RawPtrWillBeMember<SVGElement> > animatedElements = findElementInstances(targetElement);
174         ASSERT(!animatedElements.isEmpty());
175 
176         WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator end = animatedElements.end();
177         for (WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator it = animatedElements.begin(); it != end; ++it)
178             document().accessSVGExtensions().addElementReferencingTarget(this, *it);
179 
180         if (!m_animatedProperty)
181             m_animatedProperty = animator->startAnimValAnimation(animatedElements);
182         else
183             m_animatedProperty = animator->resetAnimValToBaseVal(animatedElements);
184 
185         return;
186     }
187 
188     // CSS properties animation code-path.
189     String baseValue;
190 
191     if (shouldApply == ApplyCSSAnimation) {
192         ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement, attributeName));
193         computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.localName()), baseValue);
194     }
195 
196     m_animatedProperty = animator->constructFromString(baseValue);
197 }
198 
applyCSSPropertyToTarget(SVGElement * targetElement,CSSPropertyID id,const String & value)199 static inline void applyCSSPropertyToTarget(SVGElement* targetElement, CSSPropertyID id, const String& value)
200 {
201 #if !ENABLE(OILPAN)
202     ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
203 #endif
204 
205     MutableStylePropertySet* propertySet = targetElement->ensureAnimatedSMILStyleProperties();
206     if (!propertySet->setProperty(id, value, false, 0))
207         return;
208 
209     targetElement->setNeedsStyleRecalc(LocalStyleChange);
210 }
211 
removeCSSPropertyFromTarget(SVGElement * targetElement,CSSPropertyID id)212 static inline void removeCSSPropertyFromTarget(SVGElement* targetElement, CSSPropertyID id)
213 {
214 #if !ENABLE(OILPAN)
215     ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
216 #endif
217     targetElement->ensureAnimatedSMILStyleProperties()->removeProperty(id);
218     targetElement->setNeedsStyleRecalc(LocalStyleChange);
219 }
220 
applyCSSPropertyToTargetAndInstances(SVGElement * targetElement,const QualifiedName & attributeName,const String & valueAsString)221 static inline void applyCSSPropertyToTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName, const String& valueAsString)
222 {
223     ASSERT(targetElement);
224     if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
225         return;
226 
227     CSSPropertyID id = cssPropertyID(attributeName.localName());
228 
229     SVGElement::InstanceUpdateBlocker blocker(targetElement);
230     applyCSSPropertyToTarget(targetElement, id, valueAsString);
231 
232     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
233     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
234     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
235     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
236         if (SVGElement* shadowTreeElement = *it)
237             applyCSSPropertyToTarget(shadowTreeElement, id, valueAsString);
238     }
239 }
240 
removeCSSPropertyFromTargetAndInstances(SVGElement * targetElement,const QualifiedName & attributeName)241 static inline void removeCSSPropertyFromTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName)
242 {
243     ASSERT(targetElement);
244     if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
245         return;
246 
247     CSSPropertyID id = cssPropertyID(attributeName.localName());
248 
249     SVGElement::InstanceUpdateBlocker blocker(targetElement);
250     removeCSSPropertyFromTarget(targetElement, id);
251 
252     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
253     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
254     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
255     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
256         if (SVGElement* shadowTreeElement = *it)
257             removeCSSPropertyFromTarget(shadowTreeElement, id);
258     }
259 }
260 
notifyTargetAboutAnimValChange(SVGElement * targetElement,const QualifiedName & attributeName)261 static inline void notifyTargetAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
262 {
263 #if !ENABLE(OILPAN)
264     ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
265 #endif
266     targetElement->invalidateSVGAttributes();
267     targetElement->svgAttributeChanged(attributeName);
268 }
269 
notifyTargetAndInstancesAboutAnimValChange(SVGElement * targetElement,const QualifiedName & attributeName)270 static inline void notifyTargetAndInstancesAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
271 {
272     ASSERT(targetElement);
273     if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
274         return;
275 
276     SVGElement::InstanceUpdateBlocker blocker(targetElement);
277     notifyTargetAboutAnimValChange(targetElement, attributeName);
278 
279     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
280     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = targetElement->instancesForElement();
281     const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
282     for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
283         notifyTargetAboutAnimValChange(*it, attributeName);
284     }
285 }
286 
clearAnimatedType(SVGElement * targetElement)287 void SVGAnimateElement::clearAnimatedType(SVGElement* targetElement)
288 {
289     if (!m_animatedProperty)
290         return;
291 
292     if (!targetElement) {
293         m_animatedProperty.clear();
294         return;
295     }
296 
297     if (ensureAnimator()->isAnimatingCSSProperty()) {
298         // CSS properties animation code-path.
299         removeCSSPropertyFromTargetAndInstances(targetElement, attributeName());
300         m_animatedProperty.clear();
301         return;
302     }
303 
304     // SVG DOM animVal animation code-path.
305     if (m_animator) {
306         WillBeHeapVector<RawPtrWillBeMember<SVGElement> > animatedElements = findElementInstances(targetElement);
307         m_animator->stopAnimValAnimation(animatedElements);
308         notifyTargetAndInstancesAboutAnimValChange(targetElement, attributeName());
309     }
310 
311     m_animatedProperty.clear();
312 }
313 
applyResultsToTarget()314 void SVGAnimateElement::applyResultsToTarget()
315 {
316     ASSERT(m_animator);
317     ASSERT(animatedPropertyType() != AnimatedTransformList || isSVGAnimateTransformElement(*this));
318     ASSERT(animatedPropertyType() != AnimatedUnknown);
319 
320     // Early exit if our animated type got destructed by a previous endedActiveInterval().
321     if (!m_animatedProperty)
322         return;
323 
324     if (m_animator->isAnimatingCSSProperty()) {
325         // CSS properties animation code-path.
326         // Convert the result of the animation to a String and apply it as CSS property on the target & all instances.
327         applyCSSPropertyToTargetAndInstances(targetElement(), attributeName(), m_animatedProperty->valueAsString());
328         return;
329     }
330 
331     // SVG DOM animVal animation code-path.
332     // At this point the SVG DOM values are already changed, unlike for CSS.
333     // We only have to trigger update notifications here.
334     notifyTargetAndInstancesAboutAnimValChange(targetElement(), attributeName());
335 }
336 
animatedPropertyTypeSupportsAddition()337 bool SVGAnimateElement::animatedPropertyTypeSupportsAddition()
338 {
339     // Spec: http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties.
340     switch (animatedPropertyType()) {
341     case AnimatedBoolean:
342     case AnimatedEnumeration:
343     case AnimatedPreserveAspectRatio:
344     case AnimatedString:
345     case AnimatedUnknown:
346         return false;
347     default:
348         return true;
349     }
350 }
351 
isAdditive()352 bool SVGAnimateElement::isAdditive()
353 {
354     if (animationMode() == ByAnimation || animationMode() == FromByAnimation)
355         if (!animatedPropertyTypeSupportsAddition())
356             return false;
357 
358     return SVGAnimationElement::isAdditive();
359 }
360 
calculateDistance(const String & fromString,const String & toString)361 float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString)
362 {
363     // FIXME: A return value of float is not enough to support paced animations on lists.
364     SVGElement* targetElement = this->targetElement();
365     if (!targetElement)
366         return -1;
367 
368     return ensureAnimator()->calculateDistance(fromString, toString);
369 }
370 
setTargetElement(SVGElement * target)371 void SVGAnimateElement::setTargetElement(SVGElement* target)
372 {
373     SVGAnimationElement::setTargetElement(target);
374     resetAnimatedPropertyType();
375 }
376 
setAttributeName(const QualifiedName & attributeName)377 void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName)
378 {
379     SVGAnimationElement::setAttributeName(attributeName);
380     resetAnimatedPropertyType();
381 }
382 
resetAnimatedPropertyType()383 void SVGAnimateElement::resetAnimatedPropertyType()
384 {
385     ASSERT(!m_animatedProperty);
386     m_fromProperty.clear();
387     m_toProperty.clear();
388     m_toAtEndOfDurationProperty.clear();
389     m_animator.clear();
390 }
391 
ensureAnimator()392 SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator()
393 {
394     if (!m_animator)
395         m_animator = SVGAnimatedTypeAnimator::create(this, targetElement());
396     return m_animator.get();
397 }
398 
trace(Visitor * visitor)399 void SVGAnimateElement::trace(Visitor* visitor)
400 {
401     visitor->trace(m_animator);
402     SVGAnimationElement::trace(visitor);
403 }
404 
405 }
406