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