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