Prevent NaN offset values in ElementTimeControl.
[WebKit-https.git] / Source / WebCore / svg / SVGAnimationElement.cpp
1 /*
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.
8  *
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.
13  *
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.
18  *
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.
23  */
24
25 #include "config.h"
26
27 #if ENABLE(SVG)
28 #include "SVGAnimationElement.h"
29
30 #include "Attribute.h"
31 #include "CSSComputedStyleDeclaration.h"
32 #include "CSSParser.h"
33 #include "CSSPropertyNames.h"
34 #include "Document.h"
35 #include "FloatConversion.h"
36 #include "RenderObject.h"
37 #include "SVGAnimateElement.h"
38 #include "SVGElementInstance.h"
39 #include "SVGNames.h"
40 #include "SVGParserUtilities.h"
41 #include "SVGStyledElement.h"
42 #include <wtf/MathExtras.h>
43
44 namespace WebCore {
45
46 // Animated property definitions
47 DEFINE_ANIMATED_BOOLEAN(SVGAnimationElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
48
49 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGAnimationElement)
50     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
51     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
52 END_REGISTER_ANIMATED_PROPERTIES
53
54 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document* document)
55     : SVGSMILElement(tagName, document)
56     , m_fromPropertyValueType(RegularPropertyValue)
57     , m_toPropertyValueType(RegularPropertyValue)
58     , m_animationValid(false)
59     , m_attributeType(AttributeTypeAuto)
60     , m_hasInvalidCSSAttributeType(false)
61 {
62     registerAnimatedPropertiesForSVGAnimationElement();
63 }
64
65 static void parseKeyTimes(const String& parse, Vector<float>& result, bool verifyOrder)
66 {
67     result.clear();
68     Vector<String> parseList;
69     parse.split(';', parseList);
70     for (unsigned n = 0; n < parseList.size(); ++n) {
71         String timeString = parseList[n];
72         bool ok;
73         float time = timeString.toFloat(&ok);
74         if (!ok || time < 0 || time > 1)
75             goto fail;
76         if (verifyOrder) {
77             if (!n) {
78                 if (time)
79                     goto fail;
80             } else if (time < result.last())
81                 goto fail;
82         }
83         result.append(time);
84     }
85     return;
86 fail:
87     result.clear();
88 }
89
90 static void parseKeySplines(const String& parse, Vector<UnitBezier>& result)
91 {
92     result.clear();
93     if (parse.isEmpty())
94         return;
95     const UChar* cur = parse.characters();
96     const UChar* end = cur + parse.length();
97
98     skipOptionalSVGSpaces(cur, end);
99
100     bool delimParsed = false;
101     while (cur < end) {
102         delimParsed = false;
103         float posA = 0;
104         if (!parseNumber(cur, end, posA)) {
105             result.clear();
106             return;
107         }
108
109         float posB = 0;
110         if (!parseNumber(cur, end, posB)) {
111             result.clear();
112             return;
113         }
114
115         float posC = 0;
116         if (!parseNumber(cur, end, posC)) {
117             result.clear();
118             return;
119         }
120
121         float posD = 0;
122         if (!parseNumber(cur, end, posD, false)) {
123             result.clear();
124             return;
125         }
126
127         skipOptionalSVGSpaces(cur, end);
128
129         if (cur < end && *cur == ';') {
130             delimParsed = true;
131             cur++;
132         }
133         skipOptionalSVGSpaces(cur, end);
134
135         result.append(UnitBezier(posA, posB, posC, posD));
136     }
137     if (!(cur == end && !delimParsed))
138         result.clear();
139 }
140
141 bool SVGAnimationElement::isSupportedAttribute(const QualifiedName& attrName)
142 {
143     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
144     if (supportedAttributes.isEmpty()) {
145         SVGTests::addSupportedAttributes(supportedAttributes);
146         SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
147         supportedAttributes.add(SVGNames::valuesAttr);
148         supportedAttributes.add(SVGNames::keyTimesAttr);
149         supportedAttributes.add(SVGNames::keyPointsAttr);
150         supportedAttributes.add(SVGNames::keySplinesAttr);
151         supportedAttributes.add(SVGNames::attributeTypeAttr);
152     }
153     return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
154 }
155
156 void SVGAnimationElement::parseAttribute(const Attribute& attribute)
157 {
158     if (!isSupportedAttribute(attribute.name())) {
159         SVGSMILElement::parseAttribute(attribute);
160         return;
161     }
162
163     if (attribute.name() == SVGNames::valuesAttr) {
164         // Per the SMIL specification, leading and trailing white space,
165         // and white space before and after semicolon separators, is allowed and will be ignored.
166         // http://www.w3.org/TR/SVG11/animate.html#ValuesAttribute
167         attribute.value().string().split(';', m_values);
168         for (unsigned i = 0; i < m_values.size(); ++i)
169             m_values[i] = m_values[i].stripWhiteSpace();
170         return;
171     }
172
173     if (attribute.name() == SVGNames::keyTimesAttr) {
174         parseKeyTimes(attribute.value(), m_keyTimes, true);
175         return;
176     }
177
178     if (attribute.name() == SVGNames::keyPointsAttr) {
179         if (hasTagName(SVGNames::animateMotionTag)) {
180             // This is specified to be an animateMotion attribute only but it is simpler to put it here 
181             // where the other timing calculatations are.
182             parseKeyTimes(attribute.value(), m_keyPoints, false);
183         }
184         return;
185     }
186
187     if (attribute.name() == SVGNames::keySplinesAttr) {
188         parseKeySplines(attribute.value(), m_keySplines);
189         return;
190     }
191
192     if (attribute.name() == SVGNames::attributeTypeAttr) {
193         setAttributeType(attribute.value());
194         return;
195     }
196
197     if (SVGTests::parseAttribute(attribute))
198         return;
199     if (SVGExternalResourcesRequired::parseAttribute(attribute))
200         return;
201
202     ASSERT_NOT_REACHED();
203 }
204
205 void SVGAnimationElement::svgAttributeChanged(const QualifiedName& attrName)
206 {
207     if (!isSupportedAttribute(attrName)) {
208         SVGSMILElement::svgAttributeChanged(attrName);
209         return;
210     }
211
212     animationAttributeChanged();
213 }
214
215 void SVGAnimationElement::animationAttributeChanged()
216 {
217     // Assumptions may not hold after an attribute change.
218     m_animationValid = false;
219     setInactive();
220 }
221
222 float SVGAnimationElement::getStartTime() const
223 {
224     return narrowPrecisionToFloat(intervalBegin().value());
225 }
226
227 float SVGAnimationElement::getCurrentTime() const
228 {
229     return narrowPrecisionToFloat(elapsed().value());
230 }
231
232 float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const
233 {
234     return narrowPrecisionToFloat(simpleDuration().value());
235 }    
236     
237 void SVGAnimationElement::beginElement()
238 {
239     beginElementAt(0);
240 }
241
242 void SVGAnimationElement::beginElementAt(float offset)
243 {
244     if (isnan(offset))
245         return;
246     SMILTime elapsed = this->elapsed();
247     addBeginTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
248 }
249
250 void SVGAnimationElement::endElement()
251 {
252     endElementAt(0);
253 }
254
255 void SVGAnimationElement::endElementAt(float offset)
256 {
257     if (isnan(offset))
258         return;
259     SMILTime elapsed = this->elapsed();
260     addEndTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
261 }
262
263 AnimationMode SVGAnimationElement::animationMode() const
264 {
265     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
266     if (hasTagName(SVGNames::setTag))
267         return ToAnimation;
268     if (!animationPath().isEmpty())
269         return PathAnimation;
270     if (hasAttribute(SVGNames::valuesAttr))
271         return ValuesAnimation;
272     if (!toValue().isEmpty())
273         return fromValue().isEmpty() ? ToAnimation : FromToAnimation;
274     if (!byValue().isEmpty())
275         return fromValue().isEmpty() ? ByAnimation : FromByAnimation;
276     return NoAnimation;
277 }
278
279 CalcMode SVGAnimationElement::calcMode() const
280 {    
281     DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete"));
282     DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear"));
283     DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced"));
284     DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline"));
285     const AtomicString& value = fastGetAttribute(SVGNames::calcModeAttr);
286     if (value == discrete)
287         return CalcModeDiscrete;
288     if (value == linear)
289         return CalcModeLinear;
290     if (value == paced)
291         return CalcModePaced;
292     if (value == spline)
293         return CalcModeSpline;
294     return hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear;
295 }
296
297 void SVGAnimationElement::setAttributeType(const AtomicString& attributeType)
298 {
299     DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS"));
300     DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML"));
301     if (attributeType == css)
302         m_attributeType = AttributeTypeCSS;
303     else if (attributeType == xml)
304         m_attributeType = AttributeTypeXML;
305     else
306         m_attributeType = AttributeTypeAuto;
307     checkInvalidCSSAttributeType(targetElement(DoNotResolveNewTarget));
308 }
309
310 String SVGAnimationElement::toValue() const
311 {    
312     return fastGetAttribute(SVGNames::toAttr);
313 }
314
315 String SVGAnimationElement::byValue() const
316 {    
317     return fastGetAttribute(SVGNames::byAttr);
318 }
319
320 String SVGAnimationElement::fromValue() const
321 {    
322     return fastGetAttribute(SVGNames::fromAttr);
323 }
324
325 bool SVGAnimationElement::isAdditive() const
326 {
327     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
328     const AtomicString& value = fastGetAttribute(SVGNames::additiveAttr);
329     return value == sum || animationMode() == ByAnimation;
330 }
331
332 bool SVGAnimationElement::isAccumulated() const
333 {
334     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
335     const AtomicString& value = fastGetAttribute(SVGNames::accumulateAttr);
336     return value == sum && animationMode() != ToAnimation;
337 }
338
339 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName)
340 {
341     ASSERT(targetElement);
342     if (!targetElement->isStyled())
343         return false;
344
345     return SVGStyledElement::isAnimatableCSSProperty(attributeName);
346 }
347
348 SVGAnimationElement::ShouldApplyAnimation SVGAnimationElement::shouldApplyAnimation(SVGElement* targetElement, const QualifiedName& attributeName)
349 {
350     if (!hasValidAttributeType() || !targetElement || attributeName == anyQName())
351         return DontApplyAnimation;
352
353     // Always animate CSS properties, using the ApplyCSSAnimation code path, regardless of the attributeType value.
354     if (isTargetAttributeCSSProperty(targetElement, attributeName))
355         return ApplyCSSAnimation;
356
357     // If attributeType="CSS" and attributeName doesn't point to a CSS property, ignore the animation.
358     if (attributeType() == AttributeTypeCSS)
359         return DontApplyAnimation;
360
361     return ApplyXMLAnimation;
362 }
363
364 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
365 {
366     ASSERT(calcMode() == CalcModePaced);
367     ASSERT(animationMode() == ValuesAnimation);
368
369     unsigned valuesCount = m_values.size();
370     ASSERT(valuesCount >= 1);
371     if (valuesCount == 1)
372         return;
373     Vector<float> keyTimesForPaced;
374     float totalDistance = 0;
375     keyTimesForPaced.append(0);
376     for (unsigned n = 0; n < valuesCount - 1; ++n) {
377         // Distance in any units
378         float distance = calculateDistance(m_values[n], m_values[n + 1]);
379         if (distance < 0)
380             return;
381         totalDistance += distance;
382         keyTimesForPaced.append(distance);
383     }
384     if (!totalDistance)
385         return;
386
387     // Normalize.
388     for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
389         keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
390     keyTimesForPaced[keyTimesForPaced.size() - 1] = 1;
391
392     // Use key times calculated based on pacing instead of the user provided ones.
393     m_keyTimes.swap(keyTimesForPaced);
394 }
395
396 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
397
398 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
399 {
400     unsigned index;
401     unsigned keyTimesCount = m_keyTimes.size();
402     // Compare index + 1 to keyTimesCount because the last keyTimes entry is
403     // required to be 1, and percent can never exceed 1; i.e., the second last
404     // keyTimes entry defines the beginning of the final interval
405     for (index = 1; index + 1 < keyTimesCount; ++index) {
406         if (m_keyTimes[index] > percent)
407             break;
408     }
409     return --index;
410 }
411
412 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
413 {
414     ASSERT(calcMode() == CalcModeSpline);
415     ASSERT(splineIndex < m_keySplines.size());
416     UnitBezier bezier = m_keySplines[splineIndex];
417     SMILTime duration = simpleDuration();
418     if (!duration.isFinite())
419         duration = 100.0;
420     return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
421 }
422
423 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
424 {
425     ASSERT(!m_keyPoints.isEmpty());
426     ASSERT(calcMode() != CalcModePaced);
427     ASSERT(m_keyTimes.size() > 1);
428     ASSERT(m_keyPoints.size() == m_keyTimes.size());
429
430     if (percent == 1)
431         return m_keyPoints[m_keyPoints.size() - 1];
432
433     unsigned index = calculateKeyTimesIndex(percent);
434     float fromPercent = m_keyTimes[index];
435     float toPercent = m_keyTimes[index + 1];
436     float fromKeyPoint = m_keyPoints[index];
437     float toKeyPoint = m_keyPoints[index + 1];
438     
439     if (calcMode() == CalcModeDiscrete)
440         return fromKeyPoint;
441     
442     float keyPointPercent = (percent - fromPercent) / (toPercent - fromPercent);
443     
444     if (calcMode() == CalcModeSpline) {
445         ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
446         keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
447     }
448     return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
449 }
450
451 float SVGAnimationElement::calculatePercentForFromTo(float percent) const
452 {
453     if (calcMode() == CalcModeDiscrete && m_keyTimes.size() == 2)
454         return percent > m_keyTimes[1] ? 1 : 0;
455
456     return percent;
457 }
458     
459 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
460 {
461     ASSERT(!m_keyPoints.isEmpty());
462     ASSERT(m_keyPoints.size() == m_keyTimes.size());
463     ASSERT(calcMode() != CalcModePaced);
464     effectivePercent = calculatePercentFromKeyPoints(percent);
465     unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
466     from = m_values[index];
467     to = m_values[index + 1];
468 }
469     
470 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to)
471 {
472     unsigned valuesCount = m_values.size();
473     ASSERT(m_animationValid);
474     ASSERT(valuesCount >= 1);
475
476     if (percent == 1 || valuesCount == 1) {
477         from = m_values[valuesCount - 1];
478         to = m_values[valuesCount - 1];
479         effectivePercent = 1;
480         return;
481     }
482
483     CalcMode calcMode = this->calcMode();
484     if (hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::animateColorTag)) {
485         SVGAnimateElement* animateElement = static_cast<SVGAnimateElement*>(this);
486         AnimatedPropertyType attributeType = animateElement->determineAnimatedPropertyType(targetElement());
487         // Fall back to discrete animations for Strings.
488         if (attributeType == AnimatedBoolean
489             || attributeType == AnimatedEnumeration
490             || attributeType == AnimatedPreserveAspectRatio
491             || attributeType == AnimatedString)
492             calcMode = CalcModeDiscrete;
493     }
494     if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
495         return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
496     
497     unsigned keyTimesCount = m_keyTimes.size();
498     ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
499     ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
500
501     unsigned index = calculateKeyTimesIndex(percent);
502     if (calcMode == CalcModeDiscrete) {
503         if (!keyTimesCount) 
504             index = static_cast<unsigned>(percent * valuesCount);
505         from = m_values[index];
506         to = m_values[index];
507         effectivePercent = 0;
508         return;
509     }
510     
511     float fromPercent;
512     float toPercent;
513     if (keyTimesCount) {
514         fromPercent = m_keyTimes[index];
515         toPercent = m_keyTimes[index + 1];
516     } else {        
517         index = static_cast<unsigned>(floorf(percent * (valuesCount - 1)));
518         fromPercent =  static_cast<float>(index) / (valuesCount - 1);
519         toPercent =  static_cast<float>(index + 1) / (valuesCount - 1);
520     }
521     
522     if (index == valuesCount - 1)
523         --index;
524     from = m_values[index];
525     to = m_values[index + 1];
526     ASSERT(toPercent > fromPercent);
527     effectivePercent = (percent - fromPercent) / (toPercent - fromPercent);
528
529     if (calcMode == CalcModeSpline) {
530         ASSERT(m_keySplines.size() == m_values.size() - 1);
531         effectivePercent = calculatePercentForSpline(effectivePercent, index);
532     }
533 }
534     
535 void SVGAnimationElement::startedActiveInterval()
536 {
537     m_animationValid = false;
538
539     if (!hasValidAttributeType())
540         return;
541
542     // These validations are appropriate for all animation modes.
543     if (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
544         return;
545
546     AnimationMode animationMode = this->animationMode();
547     CalcMode calcMode = this->calcMode();
548     if (calcMode == CalcModeSpline) {
549         unsigned splinesCount = m_keySplines.size() + 1;
550         if ((fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != splinesCount)
551             || (animationMode == ValuesAnimation && m_values.size() != splinesCount)
552             || (fastHasAttribute(SVGNames::keyTimesAttr) && m_keyTimes.size() != splinesCount))
553             return;
554     }
555
556     String from = fromValue();
557     String to = toValue();
558     String by = byValue();
559     if (animationMode == NoAnimation)
560         return;
561     if (animationMode == FromToAnimation)
562         m_animationValid = calculateFromAndToValues(from, to);
563     else if (animationMode == ToAnimation) {
564         // For to-animations the from value is the current accumulated value from lower priority animations.
565         // The value is not static and is determined during the animation.
566         m_animationValid = calculateFromAndToValues(emptyString(), to);
567     } else if (animationMode == FromByAnimation)
568         m_animationValid = calculateFromAndByValues(from, by);
569     else if (animationMode == ByAnimation)
570         m_animationValid = calculateFromAndByValues(emptyString(), by);
571     else if (animationMode == ValuesAnimation) {
572         m_animationValid = m_values.size() >= 1
573             && (calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyTimesAttr) || fastHasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
574             && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
575             && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
576             && (!fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
577         if (m_animationValid)
578             m_animationValid = calculateToAtEndOfDurationValue(m_values.last());
579         if (calcMode == CalcModePaced && m_animationValid)
580             calculateKeyTimesForCalcModePaced();
581     } else if (animationMode == PathAnimation)
582         m_animationValid = calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
583 }
584
585 void SVGAnimationElement::updateAnimation(float percent, unsigned repeatCount, SVGSMILElement* resultElement)
586 {    
587     if (!m_animationValid)
588         return;
589
590     float effectivePercent;
591     CalcMode calcMode = this->calcMode();
592     AnimationMode animationMode = this->animationMode();
593     if (animationMode == ValuesAnimation) {
594         String from;
595         String to;
596         currentValuesForValuesAnimation(percent, effectivePercent, from, to);
597         if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
598             m_animationValid = calculateFromAndToValues(from, to);
599             if (!m_animationValid)
600                 return;
601             m_lastValuesAnimationFrom = from;
602             m_lastValuesAnimationTo = to;
603         }
604     } else if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
605         effectivePercent = calculatePercentFromKeyPoints(percent);
606     else if (m_keyPoints.isEmpty() && calcMode == CalcModeSpline && m_keyTimes.size() > 1)
607         effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
608     else if (animationMode == FromToAnimation || animationMode == ToAnimation)
609         effectivePercent = calculatePercentForFromTo(percent);
610     else
611         effectivePercent = percent;
612
613     calculateAnimatedValue(effectivePercent, repeatCount, resultElement);
614 }
615
616 void SVGAnimationElement::computeCSSPropertyValue(SVGElement* element, CSSPropertyID id, String& value)
617 {
618     ASSERT(element);
619     ASSERT(element->isStyled());
620
621     // Don't include any properties resulting from CSS Transitions/Animations or SMIL animations, as we want to retrieve the "base value".
622     element->setUseOverrideComputedStyle(true);
623     value = CSSComputedStyleDeclaration::create(element)->getPropertyValue(id);
624     element->setUseOverrideComputedStyle(false);
625 }
626
627 void SVGAnimationElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value)
628 {
629     // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
630     // In the future we might want to work with the value type directly to avoid the String parsing.
631     ASSERT(targetElement);
632
633     Element* parent = targetElement->parentElement();
634     if (!parent || !parent->isSVGElement())
635         return;
636
637     SVGElement* svgParent = static_cast<SVGElement*>(parent);
638     if (!svgParent->isStyled())
639         return;
640     computeCSSPropertyValue(svgParent, cssPropertyID(attributeName.localName()), value);
641 }
642
643 static bool inheritsFromProperty(SVGElement* targetElement, const QualifiedName& attributeName, const String& value)
644 {
645     ASSERT(targetElement);
646     DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit"));
647     
648     if (value.isEmpty() || value != inherit || !targetElement->isStyled())
649         return false;
650     return SVGStyledElement::isAnimatableCSSProperty(attributeName);
651 }
652
653 void SVGAnimationElement::determinePropertyValueTypes(const String& from, const String& to)
654 {
655     SVGElement* targetElement = this->targetElement();
656     ASSERT(targetElement);
657
658     const QualifiedName& attributeName = this->attributeName();
659     if (inheritsFromProperty(targetElement, attributeName, from))
660         m_fromPropertyValueType = InheritValue;
661     if (inheritsFromProperty(targetElement, attributeName, to))
662         m_toPropertyValueType = InheritValue;
663 }
664
665 void SVGAnimationElement::targetElementWillChange(SVGElement* currentTarget, SVGElement* newTarget)
666 {
667     SVGSMILElement::targetElementWillChange(currentTarget, newTarget);
668
669     checkInvalidCSSAttributeType(newTarget);
670 }
671
672 void SVGAnimationElement::setAttributeName(const QualifiedName& attributeName)
673 {
674     SVGSMILElement::setAttributeName(attributeName);
675
676     checkInvalidCSSAttributeType(targetElement(DoNotResolveNewTarget));
677 }
678
679 void SVGAnimationElement::checkInvalidCSSAttributeType(SVGElement* target)
680 {
681     m_hasInvalidCSSAttributeType = target && hasValidAttributeName() && attributeType() == AttributeTypeCSS && !isTargetAttributeCSSProperty(target, attributeName());
682 }
683
684 }
685
686 #endif // ENABLE(SVG)