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