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