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 35namespace WebCore { 36 37SVGAnimateElement::SVGAnimateElement(const QualifiedName& tagName, Document& document) 38 : SVGAnimationElement(tagName, document) 39{ 40 ASSERT(isSVGAnimateElement(*this)); 41 ScriptWrappable::init(this); 42} 43 44PassRefPtrWillBeRawPtr<SVGAnimateElement> SVGAnimateElement::create(Document& document) 45{ 46 return adoptRefWillBeNoop(new SVGAnimateElement(SVGNames::animateTag, document)); 47} 48 49SVGAnimateElement::~SVGAnimateElement() 50{ 51} 52 53AnimatedPropertyType SVGAnimateElement::animatedPropertyType() 54{ 55 return ensureAnimator()->type(); 56} 57 58bool SVGAnimateElement::hasValidAttributeType() 59{ 60 SVGElement* targetElement = this->targetElement(); 61 if (!targetElement) 62 return false; 63 64 return animatedPropertyType() != AnimatedUnknown && !hasInvalidCSSAttributeType(); 65} 66 67void 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 100bool SVGAnimateElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString) 101{ 102 if (toAtEndOfDurationString.isEmpty()) 103 return false; 104 m_toAtEndOfDurationProperty = ensureAnimator()->constructFromString(toAtEndOfDurationString); 105 return true; 106} 107 108bool 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 119bool 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 139namespace { 140 141WillBeHeapVector<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 160void 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 199static 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 212static 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 221static 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 241static 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 261static 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 270static 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 287void 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 314void 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 337bool 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 352bool SVGAnimateElement::isAdditive() 353{ 354 if (animationMode() == ByAnimation || animationMode() == FromByAnimation) 355 if (!animatedPropertyTypeSupportsAddition()) 356 return false; 357 358 return SVGAnimationElement::isAdditive(); 359} 360 361float 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 371void SVGAnimateElement::setTargetElement(SVGElement* target) 372{ 373 SVGAnimationElement::setTargetElement(target); 374 resetAnimatedPropertyType(); 375} 376 377void SVGAnimateElement::setAttributeName(const QualifiedName& attributeName) 378{ 379 SVGAnimationElement::setAttributeName(attributeName); 380 resetAnimatedPropertyType(); 381} 382 383void 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 392SVGAnimatedTypeAnimator* SVGAnimateElement::ensureAnimator() 393{ 394 if (!m_animator) 395 m_animator = SVGAnimatedTypeAnimator::create(this, targetElement()); 396 return m_animator.get(); 397} 398 399void SVGAnimateElement::trace(Visitor* visitor) 400{ 401 visitor->trace(m_animator); 402 SVGAnimationElement::trace(visitor); 403} 404 405} 406