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