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