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