2 * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2008 Apple Inc. All rights reserved.
6 * Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au>
7 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
27 #include "core/svg/SVGAnimationElement.h"
29 #include "CSSPropertyNames.h"
31 #include "core/css/CSSComputedStyleDeclaration.h"
32 #include "core/css/parser/BisonCSSParser.h"
33 #include "core/frame/UseCounter.h"
34 #include "core/svg/SVGAnimateElement.h"
35 #include "core/svg/SVGElement.h"
36 #include "core/svg/SVGParserUtilities.h"
37 #include "platform/FloatConversion.h"
38 #include "wtf/MathExtras.h"
42 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document& document)
43 : SVGSMILElement(tagName, document)
45 , m_fromPropertyValueType(RegularPropertyValue)
46 , m_toPropertyValueType(RegularPropertyValue)
47 , m_animationValid(false)
48 , m_attributeType(AttributeTypeAuto)
49 , m_hasInvalidCSSAttributeType(false)
50 , m_calcMode(CalcModeLinear)
51 , m_animationMode(NoAnimation)
53 ScriptWrappable::init(this);
55 UseCounter::count(document, UseCounter::SVGAnimationElement);
58 static void parseKeyTimes(const String& string, Vector<float>& result, bool verifyOrder)
61 Vector<String> parseList;
62 string.split(';', parseList);
63 for (unsigned n = 0; n < parseList.size(); ++n) {
64 String timeString = parseList[n];
66 float time = timeString.toFloat(&ok);
67 if (!ok || time < 0 || time > 1)
73 } else if (time < result.last())
83 template<typename CharType>
84 static void parseKeySplinesInternal(const String& string, Vector<UnitBezier>& result)
86 const CharType* ptr = string.getCharacters<CharType>();
87 const CharType* end = ptr + string.length();
89 skipOptionalSVGSpaces(ptr, end);
91 bool delimParsed = false;
95 if (!parseNumber(ptr, end, posA)) {
101 if (!parseNumber(ptr, end, posB)) {
107 if (!parseNumber(ptr, end, posC)) {
113 if (!parseNumber(ptr, end, posD, false)) {
118 skipOptionalSVGSpaces(ptr, end);
120 if (ptr < end && *ptr == ';') {
124 skipOptionalSVGSpaces(ptr, end);
126 result.append(UnitBezier(posA, posB, posC, posD));
128 if (!(ptr == end && !delimParsed))
132 static void parseKeySplines(const String& string, Vector<UnitBezier>& result)
135 if (string.isEmpty())
138 parseKeySplinesInternal<LChar>(string, result);
140 parseKeySplinesInternal<UChar>(string, result);
143 bool SVGAnimationElement::isSupportedAttribute(const QualifiedName& attrName)
145 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
146 if (supportedAttributes.isEmpty()) {
147 SVGTests::addSupportedAttributes(supportedAttributes);
148 supportedAttributes.add(SVGNames::valuesAttr);
149 supportedAttributes.add(SVGNames::keyTimesAttr);
150 supportedAttributes.add(SVGNames::keyPointsAttr);
151 supportedAttributes.add(SVGNames::keySplinesAttr);
152 supportedAttributes.add(SVGNames::attributeTypeAttr);
153 supportedAttributes.add(SVGNames::calcModeAttr);
154 supportedAttributes.add(SVGNames::fromAttr);
155 supportedAttributes.add(SVGNames::toAttr);
156 supportedAttributes.add(SVGNames::byAttr);
158 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
161 void SVGAnimationElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
163 if (!isSupportedAttribute(name)) {
164 SVGSMILElement::parseAttribute(name, value);
168 if (name == SVGNames::valuesAttr) {
169 // Per the SMIL specification, leading and trailing white space,
170 // and white space before and after semicolon separators, is allowed and will be ignored.
171 // http://www.w3.org/TR/SVG11/animate.html#ValuesAttribute
172 value.string().split(';', m_values);
173 for (unsigned i = 0; i < m_values.size(); ++i)
174 m_values[i] = m_values[i].stripWhiteSpace();
176 updateAnimationMode();
180 if (name == SVGNames::keyTimesAttr) {
181 parseKeyTimes(value, m_keyTimes, true);
185 if (name == SVGNames::keyPointsAttr) {
186 if (isSVGAnimateMotionElement(*this)) {
187 // This is specified to be an animateMotion attribute only but it is simpler to put it here
188 // where the other timing calculatations are.
189 parseKeyTimes(value, m_keyPoints, false);
194 if (name == SVGNames::keySplinesAttr) {
195 parseKeySplines(value, m_keySplines);
199 if (name == SVGNames::attributeTypeAttr) {
200 setAttributeType(value);
204 if (name == SVGNames::calcModeAttr) {
209 if (name == SVGNames::fromAttr || name == SVGNames::toAttr || name == SVGNames::byAttr) {
210 updateAnimationMode();
214 if (SVGTests::parseAttribute(name, value))
217 ASSERT_NOT_REACHED();
220 void SVGAnimationElement::svgAttributeChanged(const QualifiedName& attrName)
222 if (!isSupportedAttribute(attrName)) {
223 SVGSMILElement::svgAttributeChanged(attrName);
227 animationAttributeChanged();
230 void SVGAnimationElement::animationAttributeChanged()
232 // Assumptions may not hold after an attribute change.
233 m_animationValid = false;
234 m_lastValuesAnimationFrom = String();
235 m_lastValuesAnimationTo = String();
239 float SVGAnimationElement::getStartTime() const
241 return narrowPrecisionToFloat(intervalBegin().value());
244 float SVGAnimationElement::getCurrentTime() const
246 return narrowPrecisionToFloat(elapsed().value());
249 float SVGAnimationElement::getSimpleDuration() const
251 return narrowPrecisionToFloat(simpleDuration().value());
254 void SVGAnimationElement::beginElement()
259 void SVGAnimationElement::beginElementAt(float offset)
261 if (!std::isfinite(offset))
263 SMILTime elapsed = this->elapsed();
264 addBeginTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
267 void SVGAnimationElement::endElement()
272 void SVGAnimationElement::endElementAt(float offset)
274 if (!std::isfinite(offset))
276 SMILTime elapsed = this->elapsed();
277 addEndTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
280 void SVGAnimationElement::updateAnimationMode()
282 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
283 if (hasAttribute(SVGNames::valuesAttr))
284 setAnimationMode(ValuesAnimation);
285 else if (!toValue().isEmpty())
286 setAnimationMode(fromValue().isEmpty() ? ToAnimation : FromToAnimation);
287 else if (!byValue().isEmpty())
288 setAnimationMode(fromValue().isEmpty() ? ByAnimation : FromByAnimation);
290 setAnimationMode(NoAnimation);
293 void SVGAnimationElement::setCalcMode(const AtomicString& calcMode)
295 DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete", AtomicString::ConstructFromLiteral));
296 DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear", AtomicString::ConstructFromLiteral));
297 DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced", AtomicString::ConstructFromLiteral));
298 DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline", AtomicString::ConstructFromLiteral));
299 if (calcMode == discrete)
300 setCalcMode(CalcModeDiscrete);
301 else if (calcMode == linear)
302 setCalcMode(CalcModeLinear);
303 else if (calcMode == paced)
304 setCalcMode(CalcModePaced);
305 else if (calcMode == spline)
306 setCalcMode(CalcModeSpline);
308 setCalcMode(isSVGAnimateMotionElement(*this) ? CalcModePaced : CalcModeLinear);
311 void SVGAnimationElement::setAttributeType(const AtomicString& attributeType)
313 DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS", AtomicString::ConstructFromLiteral));
314 DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML", AtomicString::ConstructFromLiteral));
315 if (attributeType == css)
316 m_attributeType = AttributeTypeCSS;
317 else if (attributeType == xml)
318 m_attributeType = AttributeTypeXML;
320 m_attributeType = AttributeTypeAuto;
321 checkInvalidCSSAttributeType(targetElement());
324 String SVGAnimationElement::toValue() const
326 return fastGetAttribute(SVGNames::toAttr);
329 String SVGAnimationElement::byValue() const
331 return fastGetAttribute(SVGNames::byAttr);
334 String SVGAnimationElement::fromValue() const
336 return fastGetAttribute(SVGNames::fromAttr);
339 bool SVGAnimationElement::isAdditive() const
341 DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum", AtomicString::ConstructFromLiteral));
342 const AtomicString& value = fastGetAttribute(SVGNames::additiveAttr);
343 return value == sum || animationMode() == ByAnimation;
346 bool SVGAnimationElement::isAccumulated() const
348 DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum", AtomicString::ConstructFromLiteral));
349 const AtomicString& value = fastGetAttribute(SVGNames::accumulateAttr);
350 return value == sum && animationMode() != ToAnimation;
353 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName)
355 ASSERT(targetElement);
357 return SVGElement::isAnimatableCSSProperty(attributeName);
360 SVGAnimationElement::ShouldApplyAnimation SVGAnimationElement::shouldApplyAnimation(SVGElement* targetElement, const QualifiedName& attributeName)
362 if (!hasValidAttributeType() || !targetElement || attributeName == anyQName())
363 return DontApplyAnimation;
365 // Always animate CSS properties, using the ApplyCSSAnimation code path, regardless of the attributeType value.
366 if (isTargetAttributeCSSProperty(targetElement, attributeName))
367 return ApplyCSSAnimation;
369 // If attributeType="CSS" and attributeName doesn't point to a CSS property, ignore the animation.
370 if (attributeType() == AttributeTypeCSS)
371 return DontApplyAnimation;
373 return ApplyXMLAnimation;
376 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
378 ASSERT(calcMode() == CalcModePaced);
379 ASSERT(animationMode() == ValuesAnimation);
381 unsigned valuesCount = m_values.size();
382 ASSERT(valuesCount >= 1);
383 if (valuesCount == 1)
386 // FIXME, webkit.org/b/109010: m_keyTimes should not be modified in this function.
389 Vector<float> keyTimesForPaced;
390 float totalDistance = 0;
391 keyTimesForPaced.append(0);
392 for (unsigned n = 0; n < valuesCount - 1; ++n) {
393 // Distance in any units
394 float distance = calculateDistance(m_values[n], m_values[n + 1]);
397 totalDistance += distance;
398 keyTimesForPaced.append(distance);
404 for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
405 keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
406 keyTimesForPaced[keyTimesForPaced.size() - 1] = 1;
408 // Use key times calculated based on pacing instead of the user provided ones.
409 m_keyTimes = keyTimesForPaced;
412 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
414 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
417 unsigned keyTimesCount = m_keyTimes.size();
418 // For linear and spline animations, the last value must be '1'. In those
419 // cases we don't need to consider the last value, since |percent| is never
421 if (keyTimesCount && calcMode() != CalcModeDiscrete)
423 for (index = 1; index < keyTimesCount; ++index) {
424 if (m_keyTimes[index] > percent)
430 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
432 ASSERT(calcMode() == CalcModeSpline);
433 ASSERT_WITH_SECURITY_IMPLICATION(splineIndex < m_keySplines.size());
434 UnitBezier bezier = m_keySplines[splineIndex];
435 SMILTime duration = simpleDuration();
436 if (!duration.isFinite())
438 return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
441 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
443 ASSERT(!m_keyPoints.isEmpty());
444 ASSERT(calcMode() != CalcModePaced);
445 ASSERT(m_keyTimes.size() > 1);
446 ASSERT(m_keyPoints.size() == m_keyTimes.size());
449 return m_keyPoints[m_keyPoints.size() - 1];
451 unsigned index = calculateKeyTimesIndex(percent);
452 float fromKeyPoint = m_keyPoints[index];
454 if (calcMode() == CalcModeDiscrete)
457 ASSERT(index + 1 < m_keyTimes.size());
458 float fromPercent = m_keyTimes[index];
459 float toPercent = m_keyTimes[index + 1];
460 float toKeyPoint = m_keyPoints[index + 1];
461 float keyPointPercent = (percent - fromPercent) / (toPercent - fromPercent);
463 if (calcMode() == CalcModeSpline) {
464 ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
465 keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
467 return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
470 float SVGAnimationElement::calculatePercentForFromTo(float percent) const
472 if (calcMode() == CalcModeDiscrete && m_keyTimes.size() == 2)
473 return percent > m_keyTimes[1] ? 1 : 0;
478 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
480 ASSERT(!m_keyPoints.isEmpty());
481 ASSERT(m_keyPoints.size() == m_keyTimes.size());
482 ASSERT(calcMode() != CalcModePaced);
483 effectivePercent = calculatePercentFromKeyPoints(percent);
484 unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
485 from = m_values[index];
486 to = m_values[index + 1];
489 AnimatedPropertyType SVGAnimationElement::determineAnimatedPropertyType() const
491 if (!targetElement())
492 return AnimatedString;
494 RefPtr<SVGAnimatedPropertyBase> property = targetElement()->propertyFromAttribute(attributeName());
496 AnimatedPropertyType propertyType = property->type();
498 // Only <animatedTransform> is allowed to animate AnimatedTransformList.
499 // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
500 if (propertyType == AnimatedTransformList && !isSVGAnimateTransformElement(*this))
501 return AnimatedUnknown;
506 return SVGElement::animatedPropertyTypeForCSSAttribute(attributeName());
509 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to)
511 unsigned valuesCount = m_values.size();
512 ASSERT(m_animationValid);
513 ASSERT(valuesCount >= 1);
515 if (percent == 1 || valuesCount == 1) {
516 from = m_values[valuesCount - 1];
517 to = m_values[valuesCount - 1];
518 effectivePercent = 1;
522 CalcMode calcMode = this->calcMode();
523 if (hasTagName(SVGNames::animateTag)) {
524 AnimatedPropertyType attributeType = determineAnimatedPropertyType();
525 // Fall back to discrete animations for Strings.
526 if (attributeType == AnimatedBoolean
527 || attributeType == AnimatedEnumeration
528 || attributeType == AnimatedPreserveAspectRatio
529 || attributeType == AnimatedString)
530 calcMode = CalcModeDiscrete;
532 if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
533 return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
535 unsigned keyTimesCount = m_keyTimes.size();
536 ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
537 ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
539 unsigned index = calculateKeyTimesIndex(percent);
540 if (calcMode == CalcModeDiscrete) {
542 index = static_cast<unsigned>(percent * valuesCount);
543 from = m_values[index];
544 to = m_values[index];
545 effectivePercent = 0;
552 fromPercent = m_keyTimes[index];
553 toPercent = m_keyTimes[index + 1];
555 index = static_cast<unsigned>(floorf(percent * (valuesCount - 1)));
556 fromPercent = static_cast<float>(index) / (valuesCount - 1);
557 toPercent = static_cast<float>(index + 1) / (valuesCount - 1);
560 if (index == valuesCount - 1)
562 from = m_values[index];
563 to = m_values[index + 1];
564 ASSERT(toPercent > fromPercent);
565 effectivePercent = (percent - fromPercent) / (toPercent - fromPercent);
567 if (calcMode == CalcModeSpline) {
568 ASSERT(m_keySplines.size() == m_values.size() - 1);
569 effectivePercent = calculatePercentForSpline(effectivePercent, index);
573 void SVGAnimationElement::startedActiveInterval()
575 m_animationValid = false;
577 if (!hasValidAttributeType())
580 // These validations are appropriate for all animation modes.
581 if (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
584 AnimationMode animationMode = this->animationMode();
585 CalcMode calcMode = this->calcMode();
586 if (calcMode == CalcModeSpline) {
587 unsigned splinesCount = m_keySplines.size();
589 || (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() - 1 != splinesCount)
590 || (animationMode == ValuesAnimation && m_values.size() - 1 != splinesCount)
591 || (fastHasAttribute(SVGNames::keyTimesAttr) && m_keyTimes.size() - 1 != splinesCount))
595 String from = fromValue();
596 String to = toValue();
597 String by = byValue();
598 if (animationMode == NoAnimation)
600 if (animationMode == FromToAnimation)
601 m_animationValid = calculateFromAndToValues(from, to);
602 else if (animationMode == ToAnimation) {
603 // For to-animations the from value is the current accumulated value from lower priority animations.
604 // The value is not static and is determined during the animation.
605 m_animationValid = calculateFromAndToValues(emptyString(), to);
606 } else if (animationMode == FromByAnimation)
607 m_animationValid = calculateFromAndByValues(from, by);
608 else if (animationMode == ByAnimation)
609 m_animationValid = calculateFromAndByValues(emptyString(), by);
610 else if (animationMode == ValuesAnimation) {
611 m_animationValid = m_values.size() >= 1
612 && (calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyTimesAttr) || fastHasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
613 && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
614 && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
615 && (!fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
616 if (m_animationValid)
617 m_animationValid = calculateToAtEndOfDurationValue(m_values.last());
618 if (calcMode == CalcModePaced && m_animationValid)
619 calculateKeyTimesForCalcModePaced();
620 } else if (animationMode == PathAnimation)
621 m_animationValid = calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
624 void SVGAnimationElement::updateAnimation(float percent, unsigned repeatCount, SVGSMILElement* resultElement)
626 if (!m_animationValid)
629 float effectivePercent;
630 CalcMode calcMode = this->calcMode();
631 AnimationMode animationMode = this->animationMode();
632 if (animationMode == ValuesAnimation) {
635 currentValuesForValuesAnimation(percent, effectivePercent, from, to);
636 if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
637 m_animationValid = calculateFromAndToValues(from, to);
638 if (!m_animationValid)
640 m_lastValuesAnimationFrom = from;
641 m_lastValuesAnimationTo = to;
643 } else if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
644 effectivePercent = calculatePercentFromKeyPoints(percent);
645 else if (m_keyPoints.isEmpty() && calcMode == CalcModeSpline && m_keyTimes.size() > 1)
646 effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
647 else if (animationMode == FromToAnimation || animationMode == ToAnimation)
648 effectivePercent = calculatePercentForFromTo(percent);
650 effectivePercent = percent;
652 calculateAnimatedValue(effectivePercent, repeatCount, resultElement);
655 void SVGAnimationElement::computeCSSPropertyValue(SVGElement* element, CSSPropertyID id, String& value)
659 // Don't include any properties resulting from CSS Transitions/Animations or SMIL animations, as we want to retrieve the "base value".
660 element->setUseOverrideComputedStyle(true);
661 value = CSSComputedStyleDeclaration::create(element)->getPropertyValue(id);
662 element->setUseOverrideComputedStyle(false);
665 void SVGAnimationElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value)
667 // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
668 // In the future we might want to work with the value type directly to avoid the String parsing.
669 ASSERT(targetElement);
671 Element* parent = targetElement->parentElement();
672 if (!parent || !parent->isSVGElement())
675 SVGElement* svgParent = toSVGElement(parent);
676 computeCSSPropertyValue(svgParent, cssPropertyID(attributeName.localName()), value);
679 static bool inheritsFromProperty(SVGElement* targetElement, const QualifiedName& attributeName, const String& value)
681 ASSERT(targetElement);
682 DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit", AtomicString::ConstructFromLiteral));
684 if (value.isEmpty() || value != inherit)
686 return SVGElement::isAnimatableCSSProperty(attributeName);
689 void SVGAnimationElement::determinePropertyValueTypes(const String& from, const String& to)
691 SVGElement* targetElement = this->targetElement();
692 ASSERT(targetElement);
694 const QualifiedName& attributeName = this->attributeName();
695 if (inheritsFromProperty(targetElement, attributeName, from))
696 m_fromPropertyValueType = InheritValue;
697 if (inheritsFromProperty(targetElement, attributeName, to))
698 m_toPropertyValueType = InheritValue;
701 void SVGAnimationElement::setTargetElement(SVGElement* target)
703 SVGSMILElement::setTargetElement(target);
704 checkInvalidCSSAttributeType(target);
707 void SVGAnimationElement::setAttributeName(const QualifiedName& attributeName)
709 SVGSMILElement::setAttributeName(attributeName);
710 checkInvalidCSSAttributeType(targetElement());
713 void SVGAnimationElement::checkInvalidCSSAttributeType(SVGElement* target)
715 m_hasInvalidCSSAttributeType = target && hasValidAttributeName() && attributeType() == AttributeTypeCSS && !isTargetAttributeCSSProperty(target, attributeName());