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