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/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 35namespace blink { 36 37SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document& document) 38 : SVGAnimationElement(tagName, document) 39{ 40} 41 42PassRefPtrWillBeRawPtr<SVGAnimateElement> SVGAnimateElement::create(Document& document) 43{ 44 return adoptRefWillBeNoop(new SVGAnimateElement(SVGNames::animateTag, document)); 45} 46 47SVGAnimateElement::~SVGAnimateElement() 48{ 49} 50 51AnimatedPropertyType SVGAnimateElement::animatedPropertyType() 52{ 53 return ensureAnimator()->type(); 54} 55 56bool SVGAnimateElement::hasValidAttributeType() 57{ 58 SVGElement* targetElement = this->targetElement(); 59 if (!targetElement) 60 return false; 61 62 return animatedPropertyType() != AnimatedUnknown && !hasInvalidCSSAttributeType(); 63} 64 65void 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 98bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString) 99{ 100 if (toAtEndOfDurationString.isEmpty()) 101 return false; 102 m_toAtEndOfDurationProperty = ensureAnimator()->constructFromString(toAtEndOfDurationString); 103 return true; 104} 105 106bool 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 117bool 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 137namespace { 138 139WillBeHeapVector<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 158void 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 197static 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 210static 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 219static 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 239static 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 259static 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 268static 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 285void 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 312void 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 335bool 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 350bool SVGAnimateElement::isAdditive() 351{ 352 if (animationMode() == ByAnimation || animationMode() == FromByAnimation) 353 if (!animatedPropertyTypeSupportsAddition()) 354 return false; 355 356 return SVGAnimationElement::isAdditive(); 357} 358 359float 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 369void SVGAnimateElement::setTargetElement(SVGElement* target) 370{ 371 SVGAnimationElement::setTargetElement(target); 372 resetAnimatedPropertyType(); 373} 374 375void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName) 376{ 377 SVGAnimationElement::setAttributeName(attributeName); 378 resetAnimatedPropertyType(); 379} 380 381void 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 390SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator() 391{ 392 if (!m_animator) 393 m_animator = SVGAnimatedTypeAnimator::create(this, targetElement()); 394 return m_animator.get(); 395} 396 397void SVGAnimateElement::trace(Visitor* visitor) 398{ 399 visitor->trace(m_animator); 400 SVGAnimationElement::trace(visitor); 401} 402 403} 404