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