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/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 
     40 namespace blink {
     41 
     42 SVGAnimationElement::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 
     55 static 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;
     76 fail:
     77     result.clear();
     78 }
     79 
     80 template<typename CharType>
     81 static 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 
    117 static 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 
    134 bool 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 
    151 void 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 
    208 void SVGAnimationElement::svgAttributeChanged(const QualifiedName& attrName)
    209 {
    210     if (!isSupportedAttribute(attrName)) {
    211         SVGSMILElement::svgAttributeChanged(attrName);
    212         return;
    213     }
    214 
    215     animationAttributeChanged();
    216 }
    217 
    218 void 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 
    227 float SVGAnimationElement::getStartTime() const
    228 {
    229     return narrowPrecisionToFloat(intervalBegin().value());
    230 }
    231 
    232 float SVGAnimationElement::getCurrentTime() const
    233 {
    234     return narrowPrecisionToFloat(elapsed().value());
    235 }
    236 
    237 float SVGAnimationElement::getSimpleDuration() const
    238 {
    239     return narrowPrecisionToFloat(simpleDuration().value());
    240 }
    241 
    242 void SVGAnimationElement::beginElement()
    243 {
    244     beginElementAt(0);
    245 }
    246 
    247 void 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 
    255 void SVGAnimationElement::endElement()
    256 {
    257     endElementAt(0);
    258 }
    259 
    260 void 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 
    268 void 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 
    281 void 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 
    299 void 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 
    312 String SVGAnimationElement::toValue() const
    313 {
    314     return fastGetAttribute(SVGNames::toAttr);
    315 }
    316 
    317 String SVGAnimationElement::byValue() const
    318 {
    319     return fastGetAttribute(SVGNames::byAttr);
    320 }
    321 
    322 String SVGAnimationElement::fromValue() const
    323 {
    324     return fastGetAttribute(SVGNames::fromAttr);
    325 }
    326 
    327 bool 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 
    334 bool 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 
    341 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName)
    342 {
    343     ASSERT(targetElement);
    344 
    345     return SVGElement::isAnimatableCSSProperty(attributeName);
    346 }
    347 
    348 SVGAnimationElement::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 
    364 void 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 
    400 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
    401 
    402 unsigned 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 
    418 float 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 
    429 float 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 
    458 float 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 
    466 void 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 
    477 void 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 
    540 void 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 
    597 void 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 
    628 void 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 
    638 void 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 
    652 static 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 
    662 void 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 
    674 void SVGAnimationElement::setTargetElement(SVGElement* target)
    675 {
    676     SVGSMILElement::setTargetElement(target);
    677     checkInvalidCSSAttributeType(target);
    678 }
    679 
    680 void SVGAnimationElement::setAttributeName(const QualifiedName& attributeName)
    681 {
    682     SVGSMILElement::setAttributeName(attributeName);
    683     checkInvalidCSSAttributeType(targetElement());
    684 }
    685 
    686 void SVGAnimationElement::checkInvalidCSSAttributeType(SVGElement* target)
    687 {
    688     m_hasInvalidCSSAttributeType = target && hasValidAttributeName() && attributeType() == AttributeTypeCSS && !isTargetAttributeCSSProperty(target, attributeName());
    689 }
    690 
    691 }
    692