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