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 "core/CSSPropertyNames.h"
30 #include "core/SVGNames.h"
31 #include "core/css/CSSComputedStyleDeclaration.h"
32 #include "core/css/parser/CSSParser.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)
44 , m_fromPropertyValueType(RegularPropertyValue)
45 , m_toPropertyValueType(RegularPropertyValue)
46 , m_animationValid(false)
47 , m_attributeType(AttributeTypeAuto)
48 , m_hasInvalidCSSAttributeType(false)
49 , m_calcMode(CalcModeLinear)
50 , m_animationMode(NoAnimation)
52 UseCounter::count(document, UseCounter::SVGAnimationElement);
55 static bool parseValues(const String& value, Vector<String>& result)
57 // Per the SMIL specification, leading and trailing white space,
58 // and white space before and after semicolon separators, is allowed and will be ignored.
59 // http://www.w3.org/TR/SVG11/animate.html#ValuesAttribute
61 Vector<String> parseList;
62 value.split(';', true, parseList);
63 unsigned last = parseList.size() - 1;
64 for (unsigned i = 0; i <= last; ++i) {
65 if (parseList[i].isEmpty()) {
66 // Tolerate trailing ';'
70 parseList[i] = parseList[i].stripWhiteSpace();
71 result.append(parseList[i]);
81 static bool parseKeyTimes(const String& string, Vector<float>& result, bool verifyOrder)
84 Vector<String> parseList;
85 string.split(';', true, parseList);
86 for (unsigned n = 0; n < parseList.size(); ++n) {
87 String timeString = parseList[n];
89 float time = timeString.toFloat(&ok);
90 if (!ok || time < 0 || time > 1)
96 } else if (time < result.last())
107 template<typename CharType>
108 static bool parseKeySplinesInternal(const String& string, Vector<UnitBezier>& result)
110 const CharType* ptr = string.getCharacters<CharType>();
111 const CharType* end = ptr + string.length();
113 skipOptionalSVGSpaces(ptr, end);
117 if (!parseNumber(ptr, end, posA))
121 if (!parseNumber(ptr, end, posB))
125 if (!parseNumber(ptr, end, posC))
129 if (!parseNumber(ptr, end, posD, DisallowWhitespace))
132 skipOptionalSVGSpaces(ptr, end);
134 if (ptr < end && *ptr == ';')
136 skipOptionalSVGSpaces(ptr, end);
138 result.append(UnitBezier(posA, posB, posC, posD));
144 static bool parseKeySplines(const String& string, Vector<UnitBezier>& result)
147 if (string.isEmpty())
151 parsed = parseKeySplinesInternal<LChar>(string, result);
153 parsed = parseKeySplinesInternal<UChar>(string, result);
161 bool SVGAnimationElement::isSupportedAttribute(const QualifiedName& attrName)
163 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
164 if (supportedAttributes.isEmpty()) {
165 supportedAttributes.add(SVGNames::valuesAttr);
166 supportedAttributes.add(SVGNames::keyTimesAttr);
167 supportedAttributes.add(SVGNames::keyPointsAttr);
168 supportedAttributes.add(SVGNames::keySplinesAttr);
169 supportedAttributes.add(SVGNames::attributeTypeAttr);
170 supportedAttributes.add(SVGNames::calcModeAttr);
171 supportedAttributes.add(SVGNames::fromAttr);
172 supportedAttributes.add(SVGNames::toAttr);
173 supportedAttributes.add(SVGNames::byAttr);
175 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
178 void SVGAnimationElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
180 if (!isSupportedAttribute(name)) {
181 SVGSMILElement::parseAttribute(name, value);
185 if (name == SVGNames::valuesAttr) {
186 if (!parseValues(value, m_values)) {
187 reportAttributeParsingError(ParsingAttributeFailedError, name, value);
190 updateAnimationMode();
194 if (name == SVGNames::keyTimesAttr) {
195 if (!parseKeyTimes(value, m_keyTimes, true))
196 reportAttributeParsingError(ParsingAttributeFailedError, name, value);
200 if (name == SVGNames::keyPointsAttr) {
201 if (isSVGAnimateMotionElement(*this)) {
202 // This is specified to be an animateMotion attribute only but it is simpler to put it here
203 // where the other timing calculatations are.
204 if (!parseKeyTimes(value, m_keyPoints, false))
205 reportAttributeParsingError(ParsingAttributeFailedError, name, value);
210 if (name == SVGNames::keySplinesAttr) {
211 if (!parseKeySplines(value, m_keySplines))
212 reportAttributeParsingError(ParsingAttributeFailedError, name, value);
216 if (name == SVGNames::attributeTypeAttr) {
217 setAttributeType(value);
221 if (name == SVGNames::calcModeAttr) {
226 if (name == SVGNames::fromAttr || name == SVGNames::toAttr || name == SVGNames::byAttr) {
227 updateAnimationMode();
231 ASSERT_NOT_REACHED();
234 void SVGAnimationElement::svgAttributeChanged(const QualifiedName& attrName)
236 if (!isSupportedAttribute(attrName)) {
237 SVGSMILElement::svgAttributeChanged(attrName);
241 animationAttributeChanged();
244 void SVGAnimationElement::animationAttributeChanged()
246 // Assumptions may not hold after an attribute change.
247 m_animationValid = false;
248 m_lastValuesAnimationFrom = String();
249 m_lastValuesAnimationTo = String();
253 float SVGAnimationElement::getStartTime() const
255 return narrowPrecisionToFloat(intervalBegin().value());
258 float SVGAnimationElement::getCurrentTime() const
260 return narrowPrecisionToFloat(elapsed().value());
263 float SVGAnimationElement::getSimpleDuration() const
265 return narrowPrecisionToFloat(simpleDuration().value());
268 void SVGAnimationElement::beginElement()
273 void SVGAnimationElement::beginElementAt(float offset)
275 if (!std::isfinite(offset))
277 SMILTime elapsed = this->elapsed();
278 addBeginTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
281 void SVGAnimationElement::endElement()
286 void SVGAnimationElement::endElementAt(float offset)
288 if (!std::isfinite(offset))
290 SMILTime elapsed = this->elapsed();
291 addEndTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
294 void SVGAnimationElement::updateAnimationMode()
296 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
297 if (hasAttribute(SVGNames::valuesAttr))
298 setAnimationMode(ValuesAnimation);
299 else if (!toValue().isEmpty())
300 setAnimationMode(fromValue().isEmpty() ? ToAnimation : FromToAnimation);
301 else if (!byValue().isEmpty())
302 setAnimationMode(fromValue().isEmpty() ? ByAnimation : FromByAnimation);
304 setAnimationMode(NoAnimation);
307 void SVGAnimationElement::setCalcMode(const AtomicString& calcMode)
309 DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete", AtomicString::ConstructFromLiteral));
310 DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear", AtomicString::ConstructFromLiteral));
311 DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced", AtomicString::ConstructFromLiteral));
312 DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline", AtomicString::ConstructFromLiteral));
313 if (calcMode == discrete)
314 setCalcMode(CalcModeDiscrete);
315 else if (calcMode == linear)
316 setCalcMode(CalcModeLinear);
317 else if (calcMode == paced)
318 setCalcMode(CalcModePaced);
319 else if (calcMode == spline)
320 setCalcMode(CalcModeSpline);
322 setCalcMode(isSVGAnimateMotionElement(*this) ? CalcModePaced : CalcModeLinear);
325 void SVGAnimationElement::setAttributeType(const AtomicString& attributeType)
327 DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS", AtomicString::ConstructFromLiteral));
328 DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML", AtomicString::ConstructFromLiteral));
329 if (attributeType == css)
330 m_attributeType = AttributeTypeCSS;
331 else if (attributeType == xml)
332 m_attributeType = AttributeTypeXML;
334 m_attributeType = AttributeTypeAuto;
335 checkInvalidCSSAttributeType(targetElement());
338 String SVGAnimationElement::toValue() const
340 return fastGetAttribute(SVGNames::toAttr);
343 String SVGAnimationElement::byValue() const
345 return fastGetAttribute(SVGNames::byAttr);
348 String SVGAnimationElement::fromValue() const
350 return fastGetAttribute(SVGNames::fromAttr);
353 bool SVGAnimationElement::isAdditive()
355 DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum", AtomicString::ConstructFromLiteral));
356 const AtomicString& value = fastGetAttribute(SVGNames::additiveAttr);
357 return value == sum || animationMode() == ByAnimation;
360 bool SVGAnimationElement::isAccumulated() const
362 DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum", AtomicString::ConstructFromLiteral));
363 const AtomicString& value = fastGetAttribute(SVGNames::accumulateAttr);
364 return value == sum && animationMode() != ToAnimation;
367 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName)
369 ASSERT(targetElement);
371 return SVGElement::isAnimatableCSSProperty(attributeName);
374 SVGAnimationElement::ShouldApplyAnimation SVGAnimationElement::shouldApplyAnimation(SVGElement* targetElement, const QualifiedName& attributeName)
376 if (!hasValidAttributeType() || !targetElement || attributeName == anyQName())
377 return DontApplyAnimation;
379 // Always animate CSS properties, using the ApplyCSSAnimation code path, regardless of the attributeType value.
380 if (isTargetAttributeCSSProperty(targetElement, attributeName))
381 return ApplyCSSAnimation;
383 // If attributeType="CSS" and attributeName doesn't point to a CSS property, ignore the animation.
384 if (attributeType() == AttributeTypeCSS)
385 return DontApplyAnimation;
387 return ApplyXMLAnimation;
390 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
392 ASSERT(calcMode() == CalcModePaced);
393 ASSERT(animationMode() == ValuesAnimation);
395 unsigned valuesCount = m_values.size();
396 ASSERT(valuesCount >= 1);
397 if (valuesCount == 1)
400 // FIXME, webkit.org/b/109010: m_keyTimes should not be modified in this function.
403 Vector<float> keyTimesForPaced;
404 float totalDistance = 0;
405 keyTimesForPaced.append(0);
406 for (unsigned n = 0; n < valuesCount - 1; ++n) {
407 // Distance in any units
408 float distance = calculateDistance(m_values[n], m_values[n + 1]);
411 totalDistance += distance;
412 keyTimesForPaced.append(distance);
418 for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
419 keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
420 keyTimesForPaced[keyTimesForPaced.size() - 1] = 1;
422 // Use key times calculated based on pacing instead of the user provided ones.
423 m_keyTimes = keyTimesForPaced;
426 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
428 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
431 unsigned keyTimesCount = m_keyTimes.size();
432 // For linear and spline animations, the last value must be '1'. In those
433 // cases we don't need to consider the last value, since |percent| is never
435 if (keyTimesCount && calcMode() != CalcModeDiscrete)
437 for (index = 1; index < keyTimesCount; ++index) {
438 if (m_keyTimes[index] > percent)
444 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
446 ASSERT(calcMode() == CalcModeSpline);
447 ASSERT_WITH_SECURITY_IMPLICATION(splineIndex < m_keySplines.size());
448 UnitBezier bezier = m_keySplines[splineIndex];
449 SMILTime duration = simpleDuration();
450 if (!duration.isFinite())
452 return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
455 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
457 ASSERT(!m_keyPoints.isEmpty());
458 ASSERT(calcMode() != CalcModePaced);
459 ASSERT(m_keyTimes.size() > 1);
460 ASSERT(m_keyPoints.size() == m_keyTimes.size());
463 return m_keyPoints[m_keyPoints.size() - 1];
465 unsigned index = calculateKeyTimesIndex(percent);
466 float fromKeyPoint = m_keyPoints[index];
468 if (calcMode() == CalcModeDiscrete)
471 ASSERT(index + 1 < m_keyTimes.size());
472 float fromPercent = m_keyTimes[index];
473 float toPercent = m_keyTimes[index + 1];
474 float toKeyPoint = m_keyPoints[index + 1];
475 float keyPointPercent = (percent - fromPercent) / (toPercent - fromPercent);
477 if (calcMode() == CalcModeSpline) {
478 ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
479 keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
481 return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
484 float SVGAnimationElement::calculatePercentForFromTo(float percent) const
486 if (calcMode() == CalcModeDiscrete && m_keyTimes.size() == 2)
487 return percent > m_keyTimes[1] ? 1 : 0;
492 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
494 ASSERT(!m_keyPoints.isEmpty());
495 ASSERT(m_keyPoints.size() == m_keyTimes.size());
496 ASSERT(calcMode() != CalcModePaced);
497 effectivePercent = calculatePercentFromKeyPoints(percent);
498 unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
499 from = m_values[index];
500 to = m_values[index + 1];
503 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to)
505 unsigned valuesCount = m_values.size();
506 ASSERT(m_animationValid);
507 ASSERT(valuesCount >= 1);
509 if (percent == 1 || valuesCount == 1) {
510 from = m_values[valuesCount - 1];
511 to = m_values[valuesCount - 1];
512 effectivePercent = 1;
516 CalcMode calcMode = this->calcMode();
517 if (isSVGAnimateElement(*this)) {
518 SVGAnimateElement& animateElement = toSVGAnimateElement(*this);
519 if (!animateElement.animatedPropertyTypeSupportsAddition()) {
520 ASSERT(animateElement.animatedPropertyType() != AnimatedTransformList || isSVGAnimateTransformElement(*this));
521 ASSERT(animateElement.animatedPropertyType() != AnimatedUnknown);
522 calcMode = CalcModeDiscrete;
525 if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
526 return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
528 unsigned keyTimesCount = m_keyTimes.size();
529 ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
530 ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
532 unsigned index = calculateKeyTimesIndex(percent);
533 if (calcMode == CalcModeDiscrete) {
535 index = static_cast<unsigned>(percent * valuesCount);
536 from = m_values[index];
537 to = m_values[index];
538 effectivePercent = 0;
545 fromPercent = m_keyTimes[index];
546 toPercent = m_keyTimes[index + 1];
548 index = static_cast<unsigned>(floorf(percent * (valuesCount - 1)));
549 fromPercent = static_cast<float>(index) / (valuesCount - 1);
550 toPercent = static_cast<float>(index + 1) / (valuesCount - 1);
553 if (index == valuesCount - 1)
555 from = m_values[index];
556 to = m_values[index + 1];
557 ASSERT(toPercent > fromPercent);
558 effectivePercent = (percent - fromPercent) / (toPercent - fromPercent);
560 if (calcMode == CalcModeSpline) {
561 ASSERT(m_keySplines.size() == m_values.size() - 1);
562 effectivePercent = calculatePercentForSpline(effectivePercent, index);
566 void SVGAnimationElement::startedActiveInterval()
568 m_animationValid = false;
573 if (!hasValidAttributeType())
576 // These validations are appropriate for all animation modes.
577 if (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
580 AnimationMode animationMode = this->animationMode();
581 CalcMode calcMode = this->calcMode();
582 if (calcMode == CalcModeSpline) {
583 unsigned splinesCount = m_keySplines.size();
585 || (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() - 1 != splinesCount)
586 || (animationMode == ValuesAnimation && m_values.size() - 1 != splinesCount)
587 || (fastHasAttribute(SVGNames::keyTimesAttr) && m_keyTimes.size() - 1 != splinesCount))
591 String from = fromValue();
592 String to = toValue();
593 String by = byValue();
594 if (animationMode == NoAnimation)
596 if ((animationMode == FromToAnimation || animationMode == FromByAnimation || animationMode == ToAnimation || animationMode == ByAnimation)
597 && (fastHasAttribute(SVGNames::keyPointsAttr) && fastHasAttribute(SVGNames::keyTimesAttr) && (m_keyTimes.size() < 2 || m_keyTimes.size() != m_keyPoints.size())))
599 if (animationMode == FromToAnimation)
600 m_animationValid = calculateFromAndToValues(from, to);
601 else if (animationMode == ToAnimation) {
602 // For to-animations the from value is the current accumulated value from lower priority animations.
603 // The value is not static and is determined during the animation.
604 m_animationValid = calculateFromAndToValues(emptyString(), to);
605 } else if (animationMode == FromByAnimation)
606 m_animationValid = calculateFromAndByValues(from, by);
607 else if (animationMode == ByAnimation)
608 m_animationValid = calculateFromAndByValues(emptyString(), by);
609 else if (animationMode == ValuesAnimation) {
610 m_animationValid = m_values.size() >= 1
611 && (calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyTimesAttr) || fastHasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
612 && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
613 && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
614 && (!fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
615 if (m_animationValid)
616 m_animationValid = calculateToAtEndOfDurationValue(m_values.last());
617 if (calcMode == CalcModePaced && m_animationValid)
618 calculateKeyTimesForCalcModePaced();
619 } else if (animationMode == PathAnimation)
620 m_animationValid = calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
623 void SVGAnimationElement::updateAnimation(float percent, unsigned repeatCount, SVGSMILElement* resultElement)
625 if (!m_animationValid)
628 float effectivePercent;
629 CalcMode calcMode = this->calcMode();
630 AnimationMode animationMode = this->animationMode();
631 if (animationMode == ValuesAnimation) {
634 currentValuesForValuesAnimation(percent, effectivePercent, from, to);
635 if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
636 m_animationValid = calculateFromAndToValues(from, to);
637 if (!m_animationValid)
639 m_lastValuesAnimationFrom = from;
640 m_lastValuesAnimationTo = to;
642 } else if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
643 effectivePercent = calculatePercentFromKeyPoints(percent);
644 else if (m_keyPoints.isEmpty() && calcMode == CalcModeSpline && m_keyTimes.size() > 1)
645 effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
646 else if (animationMode == FromToAnimation || animationMode == ToAnimation)
647 effectivePercent = calculatePercentForFromTo(percent);
649 effectivePercent = percent;
651 calculateAnimatedValue(effectivePercent, repeatCount, resultElement);
654 void SVGAnimationElement::computeCSSPropertyValue(SVGElement* element, CSSPropertyID id, String& value)
658 // Don't include any properties resulting from CSS Transitions/Animations or SMIL animations, as we want to retrieve the "base value".
659 element->setUseOverrideComputedStyle(true);
660 value = CSSComputedStyleDeclaration::create(element)->getPropertyValue(id);
661 element->setUseOverrideComputedStyle(false);
664 void SVGAnimationElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value)
666 // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
667 // In the future we might want to work with the value type directly to avoid the String parsing.
668 ASSERT(targetElement);
670 Element* parent = targetElement->parentElement();
671 if (!parent || !parent->isSVGElement())
674 SVGElement* svgParent = toSVGElement(parent);
675 computeCSSPropertyValue(svgParent, cssPropertyID(attributeName.localName()), value);
678 static bool inheritsFromProperty(SVGElement* targetElement, const QualifiedName& attributeName, const String& value)
680 ASSERT(targetElement);
681 DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit", AtomicString::ConstructFromLiteral));
683 if (value.isEmpty() || value != inherit)
685 return SVGElement::isAnimatableCSSProperty(attributeName);
688 void SVGAnimationElement::determinePropertyValueTypes(const String& from, const String& to)
690 SVGElement* targetElement = this->targetElement();
691 ASSERT(targetElement);
693 const QualifiedName& attributeName = this->attributeName();
694 if (inheritsFromProperty(targetElement, attributeName, from))
695 m_fromPropertyValueType = InheritValue;
696 if (inheritsFromProperty(targetElement, attributeName, to))
697 m_toPropertyValueType = InheritValue;
700 void SVGAnimationElement::setTargetElement(SVGElement* target)
702 SVGSMILElement::setTargetElement(target);
703 checkInvalidCSSAttributeType(target);
706 void SVGAnimationElement::setAttributeName(const QualifiedName& attributeName)
708 SVGSMILElement::setAttributeName(attributeName);
709 checkInvalidCSSAttributeType(targetElement());
712 void SVGAnimationElement::checkInvalidCSSAttributeType(SVGElement* target)
714 m_hasInvalidCSSAttributeType = target && hasValidAttributeName() && attributeType() == AttributeTypeCSS && !isTargetAttributeCSSProperty(target, attributeName());