Remove the SVG elements' attributes macros
[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/IsoMallocInlines.h>
42 #include <wtf/MathExtras.h>
43 #include <wtf/NeverDestroyed.h>
44 #include <wtf/text/StringView.h>
45
46 namespace WebCore {
47
48 WTF_MAKE_ISO_ALLOCATED_IMPL(SVGAnimationElement);
49
50 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document& document)
51     : SVGSMILElement(tagName, document)
52     , SVGExternalResourcesRequired(this)
53     , SVGTests(this)
54 {
55 }
56
57 static void parseKeyTimes(const String& parse, Vector<float>& result, bool verifyOrder)
58 {
59     result.clear();
60     bool isFirst = true;
61     for (StringView timeString : StringView(parse).split(';')) {
62         bool ok;
63         float time = timeString.toFloat(ok);
64         if (!ok || time < 0 || time > 1)
65             goto fail;
66         if (verifyOrder) {
67             if (isFirst) {
68                 if (time)
69                     goto fail;
70                 isFirst = false;
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 static void parseKeySplines(const String& parse, Vector<UnitBezier>& result)
82 {
83     result.clear();
84     if (parse.isEmpty())
85         return;
86
87     auto upconvertedCharacters = StringView(parse).upconvertedCharacters();
88     const UChar* cur = upconvertedCharacters;
89     const UChar* end = cur + parse.length();
90
91     skipOptionalSVGSpaces(cur, end);
92
93     bool delimParsed = false;
94     while (cur < end) {
95         delimParsed = false;
96         float posA = 0;
97         if (!parseNumber(cur, end, posA)) {
98             result.clear();
99             return;
100         }
101
102         float posB = 0;
103         if (!parseNumber(cur, end, posB)) {
104             result.clear();
105             return;
106         }
107
108         float posC = 0;
109         if (!parseNumber(cur, end, posC)) {
110             result.clear();
111             return;
112         }
113
114         float posD = 0;
115         if (!parseNumber(cur, end, posD, false)) {
116             result.clear();
117             return;
118         }
119
120         skipOptionalSVGSpaces(cur, end);
121
122         if (cur < end && *cur == ';') {
123             delimParsed = true;
124             cur++;
125         }
126         skipOptionalSVGSpaces(cur, end);
127
128         result.append(UnitBezier(posA, posB, posC, posD));
129     }
130     if (!(cur == end && !delimParsed))
131         result.clear();
132 }
133
134 bool SVGAnimationElement::isSupportedAttribute(const QualifiedName& attrName)
135 {
136     static const auto supportedAttributes = makeNeverDestroyed([] {
137         HashSet<QualifiedName> set;
138         SVGTests::addSupportedAttributes(set);
139         SVGExternalResourcesRequired::addSupportedAttributes(set);
140         set.add({
141             SVGNames::valuesAttr.get(),
142             SVGNames::keyTimesAttr.get(),
143             SVGNames::keyPointsAttr.get(),
144             SVGNames::keySplinesAttr.get(),
145             SVGNames::attributeTypeAttr.get(),
146             SVGNames::calcModeAttr.get(),
147             SVGNames::fromAttr.get(),
148             SVGNames::toAttr.get(),
149             SVGNames::byAttr.get(),
150         });
151         return set;
152     }());
153     return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
154 }
155
156 void SVGAnimationElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
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         m_values = value.string().split(';');
163         for (auto& value : m_values)
164             value = value.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 (hasTagName(SVGNames::animateMotionTag)) {
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         parseKeySplines(value, m_keySplines);
186         return;
187     }
188
189     if (name == SVGNames::attributeTypeAttr) {
190         setAttributeType(value);
191         return;
192     }
193
194     if (name == SVGNames::calcModeAttr) {
195         setCalcMode(value);
196         return;
197     }
198
199     if (name == SVGNames::fromAttr || name == SVGNames::toAttr || name == SVGNames::byAttr) {
200         updateAnimationMode();
201         return;
202     }
203
204     SVGSMILElement::parseAttribute(name, value);
205     SVGTests::parseAttribute(name, value);
206     SVGExternalResourcesRequired::parseAttribute(name, value);
207 }
208
209 void SVGAnimationElement::svgAttributeChanged(const QualifiedName& attrName)
210 {
211     if (!isSupportedAttribute(attrName)) {
212         SVGSMILElement::svgAttributeChanged(attrName);
213         return;
214     }
215
216     animationAttributeChanged();
217 }
218
219 void SVGAnimationElement::animationAttributeChanged()
220 {
221     // Assumptions may not hold after an attribute change.
222     m_animationValid = false;
223     setInactive();
224 }
225
226 float SVGAnimationElement::getStartTime() const
227 {
228     return narrowPrecisionToFloat(intervalBegin().value());
229 }
230
231 float SVGAnimationElement::getCurrentTime() const
232 {
233     return narrowPrecisionToFloat(elapsed().value());
234 }
235
236 float SVGAnimationElement::getSimpleDuration() const
237 {
238     return narrowPrecisionToFloat(simpleDuration().value());
239 }    
240     
241 void SVGAnimationElement::beginElement()
242 {
243     beginElementAt(0);
244 }
245
246 void SVGAnimationElement::beginElementAt(float offset)
247 {
248     if (std::isnan(offset))
249         return;
250     SMILTime elapsed = this->elapsed();
251     addBeginTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
252 }
253
254 void SVGAnimationElement::endElement()
255 {
256     endElementAt(0);
257 }
258
259 void SVGAnimationElement::endElementAt(float offset)
260 {
261     if (std::isnan(offset))
262         return;
263     SMILTime elapsed = this->elapsed();
264     addEndTime(elapsed, elapsed + offset, SMILTimeWithOrigin::ScriptOrigin);
265 }
266
267 void SVGAnimationElement::updateAnimationMode()
268 {
269     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
270     if (hasAttribute(SVGNames::valuesAttr))
271         setAnimationMode(ValuesAnimation);
272     else if (!toValue().isEmpty())
273         setAnimationMode(fromValue().isEmpty() ? ToAnimation : FromToAnimation);
274     else if (!byValue().isEmpty())
275         setAnimationMode(fromValue().isEmpty() ? ByAnimation : FromByAnimation);
276     else
277         setAnimationMode(NoAnimation);
278 }
279
280 void SVGAnimationElement::setCalcMode(const AtomicString& calcMode)
281 {
282     static NeverDestroyed<const AtomicString> discrete("discrete", AtomicString::ConstructFromLiteral);
283     static NeverDestroyed<const AtomicString> linear("linear", AtomicString::ConstructFromLiteral);
284     static NeverDestroyed<const AtomicString> paced("paced", AtomicString::ConstructFromLiteral);
285     static NeverDestroyed<const AtomicString> spline("spline", AtomicString::ConstructFromLiteral);
286     if (calcMode == discrete)
287         setCalcMode(CalcMode::Discrete);
288     else if (calcMode == linear)
289         setCalcMode(CalcMode::Linear);
290     else if (calcMode == paced)
291         setCalcMode(CalcMode::Paced);
292     else if (calcMode == spline)
293         setCalcMode(CalcMode::Spline);
294     else
295         setCalcMode(hasTagName(SVGNames::animateMotionTag) ? CalcMode::Paced : CalcMode::Linear);
296 }
297
298 void SVGAnimationElement::setAttributeType(const AtomicString& attributeType)
299 {
300     static NeverDestroyed<const AtomicString> css("CSS", AtomicString::ConstructFromLiteral);
301     static NeverDestroyed<const AtomicString> xml("XML", AtomicString::ConstructFromLiteral);
302     if (attributeType == css)
303         m_attributeType = AttributeType::CSS;
304     else if (attributeType == xml)
305         m_attributeType = AttributeType::XML;
306     else
307         m_attributeType = AttributeType::Auto;
308     checkInvalidCSSAttributeType(targetElement());
309 }
310
311 String SVGAnimationElement::toValue() const
312 {    
313     return attributeWithoutSynchronization(SVGNames::toAttr);
314 }
315
316 String SVGAnimationElement::byValue() const
317 {    
318     return attributeWithoutSynchronization(SVGNames::byAttr);
319 }
320
321 String SVGAnimationElement::fromValue() const
322 {    
323     return attributeWithoutSynchronization(SVGNames::fromAttr);
324 }
325
326 bool SVGAnimationElement::isAdditive() const
327 {
328     static NeverDestroyed<const AtomicString> sum("sum", AtomicString::ConstructFromLiteral);
329     const AtomicString& value = attributeWithoutSynchronization(SVGNames::additiveAttr);
330     return value == sum || animationMode() == ByAnimation;
331 }
332
333 bool SVGAnimationElement::isAccumulated() const
334 {
335     static NeverDestroyed<const AtomicString> sum("sum", AtomicString::ConstructFromLiteral);
336     const AtomicString& value = attributeWithoutSynchronization(SVGNames::accumulateAttr);
337     return value == sum && animationMode() != ToAnimation;
338 }
339
340 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* element, const QualifiedName& attributeName)
341 {
342     if (element->isTextContent()
343         && (attributeName == SVGNames::xAttr || attributeName == SVGNames::yAttr))
344         return false;
345
346     return SVGElement::isAnimatableCSSProperty(attributeName);
347 }
348
349 SVGAnimationElement::ShouldApplyAnimation SVGAnimationElement::shouldApplyAnimation(SVGElement* targetElement, const QualifiedName& attributeName)
350 {
351     if (!hasValidAttributeType() || !targetElement || attributeName == anyQName())
352         return DontApplyAnimation;
353
354     // Always animate CSS properties, using the ApplyCSSAnimation code path, regardless of the attributeType value.
355     if (isTargetAttributeCSSProperty(targetElement, attributeName)) {
356         if (targetElement->isPresentationAttributeWithSVGDOM(attributeName))
357             return ApplyXMLandCSSAnimation;
358         return ApplyCSSAnimation;
359     }
360
361
362     // If attributeType="CSS" and attributeName doesn't point to a CSS property, ignore the animation.
363     if (attributeType() == AttributeType::CSS)
364         return DontApplyAnimation;
365
366     return ApplyXMLAnimation;
367 }
368
369 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
370 {
371     ASSERT(calcMode() == CalcMode::Paced);
372     ASSERT(animationMode() == ValuesAnimation);
373
374     unsigned valuesCount = m_values.size();
375     ASSERT(valuesCount >= 1);
376     if (valuesCount == 1)
377         return;
378
379     // FIXME, webkit.org/b/109010: m_keyTimes should not be modified in this function.
380     m_keyTimes.clear();
381
382     Vector<float> keyTimesForPaced;
383     float totalDistance = 0;
384     keyTimesForPaced.append(0);
385     for (unsigned n = 0; n < valuesCount - 1; ++n) {
386         // Distance in any units
387         float distance = calculateDistance(m_values[n], m_values[n + 1]);
388         if (distance < 0)
389             return;
390         totalDistance += distance;
391         keyTimesForPaced.append(distance);
392     }
393     if (!totalDistance)
394         return;
395
396     // Normalize.
397     for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
398         keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
399     keyTimesForPaced[keyTimesForPaced.size() - 1] = 1;
400
401     // Use key times calculated based on pacing instead of the user provided ones.
402     m_keyTimes = keyTimesForPaced;
403 }
404
405 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
406
407 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
408 {
409     unsigned index;
410     unsigned keyTimesCount = m_keyTimes.size();
411     // Compare index + 1 to keyTimesCount because the last keyTimes entry is
412     // required to be 1, and percent can never exceed 1; i.e., the second last
413     // keyTimes entry defines the beginning of the final interval
414     for (index = 1; index + 1 < keyTimesCount; ++index) {
415         if (m_keyTimes[index] > percent)
416             break;
417     }
418     return --index;
419 }
420
421 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
422 {
423     ASSERT(calcMode() == CalcMode::Spline);
424     ASSERT_WITH_SECURITY_IMPLICATION(splineIndex < m_keySplines.size());
425     UnitBezier bezier = m_keySplines[splineIndex];
426     SMILTime duration = simpleDuration();
427     if (!duration.isFinite())
428         duration = 100.0;
429     return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
430 }
431
432 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
433 {
434     ASSERT(!m_keyPoints.isEmpty());
435     ASSERT(calcMode() != CalcMode::Paced);
436     ASSERT(m_keyTimes.size() > 1);
437     ASSERT(m_keyPoints.size() == m_keyTimes.size());
438
439     if (percent == 1)
440         return m_keyPoints[m_keyPoints.size() - 1];
441
442     unsigned index = calculateKeyTimesIndex(percent);
443     float fromPercent = m_keyTimes[index];
444     float toPercent = m_keyTimes[index + 1];
445     float fromKeyPoint = m_keyPoints[index];
446     float toKeyPoint = m_keyPoints[index + 1];
447     
448     if (calcMode() == CalcMode::Discrete)
449         return fromKeyPoint;
450     
451     float keyPointPercent = (percent - fromPercent) / (toPercent - fromPercent);
452     
453     if (calcMode() == CalcMode::Spline) {
454         ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
455         keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
456     }
457     return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
458 }
459
460 float SVGAnimationElement::calculatePercentForFromTo(float percent) const
461 {
462     if (calcMode() == CalcMode::Discrete && m_keyTimes.size() == 2)
463         return percent > m_keyTimes[1] ? 1 : 0;
464
465     return percent;
466 }
467     
468 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
469 {
470     ASSERT(!m_keyPoints.isEmpty());
471     ASSERT(m_keyPoints.size() == m_keyTimes.size());
472     ASSERT(calcMode() != CalcMode::Paced);
473     effectivePercent = calculatePercentFromKeyPoints(percent);
474     unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
475     from = m_values[index];
476     to = m_values[index + 1];
477 }
478     
479 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to)
480 {
481     unsigned valuesCount = m_values.size();
482     ASSERT(m_animationValid);
483     ASSERT(valuesCount >= 1);
484
485     if (percent == 1 || valuesCount == 1) {
486         from = m_values[valuesCount - 1];
487         to = m_values[valuesCount - 1];
488         effectivePercent = 1;
489         return;
490     }
491
492     CalcMode calcMode = this->calcMode();
493     if (is<SVGAnimateElement>(*this) || is<SVGAnimateColorElement>(*this)) {
494         ASSERT(targetElement());
495         AnimatedPropertyType type = downcast<SVGAnimateElementBase>(*this).determineAnimatedPropertyType(*targetElement());
496         if (type == AnimatedBoolean || type == AnimatedEnumeration || type == AnimatedPreserveAspectRatio || type == AnimatedString)
497             calcMode = CalcMode::Discrete;
498     }
499     if (!m_keyPoints.isEmpty() && calcMode != CalcMode::Paced)
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 == CalcMode::Discrete) {
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_WITH_SECURITY_IMPLICATION(toPercent > fromPercent);
532     effectivePercent = (percent - fromPercent) / (toPercent - fromPercent);
533
534     if (calcMode == CalcMode::Spline) {
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 (!hasValidAttributeType())
545         return;
546
547     // These validations are appropriate for all animation modes.
548     if (hasAttributeWithoutSynchronization(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
549         return;
550
551     AnimationMode animationMode = this->animationMode();
552     CalcMode calcMode = this->calcMode();
553     if (calcMode == CalcMode::Spline) {
554         unsigned splinesCount = m_keySplines.size();
555         if (!splinesCount
556             || (hasAttributeWithoutSynchronization(SVGNames::keyPointsAttr) && m_keyPoints.size() - 1 != splinesCount)
557             || (animationMode == ValuesAnimation && m_values.size() - 1 != splinesCount)
558             || (hasAttributeWithoutSynchronization(SVGNames::keyTimesAttr) && m_keyTimes.size() - 1 != splinesCount))
559             return;
560     }
561
562     String from = fromValue();
563     String to = toValue();
564     String by = byValue();
565     if (animationMode == NoAnimation)
566         return;
567     if ((animationMode == FromToAnimation || animationMode == FromByAnimation || animationMode == ToAnimation || animationMode == ByAnimation)
568         && (hasAttributeWithoutSynchronization(SVGNames::keyPointsAttr) && hasAttributeWithoutSynchronization(SVGNames::keyTimesAttr) && (m_keyTimes.size() < 2 || m_keyTimes.size() != m_keyPoints.size())))
569         return;
570     if (animationMode == FromToAnimation)
571         m_animationValid = calculateFromAndToValues(from, to);
572     else if (animationMode == ToAnimation) {
573         // For to-animations the from value is the current accumulated value from lower priority animations.
574         // The value is not static and is determined during the animation.
575         m_animationValid = calculateFromAndToValues(emptyString(), to);
576     } else if (animationMode == FromByAnimation)
577         m_animationValid = calculateFromAndByValues(from, by);
578     else if (animationMode == ByAnimation)
579         m_animationValid = calculateFromAndByValues(emptyString(), by);
580     else if (animationMode == ValuesAnimation) {
581         m_animationValid = m_values.size() >= 1
582             && (calcMode == CalcMode::Paced || !hasAttributeWithoutSynchronization(SVGNames::keyTimesAttr) || hasAttributeWithoutSynchronization(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
583             && (calcMode == CalcMode::Discrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
584             && (calcMode != CalcMode::Spline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
585             && (!hasAttributeWithoutSynchronization(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
586         if (m_animationValid)
587             m_animationValid = calculateToAtEndOfDurationValue(m_values.last());
588         if (calcMode == CalcMode::Paced && m_animationValid)
589             calculateKeyTimesForCalcModePaced();
590     } else if (animationMode == PathAnimation)
591         m_animationValid = calcMode == CalcMode::Paced || !hasAttributeWithoutSynchronization(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
592 }
593
594 void SVGAnimationElement::updateAnimation(float percent, unsigned repeatCount, SVGSMILElement* resultElement)
595 {    
596     if (!m_animationValid)
597         return;
598
599     float effectivePercent;
600     CalcMode calcMode = this->calcMode();
601     AnimationMode animationMode = this->animationMode();
602     if (animationMode == ValuesAnimation) {
603         String from;
604         String to;
605         currentValuesForValuesAnimation(percent, effectivePercent, from, to);
606         if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
607             m_animationValid = calculateFromAndToValues(from, to);
608             if (!m_animationValid)
609                 return;
610             m_lastValuesAnimationFrom = from;
611             m_lastValuesAnimationTo = to;
612         }
613     } else if (!m_keyPoints.isEmpty() && calcMode != CalcMode::Paced)
614         effectivePercent = calculatePercentFromKeyPoints(percent);
615     else if (m_keyPoints.isEmpty() && calcMode == CalcMode::Spline && m_keyTimes.size() > 1)
616         effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
617     else if (animationMode == FromToAnimation || animationMode == ToAnimation)
618         effectivePercent = calculatePercentForFromTo(percent);
619     else
620         effectivePercent = percent;
621
622     calculateAnimatedValue(effectivePercent, repeatCount, resultElement);
623 }
624
625 void SVGAnimationElement::computeCSSPropertyValue(SVGElement* element, CSSPropertyID id, String& valueString)
626 {
627     ASSERT(element);
628
629     // Don't include any properties resulting from CSS Transitions/Animations or SMIL animations, as we want to retrieve the "base value".
630     element->setUseOverrideComputedStyle(true);
631     RefPtr<CSSValue> value = ComputedStyleExtractor(element).propertyValue(id);
632     valueString = value ? value->cssText() : String();
633     element->setUseOverrideComputedStyle(false);
634 }
635
636 void SVGAnimationElement::adjustForInheritance(SVGElement* targetElement, const QualifiedName& attributeName, String& value)
637 {
638     // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
639     // In the future we might want to work with the value type directly to avoid the String parsing.
640     ASSERT(targetElement);
641
642     auto parent = makeRefPtr(targetElement->parentElement());
643     if (!parent || !parent->isSVGElement())
644         return;
645
646     SVGElement& svgParent = downcast<SVGElement>(*parent);
647     computeCSSPropertyValue(&svgParent, cssPropertyID(attributeName.localName()), value);
648 }
649
650 static bool inheritsFromProperty(SVGElement*, const QualifiedName& attributeName, const String& value)
651 {
652     static NeverDestroyed<const AtomicString> inherit("inherit", AtomicString::ConstructFromLiteral);
653     
654     if (value.isEmpty() || value != inherit)
655         return false;
656     return SVGElement::isAnimatableCSSProperty(attributeName);
657 }
658
659 void SVGAnimationElement::determinePropertyValueTypes(const String& from, const String& to)
660 {
661     auto targetElement = makeRefPtr(this->targetElement());
662     ASSERT(targetElement);
663
664     const QualifiedName& attributeName = this->attributeName();
665     if (inheritsFromProperty(targetElement.get(), attributeName, from))
666         m_fromPropertyValueType = InheritValue;
667     if (inheritsFromProperty(targetElement.get(), attributeName, to))
668         m_toPropertyValueType = InheritValue;
669 }
670 void SVGAnimationElement::resetAnimatedPropertyType()
671 {
672     m_lastValuesAnimationFrom = String();
673     m_lastValuesAnimationTo = String();
674 }
675
676 void SVGAnimationElement::setTargetElement(SVGElement* target)
677 {
678     SVGSMILElement::setTargetElement(target);
679     checkInvalidCSSAttributeType(target);
680 }
681
682 void SVGAnimationElement::checkInvalidCSSAttributeType(SVGElement* target)
683 {
684     m_hasInvalidCSSAttributeType = target && hasValidAttributeName() && attributeType() == AttributeType::CSS && !isTargetAttributeCSSProperty(target, attributeName());
685 }
686
687 }