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