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