Support values animation mode with just a single value
[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     if (valuesCount == 1)
398         return;
399     Vector<float> keyTimesForPaced;
400     float totalDistance = 0;
401     keyTimesForPaced.append(0);
402     for (unsigned n = 0; n < valuesCount - 1; ++n) {
403         // Distance in any units
404         float distance = calculateDistance(m_values[n], m_values[n + 1]);
405         if (distance < 0)
406             return;
407         totalDistance += distance;
408         keyTimesForPaced.append(distance);
409     }
410     if (!totalDistance)
411         return;
412
413     // Normalize.
414     for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
415         keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
416     keyTimesForPaced[keyTimesForPaced.size() - 1] = 1;
417
418     // Use key times calculated based on pacing instead of the user provided ones.
419     m_keyTimes.swap(keyTimesForPaced);
420 }
421
422 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
423
424 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
425 {
426     unsigned index;
427     unsigned keyTimesCount = m_keyTimes.size();
428     // Compare index + 1 to keyTimesCount because the last keyTimes entry is
429     // required to be 1, and percent can never exceed 1; i.e., the second last
430     // keyTimes entry defines the beginning of the final interval
431     for (index = 1; index + 1 < keyTimesCount; ++index) {
432         if (m_keyTimes[index] > percent)
433             break;
434     }
435     return --index;
436 }
437
438 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
439 {
440     ASSERT(calcMode() == CalcModeSpline);
441     ASSERT(splineIndex < m_keySplines.size());
442     UnitBezier bezier = m_keySplines[splineIndex];
443     SMILTime duration = simpleDuration();
444     if (!duration.isFinite())
445         duration = 100.0;
446     return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
447 }
448
449 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
450 {
451     ASSERT(!m_keyPoints.isEmpty());
452     ASSERT(calcMode() != CalcModePaced);
453     ASSERT(m_keyTimes.size() > 1);
454     ASSERT(m_keyPoints.size() == m_keyTimes.size());
455
456     if (percent == 1)
457         return m_keyPoints[m_keyPoints.size() - 1];
458
459     unsigned index = calculateKeyTimesIndex(percent);
460     float fromPercent = m_keyTimes[index];
461     float toPercent = m_keyTimes[index + 1];
462     float fromKeyPoint = m_keyPoints[index];
463     float toKeyPoint = m_keyPoints[index + 1];
464     
465     if (calcMode() == CalcModeDiscrete)
466         return fromKeyPoint;
467     
468     float keyPointPercent = (percent - fromPercent) / (toPercent - fromPercent);
469     
470     if (calcMode() == CalcModeSpline) {
471         ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
472         keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
473     }
474     return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
475 }
476
477 float SVGAnimationElement::calculatePercentForFromTo(float percent) const
478 {
479     if (calcMode() == CalcModeDiscrete && m_keyTimes.size() == 2)
480         return percent > m_keyTimes[1] ? 1 : 0;
481
482     return percent;
483 }
484     
485 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
486 {
487     ASSERT(!m_keyPoints.isEmpty());
488     ASSERT(m_keyPoints.size() == m_keyTimes.size());
489     ASSERT(calcMode() != CalcModePaced);
490     effectivePercent = calculatePercentFromKeyPoints(percent);
491     unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
492     from = m_values[index];
493     to = m_values[index + 1];
494 }
495     
496 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to)
497 {
498     unsigned valuesCount = m_values.size();
499     ASSERT(m_animationValid);
500     ASSERT(valuesCount >= 1);
501
502     if (percent == 1 || valuesCount == 1) {
503         from = m_values[valuesCount - 1];
504         to = m_values[valuesCount - 1];
505         effectivePercent = 1;
506         return;
507     }
508
509     CalcMode calcMode = this->calcMode();
510     if (hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::animateColorTag)) {
511         SVGAnimateElement* animateElement = static_cast<SVGAnimateElement*>(this);
512         AnimatedPropertyType attributeType = animateElement->determineAnimatedPropertyType(targetElement());
513         // Fall back to discrete animations for Strings.
514         if (attributeType == AnimatedBoolean
515             || attributeType == AnimatedEnumeration
516             || attributeType == AnimatedPreserveAspectRatio
517             || attributeType == AnimatedString)
518             calcMode = CalcModeDiscrete;
519     }
520     if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
521         return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
522     
523     unsigned keyTimesCount = m_keyTimes.size();
524     ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
525     ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
526
527     unsigned index = calculateKeyTimesIndex(percent);
528     if (calcMode == CalcModeDiscrete) {
529         if (!keyTimesCount) 
530             index = static_cast<unsigned>(percent * valuesCount);
531         from = m_values[index];
532         to = m_values[index];
533         effectivePercent = 0;
534         return;
535     }
536     
537     float fromPercent;
538     float toPercent;
539     if (keyTimesCount) {
540         fromPercent = m_keyTimes[index];
541         toPercent = m_keyTimes[index + 1];
542     } else {        
543         index = static_cast<unsigned>(percent * (valuesCount - 1));
544         fromPercent =  static_cast<float>(index) / (valuesCount - 1);
545         toPercent =  static_cast<float>(index + 1) / (valuesCount - 1);
546     }
547     
548     if (index == valuesCount - 1)
549         --index;
550     from = m_values[index];
551     to = m_values[index + 1];
552     ASSERT(toPercent > fromPercent);
553     effectivePercent = (percent - fromPercent) / (toPercent - fromPercent);
554     
555     if (calcMode == CalcModeSpline) {
556         ASSERT(m_keySplines.size() == m_values.size() - 1);
557         effectivePercent = calculatePercentForSpline(effectivePercent, index);
558     }
559 }
560     
561 void SVGAnimationElement::startedActiveInterval()
562 {
563     m_animationValid = false;
564
565     if (!hasValidAttributeType())
566         return;
567
568     // These validations are appropriate for all animation modes.
569     if (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
570         return;
571
572     AnimationMode animationMode = this->animationMode();
573     CalcMode calcMode = this->calcMode();
574     if (calcMode == CalcModeSpline) {
575         unsigned splinesCount = m_keySplines.size() + 1;
576         if ((fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != splinesCount)
577             || (animationMode == ValuesAnimation && m_values.size() != splinesCount)
578             || (fastHasAttribute(SVGNames::keyTimesAttr) && m_keyTimes.size() != splinesCount))
579             return;
580     }
581
582     String from = fromValue();
583     String to = toValue();
584     String by = byValue();
585     if (animationMode == NoAnimation)
586         return;
587     if (animationMode == FromToAnimation)
588         m_animationValid = calculateFromAndToValues(from, to);
589     else if (animationMode == ToAnimation) {
590         // For to-animations the from value is the current accumulated value from lower priority animations.
591         // The value is not static and is determined during the animation.
592         m_animationValid = calculateFromAndToValues(emptyString(), to);
593     } else if (animationMode == FromByAnimation)
594         m_animationValid = calculateFromAndByValues(from, by);
595     else if (animationMode == ByAnimation)
596         m_animationValid = calculateFromAndByValues(emptyString(), by);
597     else if (animationMode == ValuesAnimation) {
598         m_animationValid = m_values.size() >= 1
599             && (calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyTimesAttr) || fastHasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
600             && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
601             && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
602             && (!fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
603         if (calcMode == CalcModePaced && m_animationValid)
604             calculateKeyTimesForCalcModePaced();
605     } else if (animationMode == PathAnimation)
606         m_animationValid = calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
607 }
608
609 void SVGAnimationElement::updateAnimation(float percent, unsigned repeat, SVGSMILElement* resultElement)
610 {    
611     if (!m_animationValid)
612         return;
613     
614     float effectivePercent;
615     CalcMode mode = calcMode();
616     if (animationMode() == ValuesAnimation) {
617         String from;
618         String to;
619         currentValuesForValuesAnimation(percent, effectivePercent, from, to);
620         if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
621             m_animationValid = calculateFromAndToValues(from, to);
622             if (!m_animationValid)
623                 return;
624             m_lastValuesAnimationFrom = from;
625             m_lastValuesAnimationTo = to;
626         }
627     } else if (!m_keyPoints.isEmpty() && mode != CalcModePaced)
628         effectivePercent = calculatePercentFromKeyPoints(percent);
629     else if (m_keyPoints.isEmpty() && mode == CalcModeSpline && m_keyTimes.size() > 1)
630         effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
631     else if (animationMode() == FromToAnimation || animationMode() == ToAnimation)
632         effectivePercent = calculatePercentForFromTo(percent);
633     else
634         effectivePercent = percent;
635
636     calculateAnimatedValue(effectivePercent, repeat, resultElement);
637 }
638
639 void SVGAnimationElement::computeCSSPropertyValue(SVGElement* element, CSSPropertyID id, String& value, bool includeSMILProperties)
640 {
641     ASSERT(element);
642     ASSERT(element->isStyled());
643
644     // If includeSMILProperties=false, don't include any properties resulting from SMIL animations, as we want to retrieve the "base value".
645     if (!includeSMILProperties) {
646         ASSERT(element == targetElement());
647         element->setUseOverrideComputedStyle(true);
648     }
649
650     value = CSSComputedStyleDeclaration::create(element)->getPropertyValue(id);
651     if (!includeSMILProperties)
652         element->setUseOverrideComputedStyle(false);
653 }
654
655 void SVGAnimationElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value)
656 {
657     // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
658     // In the future we might want to work with the value type directly to avoid the String parsing.
659     ASSERT(targetElement);
660
661     Element* parent = targetElement->parentElement();
662     if (!parent || !parent->isSVGElement())
663         return;
664
665     SVGElement* svgParent = static_cast<SVGElement*>(parent);
666     if (!svgParent->isStyled())
667         return;
668     computeCSSPropertyValue(svgParent, cssPropertyID(attributeName.localName()), value, true);
669 }
670
671 static bool inheritsFromProperty(SVGElement* targetElement, const QualifiedName& attributeName, const String& value)
672 {
673     ASSERT(targetElement);
674     DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit"));
675     
676     if (value.isEmpty() || value != inherit || !targetElement->isStyled())
677         return false;
678     return SVGStyledElement::isAnimatableCSSProperty(attributeName);
679 }
680
681 void SVGAnimationElement::determinePropertyValueTypes(const String& from, const String& to)
682 {
683     SVGElement* targetElement = this->targetElement();
684     ASSERT(targetElement);
685
686     const QualifiedName& attributeName = this->attributeName();
687     if (inheritsFromProperty(targetElement, attributeName, from))
688         m_fromPropertyValueType = InheritValue;
689     if (inheritsFromProperty(targetElement, attributeName, to))
690         m_toPropertyValueType = InheritValue;
691 }
692
693 }
694
695 #endif // ENABLE(SVG)