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.
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.
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.
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.
25 #include "core/svg/SVGAnimateElement.h"
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"
37 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document& document)
38 : SVGAnimationElement(tagName, document)
39 , m_animatedPropertyType(AnimatedString)
41 ASSERT(isSVGAnimateElement(*this));
42 ScriptWrappable::init(this);
45 PassRefPtr<SVGAnimateElement> SVGAnimateElement::create(Document& document)
47 return adoptRef(new SVGAnimateElement(SVGNames::animateTag, document));
50 SVGAnimateElement::~SVGAnimateElement()
54 clearAnimatedType(targetElement());
58 bool SVGAnimateElement::hasValidAttributeType()
60 SVGElement* targetElement = this->targetElement();
64 return m_animatedPropertyType != AnimatedUnknown && !hasInvalidCSSAttributeType();
67 void SVGAnimateElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement* resultElement)
69 ASSERT(resultElement);
70 SVGElement* targetElement = this->targetElement();
71 if (!targetElement || !isSVGAnimateElement(*resultElement))
74 ASSERT(m_animatedPropertyType == determineAnimatedPropertyType());
76 ASSERT(percentage >= 0 && percentage <= 1);
77 ASSERT(m_animatedPropertyType != AnimatedTransformList || isSVGAnimateTransformElement(*this));
78 ASSERT(m_animatedPropertyType != AnimatedUnknown);
80 ASSERT(m_animator->type() == m_animatedPropertyType);
81 ASSERT(m_fromProperty);
82 ASSERT(m_fromProperty->type() == m_animatedPropertyType);
85 SVGAnimateElement* resultAnimationElement = toSVGAnimateElement(resultElement);
86 ASSERT(resultAnimationElement->m_animatedProperty);
87 ASSERT(resultAnimationElement->m_animatedPropertyType == m_animatedPropertyType);
89 if (isSVGSetElement(*this))
92 if (calcMode() == CalcModeDiscrete)
93 percentage = percentage < 0.5 ? 0 : 1;
95 // Target element might have changed.
96 m_animator->setContextElement(targetElement);
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());
103 bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
105 if (toAtEndOfDurationString.isEmpty())
107 m_toAtEndOfDurationProperty = ensureAnimator()->constructFromString(toAtEndOfDurationString);
111 bool SVGAnimateElement::calculateFromAndToValues(const String& fromString, const String& toString)
113 SVGElement* targetElement = this->targetElement();
117 determinePropertyValueTypes(fromString, toString);
118 ensureAnimator()->calculateFromAndToValues(m_fromProperty, m_toProperty, fromString, toString);
119 ASSERT(m_animatedPropertyType == m_animator->type());
123 bool SVGAnimateElement::calculateFromAndByValues(const String& fromString, const String& byString)
125 SVGElement* targetElement = this->targetElement();
129 if (animationMode() == ByAnimation && !isAdditive())
132 // from-by animation may only be used with attributes that support addition (e.g. most numeric attributes).
133 if (animationMode() == FromByAnimation && !animatedPropertyTypeSupportsAddition())
136 ASSERT(!isSVGSetElement(*this));
138 determinePropertyValueTypes(fromString, byString);
139 ensureAnimator()->calculateFromAndByValues(m_fromProperty, m_toProperty, fromString, byString);
140 ASSERT(m_animatedPropertyType == m_animator->type());
146 Vector<SVGElement*> findElementInstances(SVGElement* targetElement)
148 ASSERT(targetElement);
149 Vector<SVGElement*> animatedElements;
151 animatedElements.append(targetElement);
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);
160 return animatedElements;
165 void SVGAnimateElement::resetAnimatedType()
167 SVGAnimatedTypeAnimator* animator = ensureAnimator();
168 ASSERT(m_animatedPropertyType == animator->type());
170 SVGElement* targetElement = this->targetElement();
171 const QualifiedName& attributeName = this->attributeName();
172 ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName);
174 if (shouldApply == DontApplyAnimation)
177 if (shouldApply == ApplyXMLAnimation) {
178 // SVG DOM animVal animation code-path.
179 m_animatedElements = findElementInstances(targetElement);
180 ASSERT(!m_animatedElements.isEmpty());
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);
186 if (!m_animatedProperty)
187 m_animatedProperty = animator->startAnimValAnimation(m_animatedElements);
189 m_animatedProperty = animator->resetAnimValToBaseVal(m_animatedElements);
194 // CSS properties animation code-path.
195 ASSERT(m_animatedElements.isEmpty());
198 if (shouldApply == ApplyCSSAnimation) {
199 ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement, attributeName));
200 computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.localName()), baseValue);
203 m_animatedProperty = animator->constructFromString(baseValue);
206 static inline void applyCSSPropertyToTarget(SVGElement* targetElement, CSSPropertyID id, const String& value)
209 ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
212 MutableStylePropertySet* propertySet = targetElement->ensureAnimatedSMILStyleProperties();
213 if (!propertySet->setProperty(id, value, false, 0))
216 targetElement->setNeedsStyleRecalc(LocalStyleChange);
219 static inline void removeCSSPropertyFromTarget(SVGElement* targetElement, CSSPropertyID id)
222 ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
224 targetElement->ensureAnimatedSMILStyleProperties()->removeProperty(id);
225 targetElement->setNeedsStyleRecalc(LocalStyleChange);
228 static inline void applyCSSPropertyToTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName, const String& valueAsString)
230 ASSERT(targetElement);
231 if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
234 CSSPropertyID id = cssPropertyID(attributeName.localName());
236 SVGElement::InstanceUpdateBlocker blocker(targetElement);
237 applyCSSPropertyToTarget(targetElement, id, valueAsString);
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);
248 static inline void removeCSSPropertyFromTargetAndInstances(SVGElement* targetElement, const QualifiedName& attributeName)
250 ASSERT(targetElement);
251 if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
254 CSSPropertyID id = cssPropertyID(attributeName.localName());
256 SVGElement::InstanceUpdateBlocker blocker(targetElement);
257 removeCSSPropertyFromTarget(targetElement, id);
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);
268 static inline void notifyTargetAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
271 ASSERT_WITH_SECURITY_IMPLICATION(!targetElement->m_deletionHasBegun);
273 targetElement->invalidateSVGAttributes();
274 targetElement->svgAttributeChanged(attributeName);
277 static inline void notifyTargetAndInstancesAboutAnimValChange(SVGElement* targetElement, const QualifiedName& attributeName)
279 ASSERT(targetElement);
280 if (attributeName == anyQName() || !targetElement->inDocument() || !targetElement->parentNode())
283 SVGElement::InstanceUpdateBlocker blocker(targetElement);
284 notifyTargetAboutAnimValChange(targetElement, attributeName);
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);
295 void SVGAnimateElement::clearAnimatedType(SVGElement* targetElement)
297 if (!m_animatedProperty)
300 if (!targetElement) {
301 m_animatedProperty.clear();
305 if (m_animatedElements.isEmpty()) {
306 // CSS properties animation code-path.
307 removeCSSPropertyFromTargetAndInstances(targetElement, attributeName());
308 m_animatedProperty.clear();
312 // SVG DOM animVal animation code-path.
314 m_animator->stopAnimValAnimation(m_animatedElements);
315 notifyTargetAndInstancesAboutAnimValChange(targetElement, attributeName());
318 m_animatedElements.clear();
319 m_animatedProperty.clear();
322 void SVGAnimateElement::applyResultsToTarget()
324 ASSERT(m_animatedPropertyType != AnimatedTransformList || isSVGAnimateTransformElement(*this));
325 ASSERT(m_animatedPropertyType != AnimatedUnknown);
328 // Early exit if our animated type got destructed by a previous endedActiveInterval().
329 if (!m_animatedProperty)
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());
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());
345 bool SVGAnimateElement::animatedPropertyTypeSupportsAddition() const
347 // Spec: http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties.
348 switch (m_animatedPropertyType) {
349 case AnimatedBoolean:
350 case AnimatedEnumeration:
351 case AnimatedPreserveAspectRatio:
353 case AnimatedUnknown:
360 bool SVGAnimateElement::isAdditive() const
362 if (animationMode() == ByAnimation || animationMode() == FromByAnimation)
363 if (!animatedPropertyTypeSupportsAddition())
366 return SVGAnimationElement::isAdditive();
369 float SVGAnimateElement::calculateDistance(const String& fromString, const String& toString)
371 // FIXME: A return value of float is not enough to support paced animations on lists.
372 SVGElement* targetElement = this->targetElement();
376 return ensureAnimator()->calculateDistance(fromString, toString);
379 void SVGAnimateElement::setTargetElement(SVGElement* target)
381 SVGAnimationElement::setTargetElement(target);
382 resetAnimatedPropertyType();
385 void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName)
387 SVGAnimationElement::setAttributeName(attributeName);
388 resetAnimatedPropertyType();
391 void SVGAnimateElement::resetAnimatedPropertyType()
393 ASSERT(!m_animatedProperty);
394 m_fromProperty.clear();
395 m_toProperty.clear();
396 m_toAtEndOfDurationProperty.clear();
398 m_animatedPropertyType = determineAnimatedPropertyType();
401 SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator()
404 m_animator = SVGAnimatedTypeAnimator::create(m_animatedPropertyType, this, targetElement());
405 ASSERT(m_animatedPropertyType == m_animator->type());
406 return m_animator.get();