SVG Animations update baseVal instead of animVal
[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  *
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 #if ENABLE(SVG)
28 #include "SVGAnimationElement.h"
29
30 #include "Attribute.h"
31 #include "CSSComputedStyleDeclaration.h"
32 #include "CSSParser.h"
33 #include "CSSPropertyNames.h"
34 #include "Color.h"
35 #include "Document.h"
36 #include "Event.h"
37 #include "EventListener.h"
38 #include "FloatConversion.h"
39 #include "HTMLNames.h"
40 #include "PlatformString.h"
41 #include "SVGAnimateElement.h"
42 #include "SVGElementInstance.h"
43 #include "SVGNames.h"
44 #include "SVGParserUtilities.h"
45 #include "SVGStyledElement.h"
46 #include "SVGURIReference.h"
47 #include "SVGUseElement.h"
48 #include "XLinkNames.h"
49 #include <wtf/StdLibExtras.h>
50
51 using namespace std;
52
53 namespace WebCore {
54
55 // Animated property definitions
56 DEFINE_ANIMATED_BOOLEAN(SVGAnimationElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
57
58 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGAnimationElement)
59     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
60     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
61 END_REGISTER_ANIMATED_PROPERTIES
62
63 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document* document)
64     : SVGSMILElement(tagName, document)
65     , m_animationValid(false)
66 {
67     registerAnimatedPropertiesForSVGAnimationElement();
68 }
69
70 static void parseKeyTimes(const String& parse, Vector<float>& result, bool verifyOrder)
71 {
72     result.clear();
73     Vector<String> parseList;
74     parse.split(';', parseList);
75     for (unsigned n = 0; n < parseList.size(); ++n) {
76         String timeString = parseList[n];
77         bool ok;
78         float time = timeString.toFloat(&ok);
79         if (!ok || time < 0 || time > 1)
80             goto fail;
81         if (verifyOrder) {
82             if (!n) {
83                 if (time)
84                     goto fail;
85             } else if (time < result.last())
86                 goto fail;
87         }
88         result.append(time);
89     }
90     return;
91 fail:
92     result.clear();
93 }
94
95 static void parseKeySplines(const String& parse, Vector<UnitBezier>& result)
96 {
97     result.clear();
98     if (parse.isEmpty())
99         return;
100     const UChar* cur = parse.characters();
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     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
149     if (supportedAttributes.isEmpty()) {
150         SVGTests::addSupportedAttributes(supportedAttributes);
151         SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
152         supportedAttributes.add(SVGNames::valuesAttr);
153         supportedAttributes.add(SVGNames::keyTimesAttr);
154         supportedAttributes.add(SVGNames::keyPointsAttr);
155         supportedAttributes.add(SVGNames::keySplinesAttr);
156     }
157     return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
158 }
159
160 void SVGAnimationElement::parseAttribute(Attribute* attr)
161 {
162     if (!isSupportedAttribute(attr->name())) {
163         SVGSMILElement::parseAttribute(attr);
164         return;
165     }
166
167     if (attr->name() == SVGNames::valuesAttr) {
168         // Per the SMIL specification, leading and trailing white space,
169         // and white space before and after semicolon separators, is allowed and will be ignored.
170         // http://www.w3.org/TR/SVG11/animate.html#ValuesAttribute
171         attr->value().string().split(';', m_values);
172         for (unsigned i = 0; i < m_values.size(); ++i)
173             m_values[i] = m_values[i].stripWhiteSpace();
174         return;
175     }
176
177     if (attr->name() == SVGNames::keyTimesAttr) {
178         parseKeyTimes(attr->value(), m_keyTimes, true);
179         return;
180     }
181
182     if (attr->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(attr->value(), m_keyPoints, false);
187         }
188         return;
189     }
190
191     if (attr->name() == SVGNames::keySplinesAttr) {
192         parseKeySplines(attr->value(), m_keySplines);
193         return;
194     }
195
196     if (SVGTests::parseAttribute(attr))
197         return;
198     if (SVGExternalResourcesRequired::parseAttribute(attr))
199         return;
200
201     ASSERT_NOT_REACHED();
202 }
203
204 void SVGAnimationElement::svgAttributeChanged(const QualifiedName& attrName)
205 {
206     if (!isSupportedAttribute(attrName)) {
207         SVGSMILElement::svgAttributeChanged(attrName);
208         return;
209     }
210
211     animationAttributeChanged();
212 }
213
214 void SVGAnimationElement::animationAttributeChanged()
215 {
216     // Assumptions may not hold after an attribute change.
217     m_animationValid = false;
218     setInactive();
219 }
220
221 float SVGAnimationElement::getStartTime() const
222 {
223     return narrowPrecisionToFloat(intervalBegin().value());
224 }
225
226 float SVGAnimationElement::getCurrentTime() const
227 {
228     return narrowPrecisionToFloat(elapsed().value());
229 }
230
231 float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const
232 {
233     return narrowPrecisionToFloat(simpleDuration().value());
234 }    
235     
236 void SVGAnimationElement::beginElement()
237 {
238     beginElementAt(0);
239 }
240
241 void SVGAnimationElement::beginElementAt(float offset)
242 {
243     SMILTime elapsed = this->elapsed();
244     addBeginTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
245 }
246
247 void SVGAnimationElement::endElement()
248 {
249     endElementAt(0);
250 }
251
252 void SVGAnimationElement::endElementAt(float offset)
253 {
254     SMILTime elapsed = this->elapsed();
255     addEndTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
256 }
257
258 AnimationMode SVGAnimationElement::animationMode() const
259 {
260     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
261     if (hasTagName(SVGNames::setTag))
262         return ToAnimation;
263     if (!animationPath().isEmpty())
264         return PathAnimation;
265     if (hasAttribute(SVGNames::valuesAttr))
266         return ValuesAnimation;
267     if (!toValue().isEmpty())
268         return fromValue().isEmpty() ? ToAnimation : FromToAnimation;
269     if (!byValue().isEmpty())
270         return fromValue().isEmpty() ? ByAnimation : FromByAnimation;
271     return NoAnimation;
272 }
273
274 CalcMode SVGAnimationElement::calcMode() const
275 {    
276     DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete"));
277     DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear"));
278     DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced"));
279     DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline"));
280     const AtomicString& value = fastGetAttribute(SVGNames::calcModeAttr);
281     if (value == discrete)
282         return CalcModeDiscrete;
283     if (value == linear)
284         return CalcModeLinear;
285     if (value == paced)
286         return CalcModePaced;
287     if (value == spline)
288         return CalcModeSpline;
289     return hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear;
290 }
291
292 SVGAnimationElement::AttributeType SVGAnimationElement::attributeType() const
293 {    
294     DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS"));
295     DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML"));
296     const AtomicString& value = fastGetAttribute(SVGNames::attributeTypeAttr);
297     if (value == css)
298         return AttributeTypeCSS;
299     if (value == xml)
300         return AttributeTypeXML;
301     return AttributeTypeAuto;
302 }
303
304 String SVGAnimationElement::toValue() const
305 {    
306     return fastGetAttribute(SVGNames::toAttr);
307 }
308
309 String SVGAnimationElement::byValue() const
310 {    
311     return fastGetAttribute(SVGNames::byAttr);
312 }
313
314 String SVGAnimationElement::fromValue() const
315 {    
316     return fastGetAttribute(SVGNames::fromAttr);
317 }
318
319 bool SVGAnimationElement::isAdditive() const
320 {
321     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
322     const AtomicString& value = fastGetAttribute(SVGNames::additiveAttr);
323     return value == sum || animationMode() == ByAnimation;
324 }
325
326 bool SVGAnimationElement::isAccumulated() const
327 {
328     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
329     const AtomicString& value = fastGetAttribute(SVGNames::accumulateAttr);
330     return value == sum && animationMode() != ToAnimation;
331 }
332
333 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName)
334 {
335     ASSERT(targetElement);
336     if (!targetElement->isStyled())
337         return false;
338
339     return SVGStyledElement::isAnimatableCSSProperty(attributeName);
340 }
341
342 static inline void setTargetAttributeAnimatedCSSValue(SVGElement* targetElement, const QualifiedName& attributeName, const String& value)
343 {
344     StylePropertySet* propertySet = targetElement->ensureAnimatedSMILStyleProperties();
345     if (propertySet->setProperty(cssPropertyID(attributeName.localName()), value, false, 0))
346         targetElement->setNeedsStyleRecalc();
347 }
348
349 static inline SVGAnimatedProperty* findMatchingAnimatedProperty(SVGElement* targetElement, const QualifiedName& attributeName, AnimatedPropertyType type)
350 {
351     Vector<RefPtr<SVGAnimatedProperty> > properties;
352     targetElement->localAttributeToPropertyMap().animatedPropertiesForAttribute(targetElement, attributeName, properties);
353
354     size_t propertiesSize = properties.size();
355     for (size_t i = 0; i < propertiesSize; ++i) {
356         SVGAnimatedProperty* property = properties[i].get();
357         if (property->animatedPropertyType() != type)
358             continue;
359
360         // FIXME: This check can go away once all types support animVal.
361         if (SVGAnimatedType::supportsAnimVal(property->animatedPropertyType()))
362             return property;
363         return 0;
364     }
365
366     return 0;
367 }
368
369 void SVGAnimationElement::applyAnimatedValue(SVGAnimationElement::ShouldApplyAnimation shouldApply, SVGElement* targetElement, const QualifiedName& attributeName, SVGAnimatedType* animatedType)
370 {
371     ASSERT(animatedType);
372
373     switch (shouldApply) {
374     case DontApplyAnimation:
375         ASSERT_NOT_REACHED();
376         break;
377     case ApplyCSSAnimation:
378         setTargetAttributeAnimatedCSSValue(targetElement, attributeName, animatedType->valueAsString());
379         break;
380     case ApplyXMLAnimation:
381         if (SVGAnimatedProperty* property = findMatchingAnimatedProperty(targetElement, attributeName, animatedType->type())) {
382             property->animationValueChanged();
383             break;
384         }
385
386         // FIXME: Deprecated legacy code path which mutates the DOM.
387         targetElement->setAttribute(attributeName, animatedType->valueAsString());
388         break;
389     }
390 }
391
392 struct InstanceUpdateBlocker {
393     InstanceUpdateBlocker(SVGElement* targetElement)
394         : m_targetElement(targetElement->isStyled() ? static_cast<SVGStyledElement*>(targetElement) : 0)
395     {
396         if (m_targetElement)
397             m_targetElement->setInstanceUpdatesBlocked(true);
398     }
399
400     ~InstanceUpdateBlocker()
401     {
402         if (m_targetElement)
403             m_targetElement->setInstanceUpdatesBlocked(false);
404     }
405
406     SVGStyledElement* m_targetElement;
407 };
408
409 void SVGAnimationElement::setTargetAttributeAnimatedValue(SVGAnimatedType* animatedType)
410 {
411     ASSERT(animatedType);
412
413     SVGElement* targetElement = this->targetElement();
414     const QualifiedName& attributeName = this->attributeName();
415     ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName);
416     if (shouldApply == DontApplyAnimation)
417         return;
418
419     // Scope this block, so instances updates will be unblocked, before we try to update the instances.
420     {
421         InstanceUpdateBlocker blocker(targetElement);
422         applyAnimatedValue(shouldApply, targetElement, attributeName, animatedType);
423     }
424
425     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
426     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
427     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
428     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
429         if (SVGElement* shadowTreeElement = (*it)->shadowTreeElement())
430             applyAnimatedValue(shouldApply, shadowTreeElement, attributeName, animatedType);
431     }
432 }
433
434 static inline void notifyAnimatedPropertyAboutAnimationBeginEnd(SVGAnimatedProperty* property, SVGAnimatedType* type, SVGElement* targetElement, const QualifiedName& attributeName)
435 {
436     ASSERT(property);
437     ASSERT(targetElement);
438     ASSERT(property->contextElement() == targetElement);
439     ASSERT(property->attributeName() == attributeName);
440     if (type)
441         property->animationStarted(type);
442     else {
443         // animationEnded() notifies the targetElement about the attribute change - take care of blocking
444         // updates to instances of elements, otherwise they use trees got recloned, unncessary.
445         InstanceUpdateBlocker blocker(targetElement);
446         property->animationEnded();
447     }
448
449     // If the target element has instances, update them as well, w/o requiring the <use> tree to be rebuilt.
450     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
451     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
452     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
453         SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
454         if (!shadowTreeElement)
455             continue;
456
457         SVGAnimatedProperty* propertyInstance = findMatchingAnimatedProperty(shadowTreeElement, attributeName, property->animatedPropertyType());
458         if (!propertyInstance)
459             continue;
460
461         if (type)
462             propertyInstance->animationStarted(type);
463         else
464             propertyInstance->animationEnded();
465     }
466 }
467
468 void SVGAnimationElement::animationStarted(SVGAnimatedProperty* property, SVGAnimatedType* type)
469 {
470     ASSERT(type);
471     notifyAnimatedPropertyAboutAnimationBeginEnd(property, type, targetElement(), attributeName());
472 }
473
474 void SVGAnimationElement::animationEnded(SVGAnimatedProperty* property)
475 {
476     notifyAnimatedPropertyAboutAnimationBeginEnd(property, 0, targetElement(), attributeName());
477 }
478
479 SVGAnimatedProperty* SVGAnimationElement::animatedPropertyForType(AnimatedPropertyType type)
480 {
481     SVGElement* targetElement = this->targetElement();
482     const QualifiedName& attributeName = this->attributeName();
483     ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName);
484     if (shouldApply != ApplyXMLAnimation)
485         return 0;
486     return findMatchingAnimatedProperty(targetElement, attributeName, type);
487 }
488
489 SVGAnimationElement::ShouldApplyAnimation SVGAnimationElement::shouldApplyAnimation(SVGElement* targetElement, const QualifiedName& attributeName)
490 {
491     if (!hasValidAttributeType() || !targetElement || attributeName == anyQName())
492         return DontApplyAnimation;
493
494     // Always animate CSS properties, using the ApplyCSSAnimation code path, regardless of the attributeType value.
495     if (isTargetAttributeCSSProperty(targetElement, attributeName))
496         return ApplyCSSAnimation;
497
498     // If attributeType="CSS" and attributeName doesn't point to a CSS property, ignore the animation.
499     if (attributeType() == AttributeTypeCSS)
500         return DontApplyAnimation;
501
502     return ApplyXMLAnimation;
503 }
504
505 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
506 {
507     ASSERT(calcMode() == CalcModePaced);
508     ASSERT(animationMode() == ValuesAnimation);
509
510     unsigned valuesCount = m_values.size();
511     ASSERT(valuesCount > 1);
512     Vector<float> keyTimesForPaced;
513     float totalDistance = 0;
514     keyTimesForPaced.append(0);
515     for (unsigned n = 0; n < valuesCount - 1; ++n) {
516         // Distance in any units
517         float distance = calculateDistance(m_values[n], m_values[n + 1]);
518         if (distance < 0)
519             return;
520         totalDistance += distance;
521         keyTimesForPaced.append(distance);
522     }
523     if (!totalDistance)
524         return;
525
526     // Normalize.
527     for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
528         keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
529     keyTimesForPaced[keyTimesForPaced.size() - 1] = 1;
530
531     // Use key times calculated based on pacing instead of the user provided ones.
532     m_keyTimes.swap(keyTimesForPaced);
533 }
534
535 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
536
537 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
538 {
539     unsigned index;
540     unsigned keyTimesCount = m_keyTimes.size();
541     // Compare index + 1 to keyTimesCount because the last keyTimes entry is
542     // required to be 1, and percent can never exceed 1; i.e., the second last
543     // keyTimes entry defines the beginning of the final interval
544     for (index = 1; index + 1 < keyTimesCount; ++index) {
545         if (m_keyTimes[index] > percent)
546             break;
547     }
548     return --index;
549 }
550
551 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
552 {
553     ASSERT(calcMode() == CalcModeSpline);
554     ASSERT(splineIndex < m_keySplines.size());
555     UnitBezier bezier = m_keySplines[splineIndex];
556     SMILTime duration = simpleDuration();
557     if (!duration.isFinite())
558         duration = 100.0;
559     return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
560 }
561
562 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
563 {
564     ASSERT(!m_keyPoints.isEmpty());
565     ASSERT(calcMode() != CalcModePaced);
566     ASSERT(m_keyTimes.size() > 1);
567     ASSERT(m_keyPoints.size() == m_keyTimes.size());
568
569     if (percent == 1)
570         return m_keyPoints[m_keyPoints.size() - 1];
571
572     unsigned index = calculateKeyTimesIndex(percent);
573     float fromPercent = m_keyTimes[index];
574     float toPercent = m_keyTimes[index + 1];
575     float fromKeyPoint = m_keyPoints[index];
576     float toKeyPoint = m_keyPoints[index + 1];
577     
578     if (calcMode() == CalcModeDiscrete)
579         return fromKeyPoint;
580     
581     float keyPointPercent = (percent - fromPercent) / (toPercent - fromPercent);
582     
583     if (calcMode() == CalcModeSpline) {
584         ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
585         keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
586     }
587     return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
588 }
589
590 float SVGAnimationElement::calculatePercentForFromTo(float percent) const
591 {
592     if (calcMode() == CalcModeDiscrete && m_keyTimes.size() == 2)
593         return percent > m_keyTimes[1] ? 1 : 0;
594
595     return percent;
596 }
597     
598 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
599 {
600     ASSERT(!m_keyPoints.isEmpty());
601     ASSERT(m_keyPoints.size() == m_keyTimes.size());
602     ASSERT(calcMode() != CalcModePaced);
603     effectivePercent = calculatePercentFromKeyPoints(percent);
604     unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
605     from = m_values[index];
606     to = m_values[index + 1];
607 }
608     
609 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to)
610 {
611     unsigned valuesCount = m_values.size();
612     ASSERT(m_animationValid);
613     ASSERT(valuesCount > 1);
614     
615     if (percent == 1) {
616         from = m_values[valuesCount - 1];
617         to = m_values[valuesCount - 1];
618         effectivePercent = 1;
619         return;
620     }
621
622     CalcMode calcMode = this->calcMode();
623     if (hasTagName(SVGNames::animateTag) || hasTagName(SVGNames::animateColorTag)) {
624         SVGAnimateElement* animateElement = static_cast<SVGAnimateElement*>(this);
625         AnimatedPropertyType attributeType = animateElement->determineAnimatedPropertyType(targetElement());
626         // Fall back to discrete animations for Strings.
627         if (attributeType == AnimatedBoolean
628             || attributeType == AnimatedEnumeration
629             || attributeType == AnimatedPreserveAspectRatio
630             || attributeType == AnimatedString)
631             calcMode = CalcModeDiscrete;
632     }
633     if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
634         return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
635     
636     unsigned keyTimesCount = m_keyTimes.size();
637     ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
638     ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
639
640     unsigned index = calculateKeyTimesIndex(percent);
641     if (calcMode == CalcModeDiscrete) {
642         if (!keyTimesCount) 
643             index = static_cast<unsigned>(percent * valuesCount);
644         from = m_values[index];
645         to = m_values[index];
646         effectivePercent = 0;
647         return;
648     }
649     
650     float fromPercent;
651     float toPercent;
652     if (keyTimesCount) {
653         fromPercent = m_keyTimes[index];
654         toPercent = m_keyTimes[index + 1];
655     } else {        
656         index = static_cast<unsigned>(percent * (valuesCount - 1));
657         fromPercent =  static_cast<float>(index) / (valuesCount - 1);
658         toPercent =  static_cast<float>(index + 1) / (valuesCount - 1);
659     }
660     
661     if (index == valuesCount - 1)
662         --index;
663     from = m_values[index];
664     to = m_values[index + 1];
665     ASSERT(toPercent > fromPercent);
666     effectivePercent = (percent - fromPercent) / (toPercent - fromPercent);
667     
668     if (calcMode == CalcModeSpline) {
669         ASSERT(m_keySplines.size() == m_values.size() - 1);
670         effectivePercent = calculatePercentForSpline(effectivePercent, index);
671     }
672 }
673     
674 void SVGAnimationElement::startedActiveInterval()
675 {
676     m_animationValid = false;
677
678     if (!hasValidAttributeType())
679         return;
680
681     // These validations are appropriate for all animation modes.
682     if (fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
683         return;
684
685     AnimationMode animationMode = this->animationMode();
686     CalcMode calcMode = this->calcMode();
687     if (calcMode == CalcModeSpline) {
688         unsigned splinesCount = m_keySplines.size() + 1;
689         if ((fastHasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != splinesCount)
690             || (animationMode == ValuesAnimation && m_values.size() != splinesCount)
691             || (fastHasAttribute(SVGNames::keyTimesAttr) && m_keyTimes.size() != splinesCount))
692             return;
693     }
694
695     String from = fromValue();
696     String to = toValue();
697     String by = byValue();
698     if (animationMode == NoAnimation)
699         return;
700     if (animationMode == FromToAnimation)
701         m_animationValid = calculateFromAndToValues(from, to);
702     else if (animationMode == ToAnimation) {
703         // For to-animations the from value is the current accumulated value from lower priority animations.
704         // The value is not static and is determined during the animation.
705         m_animationValid = calculateFromAndToValues(String(), to);
706     } else if (animationMode == FromByAnimation)
707         m_animationValid = calculateFromAndByValues(from, by);
708     else if (animationMode == ByAnimation)
709         m_animationValid = calculateFromAndByValues(String(), by);
710     else if (animationMode == ValuesAnimation) {
711         m_animationValid = m_values.size() > 1
712             && (calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyTimesAttr) || fastHasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
713             && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
714             && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
715             && (!fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
716         if (calcMode == CalcModePaced && m_animationValid)
717             calculateKeyTimesForCalcModePaced();
718     } else if (animationMode == PathAnimation)
719         m_animationValid = calcMode == CalcModePaced || !fastHasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
720 }
721
722 void SVGAnimationElement::updateAnimation(float percent, unsigned repeat, SVGSMILElement* resultElement)
723 {    
724     if (!m_animationValid)
725         return;
726     
727     float effectivePercent;
728     CalcMode mode = calcMode();
729     if (animationMode() == ValuesAnimation) {
730         String from;
731         String to;
732         currentValuesForValuesAnimation(percent, effectivePercent, from, to);
733         if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
734             m_animationValid = calculateFromAndToValues(from, to);
735             if (!m_animationValid)
736                 return;
737             m_lastValuesAnimationFrom = from;
738             m_lastValuesAnimationTo = to;
739         }
740     } else if (!m_keyPoints.isEmpty() && mode != CalcModePaced)
741         effectivePercent = calculatePercentFromKeyPoints(percent);
742     else if (m_keyPoints.isEmpty() && mode == CalcModeSpline && m_keyTimes.size() > 1)
743         effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
744     else if (animationMode() == FromToAnimation || animationMode() == ToAnimation)
745         effectivePercent = calculatePercentForFromTo(percent);
746     else
747         effectivePercent = percent;
748
749     calculateAnimatedValue(effectivePercent, repeat, resultElement);
750 }
751
752 }
753
754 #endif // ENABLE(SVG)