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