Fix keyTimes list length of from/to/by animations.
[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 "SVGElementInstance.h"
39 #include "SVGNames.h"
40 #include "SVGParserUtilities.h"
41 #include <wtf/MathExtras.h>
42 #include <wtf/NeverDestroyed.h>
43 #include <wtf/text/StringView.h>
44
45 namespace WebCore {
46
47 // Animated property definitions
48 DEFINE_ANIMATED_BOOLEAN(SVGAnimationElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
49
50 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGAnimationElement)
51     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
52     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
53 END_REGISTER_ANIMATED_PROPERTIES
54
55 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document& document)
56     : SVGSMILElement(tagName, document)
57     , m_fromPropertyValueType(RegularPropertyValue)
58     , m_toPropertyValueType(RegularPropertyValue)
59     , m_animationValid(false)
60     , m_attributeType(AttributeTypeAuto)
61     , m_hasInvalidCSSAttributeType(false)
62     , m_calcMode(CalcModeLinear)
63     , m_animationMode(NoAnimation)
64 {
65     registerAnimatedPropertiesForSVGAnimationElement();
66 }
67
68 static void parseKeyTimes(const String& parse, Vector<float>& result, bool verifyOrder)
69 {
70     result.clear();
71     Vector<String> parseList;
72     parse.split(';', parseList);
73     for (unsigned n = 0; n < parseList.size(); ++n) {
74         String timeString = parseList[n];
75         bool ok;
76         float time = timeString.toFloat(&ok);
77         if (!ok || time < 0 || time > 1)
78             goto fail;
79         if (verifyOrder) {
80             if (!n) {
81                 if (time)
82                     goto fail;
83             } else if (time < result.last())
84                 goto fail;
85         }
86         result.append(time);
87     }
88     return;
89 fail:
90     result.clear();
91 }
92
93 static void parseKeySplines(const String& parse, Vector<UnitBezier>& result)
94 {
95     result.clear();
96     if (parse.isEmpty())
97         return;
98
99     auto upconvertedCharacters = StringView(parse).upconvertedCharacters();
100     const UChar* cur = upconvertedCharacters;
101     const UChar* end = cur + parse.length();
102
103     skipOptionalSVGSpaces(cur, end);
104
105     bool delimParsed = false;
106     while (cur < end) {
107         delimParsed = false;
108         float posA = 0;
109         if (!parseNumber(cur, end, posA)) {
110             result.clear();
111             return;
112         }
113
114         float posB = 0;
115         if (!parseNumber(cur, end, posB)) {
116             result.clear();
117             return;
118         }
119
120         float posC = 0;
121         if (!parseNumber(cur, end, posC)) {
122             result.clear();
123             return;
124         }
125
126         float posD = 0;
127         if (!parseNumber(cur, end, posD, false)) {
128             result.clear();
129             return;
130         }
131
132         skipOptionalSVGSpaces(cur, end);
133
134         if (cur < end && *cur == ';') {
135             delimParsed = true;
136             cur++;
137         }
138         skipOptionalSVGSpaces(cur, end);
139
140         result.append(UnitBezier(posA, posB, posC, posD));
141     }
142     if (!(cur == end && !delimParsed))
143         result.clear();
144 }
145
146 bool SVGAnimationElement::isSupportedAttribute(const QualifiedName& attrName)
147 {
148     static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes;
149     if (supportedAttributes.get().isEmpty()) {
150         SVGTests::addSupportedAttributes(supportedAttributes);
151         SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
152         supportedAttributes.get().add(SVGNames::valuesAttr);
153         supportedAttributes.get().add(SVGNames::keyTimesAttr);
154         supportedAttributes.get().add(SVGNames::keyPointsAttr);
155         supportedAttributes.get().add(SVGNames::keySplinesAttr);
156         supportedAttributes.get().add(SVGNames::attributeTypeAttr);
157         supportedAttributes.get().add(SVGNames::calcModeAttr);
158         supportedAttributes.get().add(SVGNames::fromAttr);
159         supportedAttributes.get().add(SVGNames::toAttr);
160         supportedAttributes.get().add(SVGNames::byAttr);
161     }
162     return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
163 }
164
165 void SVGAnimationElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
166 {
167     if (!isSupportedAttribute(name)) {
168         SVGSMILElement::parseAttribute(name, value);
169         return;
170     }
171
172     if (name == SVGNames::valuesAttr) {
173         // Per the SMIL specification, leading and trailing white space,
174         // and white space before and after semicolon separators, is allowed and will be ignored.
175         // http://www.w3.org/TR/SVG11/animate.html#ValuesAttribute
176         value.string().split(';', m_values);
177         for (unsigned i = 0; i < m_values.size(); ++i)
178             m_values[i] = m_values[i].stripWhiteSpace();
179
180         updateAnimationMode();
181         return;
182     }
183
184     if (name == SVGNames::keyTimesAttr) {
185         parseKeyTimes(value, m_keyTimes, true);
186         return;
187     }
188
189     if (name == SVGNames::keyPointsAttr) {
190         if (hasTagName(SVGNames::animateMotionTag)) {
191             // This is specified to be an animateMotion attribute only but it is simpler to put it here 
192             // where the other timing calculatations are.
193             parseKeyTimes(value, m_keyPoints, false);
194         }
195         return;
196     }
197
198     if (name == SVGNames::keySplinesAttr) {
199         parseKeySplines(value, m_keySplines);
200         return;
201     }
202
203     if (name == SVGNames::attributeTypeAttr) {
204         setAttributeType(value);
205         return;
206     }
207
208     if (name == SVGNames::calcModeAttr) {
209         setCalcMode(value);
210         return;
211     }
212
213     if (name == SVGNames::fromAttr || name == SVGNames::toAttr || name == SVGNames::byAttr) {
214         updateAnimationMode();
215         return;
216     }
217
218     if (SVGTests::parseAttribute(name, value))
219         return;
220     if (SVGExternalResourcesRequired::parseAttribute(name, value))
221         return;
222
223     ASSERT_NOT_REACHED();
224 }
225
226 void SVGAnimationElement::svgAttributeChanged(const QualifiedName& attrName)
227 {
228     if (!isSupportedAttribute(attrName)) {
229         SVGSMILElement::svgAttributeChanged(attrName);
230         return;
231     }
232
233     animationAttributeChanged();
234 }
235
236 void SVGAnimationElement::animationAttributeChanged()
237 {
238     // Assumptions may not hold after an attribute change.
239     m_animationValid = false;
240     setInactive();
241 }
242
243 float SVGAnimationElement::getStartTime() const
244 {
245     return narrowPrecisionToFloat(intervalBegin().value());
246 }
247
248 float SVGAnimationElement::getCurrentTime() const
249 {
250     return narrowPrecisionToFloat(elapsed().value());
251 }
252
253 float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const
254 {
255     return narrowPrecisionToFloat(simpleDuration().value());
256 }    
257     
258 void SVGAnimationElement::beginElement()
259 {
260     beginElementAt(0);
261 }
262
263 void SVGAnimationElement::beginElementAt(float offset)
264 {
265     if (std::isnan(offset))
266         return;
267     SMILTime elapsed = this->elapsed();
268     addBeginTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
269 }
270
271 void SVGAnimationElement::endElement()
272 {
273     endElementAt(0);
274 }
275
276 void SVGAnimationElement::endElementAt(float offset)
277 {
278     if (std::isnan(offset))
279         return;
280     SMILTime elapsed = this->elapsed();
281     addEndTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
282 }
283
284 void SVGAnimationElement::updateAnimationMode()
285 {
286     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
287     if (hasAttribute(SVGNames::valuesAttr))
288         setAnimationMode(ValuesAnimation);
289     else if (!toValue().isEmpty())
290         setAnimationMode(fromValue().isEmpty() ? ToAnimation : FromToAnimation);
291     else if (!byValue().isEmpty())
292         setAnimationMode(fromValue().isEmpty() ? ByAnimation : FromByAnimation);
293     else
294         setAnimationMode(NoAnimation);
295 }
296
297 void SVGAnimationElement::setCalcMode(const AtomicString& calcMode)
298 {
299     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete", AtomicString::ConstructFromLiteral));
300     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear", AtomicString::ConstructFromLiteral));
301     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced", AtomicString::ConstructFromLiteral));
302     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline", AtomicString::ConstructFromLiteral));
303     if (calcMode == discrete)
304         setCalcMode(CalcModeDiscrete);
305     else if (calcMode == linear)
306         setCalcMode(CalcModeLinear);
307     else if (calcMode == paced)
308         setCalcMode(CalcModePaced);
309     else if (calcMode == spline)
310         setCalcMode(CalcModeSpline);
311     else
312         setCalcMode(hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear);
313 }
314
315 void SVGAnimationElement::setAttributeType(const AtomicString& attributeType)
316 {
317     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS", AtomicString::ConstructFromLiteral));
318     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML", AtomicString::ConstructFromLiteral));
319     if (attributeType == css)
320         m_attributeType = AttributeTypeCSS;
321     else if (attributeType == xml)
322         m_attributeType = AttributeTypeXML;
323     else
324         m_attributeType = AttributeTypeAuto;
325     checkInvalidCSSAttributeType(targetElement());
326 }
327
328 String SVGAnimationElement::toValue() const
329 {    
330     return fastGetAttribute(SVGNames::toAttr);
331 }
332
333 String SVGAnimationElement::byValue() const
334 {    
335     return fastGetAttribute(SVGNames::byAttr);
336 }
337
338 String SVGAnimationElement::fromValue() const
339 {    
340     return fastGetAttribute(SVGNames::fromAttr);
341 }
342
343 bool SVGAnimationElement::isAdditive() const
344 {
345     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum", AtomicString::ConstructFromLiteral));
346     const AtomicString& value = fastGetAttribute(SVGNames::additiveAttr);
347     return value == sum || animationMode() == ByAnimation;
348 }
349
350 bool SVGAnimationElement::isAccumulated() const
351 {
352     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum", AtomicString::ConstructFromLiteral));
353     const AtomicString& value = fastGetAttribute(SVGNames::accumulateAttr);
354     return value == sum && animationMode() != ToAnimation;
355 }
356
357 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* element, const QualifiedName& attributeName)
358 {
359     if (element->isTextContent()
360         && (attributeName == SVGNames::xAttr || attributeName == SVGNames::yAttr))
361         return false;
362
363     return SVGElement::isAnimatableCSSProperty(attributeName);
364 }
365
366 SVGAnimationElement::ShouldApplyAnimation SVGAnimationElement::shouldApplyAnimation(SVGElement* targetElement, const QualifiedName& attributeName)
367 {
368     if (!hasValidAttributeType() || !targetElement || attributeName == anyQName())
369         return DontApplyAnimation;
370
371     // Always animate CSS properties, using the ApplyCSSAnimation code path, regardless of the attributeType value.
372     if (isTargetAttributeCSSProperty(targetElement, attributeName)) {
373         if (targetElement->isPresentationAttributeWithSVGDOM(attributeName))
374             return ApplyXMLandCSSAnimation;
375         return ApplyCSSAnimation;
376     }
377
378
379     // If attributeType="CSS" and attributeName doesn't point to a CSS property, ignore the animation.
380     if (attributeType() == AttributeTypeCSS)
381         return DontApplyAnimation;
382
383     return ApplyXMLAnimation;
384 }
385
386 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
387 {
388     ASSERT(calcMode() == CalcModePaced);
389     ASSERT(animationMode() == ValuesAnimation);
390
391     unsigned valuesCount = m_values.size();
392     ASSERT(valuesCount >= 1);
393     if (valuesCount == 1)
394         return;
395
396     // FIXME, webkit.org/b/109010: m_keyTimes should not be modified in this function.
397     m_keyTimes.clear();
398
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 = 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_WITH_SECURITY_IMPLICATION(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         AnimatedPropertyType attributeType = toSVGAnimateElement(this)->determineAnimatedPropertyType(targetElement());
512         // Fall back to discrete animations for Strings.
513         if (attributeType == AnimatedBoolean
514             || attributeType == AnimatedEnumeration
515             || attributeType == AnimatedPreserveAspectRatio
516             || attributeType == AnimatedString)
517             calcMode = CalcModeDiscrete;
518     }
519     if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
520         return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
521     
522     unsigned keyTimesCount = m_keyTimes.size();
523     ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
524     ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
525
526     unsigned index = calculateKeyTimesIndex(percent);
527     if (calcMode == CalcModeDiscrete) {
528         if (!keyTimesCount) 
529             index = static_cast<unsigned>(percent * valuesCount);
530         from = m_values[index];
531         to = m_values[index];
532         effectivePercent = 0;
533         return;
534     }
535     
536     float fromPercent;
537     float toPercent;
538     if (keyTimesCount) {
539         fromPercent = m_keyTimes[index];
540         toPercent = m_keyTimes[index + 1];
541     } else {        
542         index = static_cast<unsigned>(floorf(percent * (valuesCount - 1)));
543         fromPercent =  static_cast<float>(index) / (valuesCount - 1);
544         toPercent =  static_cast<float>(index + 1) / (valuesCount - 1);
545     }
546     
547     if (index == valuesCount - 1)
548         --index;
549     from = m_values[index];
550     to = m_values[index + 1];
551     ASSERT_WITH_SECURITY_IMPLICATION(toPercent > fromPercent);
552     effectivePercent = (percent - fromPercent) / (toPercent - fromPercent);
553
554     if (calcMode == CalcModeSpline) {
555         ASSERT(m_keySplines.size() == m_values.size() - 1);
556         effectivePercent = calculatePercentForSpline(effectivePercent, index);
557     }
558 }
559     
560 void SVGAnimationElement::startedActiveInterval()
561 {
562     m_animationValid = false;
563
564     if (!hasValidAttributeType())
565         return;
566
567     // These validations are appropriate for all animation modes.
568     if (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
569         return;
570
571     AnimationMode animationMode = this->animationMode();
572     CalcMode calcMode = this->calcMode();
573     if (calcMode == CalcModeSpline) {
574         unsigned splinesCount = m_keySplines.size();
575         if (!splinesCount
576             || (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() - 1 != splinesCount)
577             || (animationMode == ValuesAnimation && m_values.size() - 1 != splinesCount)
578             || (fastHasAttribute(SVGNames::keyTimesAttr) && m_keyTimes.size() - 1 != 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 || animationMode == FromByAnimation || animationMode == ToAnimation || animationMode == ByAnimation)
588         && (fastHasAttribute(SVGNames::keyPointsAttr) && fastHasAttribute(SVGNames::keyTimesAttr) && (m_keyTimes.size() < 2 || m_keyTimes.size() != m_keyPoints.size())))
589         return;
590     if (animationMode == FromToAnimation)
591         m_animationValid = calculateFromAndToValues(from, to);
592     else if (animationMode == ToAnimation) {
593         // For to-animations the from value is the current accumulated value from lower priority animations.
594         // The value is not static and is determined during the animation.
595         m_animationValid = calculateFromAndToValues(emptyString(), to);
596     } else if (animationMode == FromByAnimation)
597         m_animationValid = calculateFromAndByValues(from, by);
598     else if (animationMode == ByAnimation)
599         m_animationValid = calculateFromAndByValues(emptyString(), by);
600     else if (animationMode == ValuesAnimation) {
601         m_animationValid = m_values.size() >= 1
602             && (calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyTimesAttr) || fastHasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
603             && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
604             && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
605             && (!fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
606         if (m_animationValid)
607             m_animationValid = calculateToAtEndOfDurationValue(m_values.last());
608         if (calcMode == CalcModePaced && m_animationValid)
609             calculateKeyTimesForCalcModePaced();
610     } else if (animationMode == PathAnimation)
611         m_animationValid = calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
612 }
613
614 void SVGAnimationElement::updateAnimation(float percent, unsigned repeatCount, SVGSMILElement* resultElement)
615 {    
616     if (!m_animationValid)
617         return;
618
619     float effectivePercent;
620     CalcMode calcMode = this->calcMode();
621     AnimationMode animationMode = this->animationMode();
622     if (animationMode == ValuesAnimation) {
623         String from;
624         String to;
625         currentValuesForValuesAnimation(percent, effectivePercent, from, to);
626         if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
627             m_animationValid = calculateFromAndToValues(from, to);
628             if (!m_animationValid)
629                 return;
630             m_lastValuesAnimationFrom = from;
631             m_lastValuesAnimationTo = to;
632         }
633     } else if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
634         effectivePercent = calculatePercentFromKeyPoints(percent);
635     else if (m_keyPoints.isEmpty() && calcMode == CalcModeSpline && m_keyTimes.size() > 1)
636         effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
637     else if (animationMode == FromToAnimation || animationMode == ToAnimation)
638         effectivePercent = calculatePercentForFromTo(percent);
639     else
640         effectivePercent = percent;
641
642     calculateAnimatedValue(effectivePercent, repeatCount, resultElement);
643 }
644
645 void SVGAnimationElement::computeCSSPropertyValue(SVGElement* element, CSSPropertyID id, String& valueString)
646 {
647     ASSERT(element);
648
649     // Don't include any properties resulting from CSS Transitions/Animations or SMIL animations, as we want to retrieve the "base value".
650     element->setUseOverrideComputedStyle(true);
651     RefPtr<CSSValue> value = ComputedStyleExtractor(element).propertyValue(id);
652     valueString = value ? value->cssText() : String();
653     element->setUseOverrideComputedStyle(false);
654 }
655
656 void SVGAnimationElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value)
657 {
658     // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
659     // In the future we might want to work with the value type directly to avoid the String parsing.
660     ASSERT(targetElement);
661
662     Element* parent = targetElement->parentElement();
663     if (!parent || !parent->isSVGElement())
664         return;
665
666     SVGElement* svgParent = toSVGElement(parent);
667     computeCSSPropertyValue(svgParent, cssPropertyID(attributeName.localName()), value);
668 }
669
670 static bool inheritsFromProperty(SVGElement*, const QualifiedName& attributeName, const String& value)
671 {
672     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit", AtomicString::ConstructFromLiteral));
673     
674     if (value.isEmpty() || value != inherit)
675         return false;
676     return SVGElement::isAnimatableCSSProperty(attributeName);
677 }
678
679 void SVGAnimationElement::determinePropertyValueTypes(const String& from, const String& to)
680 {
681     SVGElement* targetElement = this->targetElement();
682     ASSERT(targetElement);
683
684     const QualifiedName& attributeName = this->attributeName();
685     if (inheritsFromProperty(targetElement, attributeName, from))
686         m_fromPropertyValueType = InheritValue;
687     if (inheritsFromProperty(targetElement, attributeName, to))
688         m_toPropertyValueType = InheritValue;
689 }
690
691 void SVGAnimationElement::setTargetElement(SVGElement* target)
692 {
693     SVGSMILElement::setTargetElement(target);
694     checkInvalidCSSAttributeType(target);
695 }
696
697 void SVGAnimationElement::setAttributeName(const QualifiedName& attributeName)
698 {
699     SVGSMILElement::setAttributeName(attributeName);
700     checkInvalidCSSAttributeType(targetElement());
701 }
702
703 void SVGAnimationElement::checkInvalidCSSAttributeType(SVGElement* target)
704 {
705     m_hasInvalidCSSAttributeType = target && hasValidAttributeName() && attributeType() == AttributeTypeCSS && !isTargetAttributeCSSProperty(target, attributeName());
706 }
707
708 }