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.
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.
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.
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.
26 #include "core/svg/SVGAnimationElement.h"
28 #include "bindings/core/v8/ExceptionState.h"
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"
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
)
52 ASSERT(RuntimeEnabledFeatures::smilEnabled());
53 UseCounter::count(document
, UseCounter::SVGAnimationElement
);
56 bool SVGAnimationElement::parseValues(const String
& value
, Vector
<String
>& result
)
58 // Per the SMIL specification, leading and trailing white space,
59 // and white space before and after semicolon separators, is allowed and will be ignored.
60 // http://www.w3.org/TR/SVG11/animate.html#ValuesAttribute
62 Vector
<String
> parseList
;
63 value
.split(';', true, parseList
);
64 unsigned last
= parseList
.size() - 1;
65 for (unsigned i
= 0; i
<= last
; ++i
) {
66 if (parseList
[i
].isEmpty()) {
67 // Tolerate trailing ';'
71 parseList
[i
] = parseList
[i
].stripWhiteSpace();
72 result
.append(parseList
[i
]);
82 static bool parseKeyTimes(const String
& string
, Vector
<float>& result
, bool verifyOrder
)
85 Vector
<String
> parseList
;
86 string
.split(';', true, parseList
);
87 for (unsigned n
= 0; n
< parseList
.size(); ++n
) {
88 String timeString
= parseList
[n
].stripWhiteSpace();
90 float time
= timeString
.toFloat(&ok
);
91 if (!ok
|| time
< 0 || time
> 1)
97 } else if (time
< result
.last()) {
109 template<typename CharType
>
110 static bool parseKeySplinesInternal(const String
& string
, Vector
<UnitBezier
>& result
)
112 const CharType
* ptr
= string
.getCharacters
<CharType
>();
113 const CharType
* end
= ptr
+ string
.length();
115 skipOptionalSVGSpaces(ptr
, end
);
119 if (!parseNumber(ptr
, end
, posA
))
123 if (!parseNumber(ptr
, end
, posB
))
127 if (!parseNumber(ptr
, end
, posC
))
131 if (!parseNumber(ptr
, end
, posD
, DisallowWhitespace
))
134 skipOptionalSVGSpaces(ptr
, end
);
136 if (ptr
< end
&& *ptr
== ';')
138 skipOptionalSVGSpaces(ptr
, end
);
140 result
.append(UnitBezier(posA
, posB
, posC
, posD
));
146 static bool parseKeySplines(const String
& string
, Vector
<UnitBezier
>& result
)
149 if (string
.isEmpty())
153 parsed
= parseKeySplinesInternal
<LChar
>(string
, result
);
155 parsed
= parseKeySplinesInternal
<UChar
>(string
, result
);
163 void SVGAnimationElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
165 if (name
== SVGNames::valuesAttr
) {
166 if (!parseValues(value
, m_values
)) {
167 reportAttributeParsingError(ParsingAttributeFailedError
, name
, value
);
170 updateAnimationMode();
174 if (name
== SVGNames::keyTimesAttr
) {
175 if (!parseKeyTimes(value
, m_keyTimes
, true))
176 reportAttributeParsingError(ParsingAttributeFailedError
, name
, value
);
180 if (name
== SVGNames::keyPointsAttr
) {
181 if (isSVGAnimateMotionElement(*this)) {
182 // This is specified to be an animateMotion attribute only but it is simpler to put it here
183 // where the other timing calculatations are.
184 if (!parseKeyTimes(value
, m_keyPoints
, false))
185 reportAttributeParsingError(ParsingAttributeFailedError
, name
, value
);
190 if (name
== SVGNames::keySplinesAttr
) {
191 if (!parseKeySplines(value
, m_keySplines
))
192 reportAttributeParsingError(ParsingAttributeFailedError
, name
, value
);
196 if (name
== SVGNames::attributeTypeAttr
) {
197 setAttributeType(value
);
201 if (name
== SVGNames::calcModeAttr
) {
206 if (name
== SVGNames::fromAttr
|| name
== SVGNames::toAttr
|| name
== SVGNames::byAttr
) {
207 updateAnimationMode();
211 SVGSMILElement::parseAttribute(name
, value
);
214 void SVGAnimationElement::svgAttributeChanged(const QualifiedName
& attrName
)
216 if (attrName
== SVGNames::valuesAttr
217 || attrName
== SVGNames::byAttr
218 || attrName
== SVGNames::fromAttr
219 || attrName
== SVGNames::toAttr
220 || attrName
== SVGNames::calcModeAttr
221 || attrName
== SVGNames::attributeTypeAttr
222 || attrName
== SVGNames::keySplinesAttr
223 || attrName
== SVGNames::keyPointsAttr
224 || attrName
== SVGNames::keyTimesAttr
) {
225 animationAttributeChanged();
229 SVGSMILElement::svgAttributeChanged(attrName
);
232 void SVGAnimationElement::animationAttributeChanged()
234 // Assumptions may not hold after an attribute change.
235 m_animationValid
= false;
236 m_lastValuesAnimationFrom
= String();
237 m_lastValuesAnimationTo
= String();
241 float SVGAnimationElement::getStartTime(ExceptionState
& exceptionState
) const
243 SMILTime startTime
= intervalBegin();
244 if (!startTime
.isFinite()) {
245 exceptionState
.throwDOMException(InvalidStateError
, "No current interval.");
248 return narrowPrecisionToFloat(startTime
.value());
251 float SVGAnimationElement::getCurrentTime() const
253 return narrowPrecisionToFloat(elapsed().value());
256 float SVGAnimationElement::getSimpleDuration(ExceptionState
& exceptionState
) const
258 SMILTime duration
= simpleDuration();
259 if (!duration
.isFinite()) {
260 exceptionState
.throwDOMException(NotSupportedError
, "No simple duration defined.");
263 return narrowPrecisionToFloat(duration
.value());
266 void SVGAnimationElement::beginElement()
271 void SVGAnimationElement::beginElementAt(float offset
)
273 ASSERT(std::isfinite(offset
));
274 SMILTime elapsed
= this->elapsed();
275 addBeginTime(elapsed
, elapsed
+ offset
, SMILTimeWithOrigin::ScriptOrigin
);
278 void SVGAnimationElement::endElement()
283 void SVGAnimationElement::endElementAt(float offset
)
285 ASSERT(std::isfinite(offset
));
286 SMILTime elapsed
= this->elapsed();
287 addEndTime(elapsed
, elapsed
+ offset
, SMILTimeWithOrigin::ScriptOrigin
);
290 void SVGAnimationElement::updateAnimationMode()
292 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
293 if (hasAttribute(SVGNames::valuesAttr
))
294 setAnimationMode(ValuesAnimation
);
295 else if (!toValue().isEmpty())
296 setAnimationMode(fromValue().isEmpty() ? ToAnimation
: FromToAnimation
);
297 else if (!byValue().isEmpty())
298 setAnimationMode(fromValue().isEmpty() ? ByAnimation
: FromByAnimation
);
300 setAnimationMode(NoAnimation
);
303 void SVGAnimationElement::setCalcMode(const AtomicString
& calcMode
)
305 DEFINE_STATIC_LOCAL(const AtomicString
, discrete
, ("discrete", AtomicString::ConstructFromLiteral
));
306 DEFINE_STATIC_LOCAL(const AtomicString
, linear
, ("linear", AtomicString::ConstructFromLiteral
));
307 DEFINE_STATIC_LOCAL(const AtomicString
, paced
, ("paced", AtomicString::ConstructFromLiteral
));
308 DEFINE_STATIC_LOCAL(const AtomicString
, spline
, ("spline", AtomicString::ConstructFromLiteral
));
309 if (calcMode
== discrete
)
310 setCalcMode(CalcModeDiscrete
);
311 else if (calcMode
== linear
)
312 setCalcMode(CalcModeLinear
);
313 else if (calcMode
== paced
)
314 setCalcMode(CalcModePaced
);
315 else if (calcMode
== spline
)
316 setCalcMode(CalcModeSpline
);
318 setCalcMode(isSVGAnimateMotionElement(*this) ? CalcModePaced
: CalcModeLinear
);
321 void SVGAnimationElement::setAttributeType(const AtomicString
& attributeType
)
323 DEFINE_STATIC_LOCAL(const AtomicString
, css
, ("CSS", AtomicString::ConstructFromLiteral
));
324 DEFINE_STATIC_LOCAL(const AtomicString
, xml
, ("XML", AtomicString::ConstructFromLiteral
));
325 if (attributeType
== css
)
326 m_attributeType
= AttributeTypeCSS
;
327 else if (attributeType
== xml
)
328 m_attributeType
= AttributeTypeXML
;
330 m_attributeType
= AttributeTypeAuto
;
331 checkInvalidCSSAttributeType();
334 String
SVGAnimationElement::toValue() const
336 return fastGetAttribute(SVGNames::toAttr
);
339 String
SVGAnimationElement::byValue() const
341 return fastGetAttribute(SVGNames::byAttr
);
344 String
SVGAnimationElement::fromValue() const
346 return fastGetAttribute(SVGNames::fromAttr
);
349 bool SVGAnimationElement::isAdditive()
351 DEFINE_STATIC_LOCAL(const AtomicString
, sum
, ("sum", AtomicString::ConstructFromLiteral
));
352 const AtomicString
& value
= fastGetAttribute(SVGNames::additiveAttr
);
353 return value
== sum
|| animationMode() == ByAnimation
;
356 bool SVGAnimationElement::isAccumulated() const
358 DEFINE_STATIC_LOCAL(const AtomicString
, sum
, ("sum", AtomicString::ConstructFromLiteral
));
359 const AtomicString
& value
= fastGetAttribute(SVGNames::accumulateAttr
);
360 return value
== sum
&& animationMode() != ToAnimation
;
363 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement
* targetElement
, const QualifiedName
& attributeName
)
365 ASSERT(targetElement
);
367 return SVGElement::isAnimatableCSSProperty(attributeName
) || targetElement
->isPresentationAttribute(attributeName
);
370 SVGAnimationElement::ShouldApplyAnimation
SVGAnimationElement::shouldApplyAnimation(SVGElement
* targetElement
, const QualifiedName
& attributeName
)
372 if (!hasValidAttributeType() || !targetElement
|| attributeName
== anyQName() || !targetElement
->inActiveDocument())
373 return DontApplyAnimation
;
375 // Always animate CSS properties, using the ApplyCSSAnimation code path, regardless of the attributeType value.
376 if (isTargetAttributeCSSProperty(targetElement
, attributeName
)) {
377 if (targetElement
->isPresentationAttributeWithSVGDOM(attributeName
))
378 return ApplyXMLandCSSAnimation
;
380 return ApplyCSSAnimation
;
382 // If attributeType="CSS" and attributeName doesn't point to a CSS property, ignore the animation.
383 if (attributeType() == AttributeTypeCSS
)
384 return DontApplyAnimation
;
386 return ApplyXMLAnimation
;
389 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
391 ASSERT(calcMode() == CalcModePaced
);
392 ASSERT(animationMode() == ValuesAnimation
);
394 unsigned valuesCount
= m_values
.size();
395 ASSERT(valuesCount
>= 1);
396 if (valuesCount
== 1)
399 // FIXME, webkit.org/b/109010: m_keyTimes should not be modified in this function.
402 Vector
<float> keyTimesForPaced
;
403 float totalDistance
= 0;
404 keyTimesForPaced
.append(0);
405 for (unsigned n
= 0; n
< valuesCount
- 1; ++n
) {
406 // Distance in any units
407 float distance
= calculateDistance(m_values
[n
], m_values
[n
+ 1]);
410 totalDistance
+= distance
;
411 keyTimesForPaced
.append(distance
);
417 for (unsigned n
= 1; n
< keyTimesForPaced
.size() - 1; ++n
)
418 keyTimesForPaced
[n
] = keyTimesForPaced
[n
- 1] + keyTimesForPaced
[n
] / totalDistance
;
419 keyTimesForPaced
[keyTimesForPaced
.size() - 1] = 1;
421 // Use key times calculated based on pacing instead of the user provided ones.
422 m_keyTimes
= keyTimesForPaced
;
425 static inline double solveEpsilon(double duration
) { return 1 / (200 * duration
); }
427 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent
) const
430 unsigned keyTimesCount
= m_keyTimes
.size();
431 // For linear and spline animations, the last value must be '1'. In those
432 // cases we don't need to consider the last value, since |percent| is never
434 if (keyTimesCount
&& calcMode() != CalcModeDiscrete
)
436 for (index
= 1; index
< keyTimesCount
; ++index
) {
437 if (m_keyTimes
[index
] > percent
)
443 float SVGAnimationElement::calculatePercentForSpline(float percent
, unsigned splineIndex
) const
445 ASSERT(calcMode() == CalcModeSpline
);
446 ASSERT_WITH_SECURITY_IMPLICATION(splineIndex
< m_keySplines
.size());
447 UnitBezier bezier
= m_keySplines
[splineIndex
];
448 SMILTime duration
= simpleDuration();
449 if (!duration
.isFinite())
451 return narrowPrecisionToFloat(bezier
.solve(percent
, solveEpsilon(duration
.value())));
454 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent
) const
456 ASSERT(!m_keyPoints
.isEmpty());
457 ASSERT(calcMode() != CalcModePaced
);
458 ASSERT(m_keyTimes
.size() > 1);
459 ASSERT(m_keyPoints
.size() == m_keyTimes
.size());
462 return m_keyPoints
[m_keyPoints
.size() - 1];
464 unsigned index
= calculateKeyTimesIndex(percent
);
465 float fromKeyPoint
= m_keyPoints
[index
];
467 if (calcMode() == CalcModeDiscrete
)
470 ASSERT(index
+ 1 < m_keyTimes
.size());
471 float fromPercent
= m_keyTimes
[index
];
472 float toPercent
= m_keyTimes
[index
+ 1];
473 float toKeyPoint
= m_keyPoints
[index
+ 1];
474 float keyPointPercent
= (percent
- fromPercent
) / (toPercent
- fromPercent
);
476 if (calcMode() == CalcModeSpline
) {
477 ASSERT(m_keySplines
.size() == m_keyPoints
.size() - 1);
478 keyPointPercent
= calculatePercentForSpline(keyPointPercent
, index
);
480 return (toKeyPoint
- fromKeyPoint
) * keyPointPercent
+ fromKeyPoint
;
483 float SVGAnimationElement::calculatePercentForFromTo(float percent
) const
485 if (calcMode() == CalcModeDiscrete
&& m_keyTimes
.size() == 2)
486 return percent
> m_keyTimes
[1] ? 1 : 0;
491 void SVGAnimationElement::currentValuesFromKeyPoints(float percent
, float& effectivePercent
, String
& from
, String
& to
) const
493 ASSERT(!m_keyPoints
.isEmpty());
494 ASSERT(m_keyPoints
.size() == m_keyTimes
.size());
495 ASSERT(calcMode() != CalcModePaced
);
496 effectivePercent
= calculatePercentFromKeyPoints(percent
);
497 unsigned index
= effectivePercent
== 1 ? m_values
.size() - 2 : static_cast<unsigned>(effectivePercent
* (m_values
.size() - 1));
498 from
= m_values
[index
];
499 to
= m_values
[index
+ 1];
502 void SVGAnimationElement::currentValuesForValuesAnimation(float percent
, float& effectivePercent
, String
& from
, String
& to
)
504 unsigned valuesCount
= m_values
.size();
505 ASSERT(m_animationValid
);
506 ASSERT(valuesCount
>= 1);
508 if (percent
== 1 || valuesCount
== 1) {
509 from
= m_values
[valuesCount
- 1];
510 to
= m_values
[valuesCount
- 1];
511 effectivePercent
= 1;
515 CalcMode calcMode
= this->calcMode();
516 if (isSVGAnimateElement(*this)) {
517 SVGAnimateElement
& animateElement
= toSVGAnimateElement(*this);
518 if (!animateElement
.animatedPropertyTypeSupportsAddition()) {
519 ASSERT(animateElement
.animatedPropertyType() != AnimatedTransformList
|| isSVGAnimateTransformElement(*this));
520 ASSERT(animateElement
.animatedPropertyType() != AnimatedUnknown
);
521 calcMode
= CalcModeDiscrete
;
524 if (!m_keyPoints
.isEmpty() && calcMode
!= CalcModePaced
)
525 return currentValuesFromKeyPoints(percent
, effectivePercent
, from
, to
);
527 unsigned keyTimesCount
= m_keyTimes
.size();
528 ASSERT(!keyTimesCount
|| valuesCount
== keyTimesCount
);
529 ASSERT(!keyTimesCount
|| (keyTimesCount
> 1 && !m_keyTimes
[0]));
531 unsigned index
= calculateKeyTimesIndex(percent
);
532 if (calcMode
== CalcModeDiscrete
) {
534 index
= static_cast<unsigned>(percent
* valuesCount
);
535 from
= m_values
[index
];
536 to
= m_values
[index
];
537 effectivePercent
= 0;
544 fromPercent
= m_keyTimes
[index
];
545 toPercent
= m_keyTimes
[index
+ 1];
547 index
= static_cast<unsigned>(floorf(percent
* (valuesCount
- 1)));
548 fromPercent
= static_cast<float>(index
) / (valuesCount
- 1);
549 toPercent
= static_cast<float>(index
+ 1) / (valuesCount
- 1);
552 if (index
== valuesCount
- 1)
554 from
= m_values
[index
];
555 to
= m_values
[index
+ 1];
556 ASSERT(toPercent
> fromPercent
);
557 effectivePercent
= (percent
- fromPercent
) / (toPercent
- fromPercent
);
559 if (calcMode
== CalcModeSpline
) {
560 ASSERT(m_keySplines
.size() == m_values
.size() - 1);
561 effectivePercent
= calculatePercentForSpline(effectivePercent
, index
);
565 void SVGAnimationElement::startedActiveInterval()
567 m_animationValid
= false;
572 if (!hasValidAttributeType())
575 // These validations are appropriate for all animation modes.
576 if (fastHasAttribute(SVGNames::keyPointsAttr
) && m_keyPoints
.size() != m_keyTimes
.size())
579 AnimationMode animationMode
= this->animationMode();
580 CalcMode calcMode
= this->calcMode();
581 if (calcMode
== CalcModeSpline
) {
582 unsigned splinesCount
= m_keySplines
.size();
584 || (fastHasAttribute(SVGNames::keyPointsAttr
) && m_keyPoints
.size() - 1 != splinesCount
)
585 || (animationMode
== ValuesAnimation
&& m_values
.size() - 1 != splinesCount
)
586 || (fastHasAttribute(SVGNames::keyTimesAttr
) && m_keyTimes
.size() - 1 != splinesCount
))
590 String from
= fromValue();
591 String to
= toValue();
592 String by
= byValue();
593 if (animationMode
== NoAnimation
)
595 if ((animationMode
== FromToAnimation
|| animationMode
== FromByAnimation
|| animationMode
== ToAnimation
|| animationMode
== ByAnimation
)
596 && (fastHasAttribute(SVGNames::keyPointsAttr
) && fastHasAttribute(SVGNames::keyTimesAttr
) && (m_keyTimes
.size() < 2 || m_keyTimes
.size() != m_keyPoints
.size())))
598 if (animationMode
== FromToAnimation
) {
599 m_animationValid
= calculateFromAndToValues(from
, to
);
600 } else if (animationMode
== ToAnimation
) {
601 // For to-animations the from value is the current accumulated value from lower priority animations.
602 // The value is not static and is determined during the animation.
603 m_animationValid
= calculateFromAndToValues(emptyString(), to
);
604 } else if (animationMode
== FromByAnimation
) {
605 m_animationValid
= calculateFromAndByValues(from
, by
);
606 } else if (animationMode
== ByAnimation
) {
607 m_animationValid
= calculateFromAndByValues(emptyString(), by
);
608 } else if (animationMode
== ValuesAnimation
) {
609 m_animationValid
= m_values
.size() >= 1
610 && (calcMode
== CalcModePaced
|| !fastHasAttribute(SVGNames::keyTimesAttr
) || fastHasAttribute(SVGNames::keyPointsAttr
) || (m_values
.size() == m_keyTimes
.size()))
611 && (calcMode
== CalcModeDiscrete
|| !m_keyTimes
.size() || m_keyTimes
.last() == 1)
612 && (calcMode
!= CalcModeSpline
|| ((m_keySplines
.size() && (m_keySplines
.size() == m_values
.size() - 1)) || m_keySplines
.size() == m_keyPoints
.size() - 1))
613 && (!fastHasAttribute(SVGNames::keyPointsAttr
) || (m_keyTimes
.size() > 1 && m_keyTimes
.size() == m_keyPoints
.size()));
614 if (m_animationValid
)
615 m_animationValid
= calculateToAtEndOfDurationValue(m_values
.last());
616 if (calcMode
== CalcModePaced
&& m_animationValid
)
617 calculateKeyTimesForCalcModePaced();
618 } else if (animationMode
== PathAnimation
) {
619 m_animationValid
= calcMode
== CalcModePaced
|| !fastHasAttribute(SVGNames::keyPointsAttr
) || (m_keyTimes
.size() > 1 && m_keyTimes
.size() == m_keyPoints
.size());
623 void SVGAnimationElement::updateAnimation(float percent
, unsigned repeatCount
, SVGSMILElement
* resultElement
)
625 if (!m_animationValid
)
628 float effectivePercent
;
629 CalcMode calcMode
= this->calcMode();
630 AnimationMode animationMode
= this->animationMode();
631 if (animationMode
== ValuesAnimation
) {
634 currentValuesForValuesAnimation(percent
, effectivePercent
, from
, to
);
635 if (from
!= m_lastValuesAnimationFrom
|| to
!= m_lastValuesAnimationTo
) {
636 m_animationValid
= calculateFromAndToValues(from
, to
);
637 if (!m_animationValid
)
639 m_lastValuesAnimationFrom
= from
;
640 m_lastValuesAnimationTo
= to
;
642 } else if (!m_keyPoints
.isEmpty() && calcMode
!= CalcModePaced
)
643 effectivePercent
= calculatePercentFromKeyPoints(percent
);
644 else if (m_keyPoints
.isEmpty() && calcMode
== CalcModeSpline
&& m_keyTimes
.size() > 1)
645 effectivePercent
= calculatePercentForSpline(percent
, calculateKeyTimesIndex(percent
));
646 else if (animationMode
== FromToAnimation
|| animationMode
== ToAnimation
)
647 effectivePercent
= calculatePercentForFromTo(percent
);
649 effectivePercent
= percent
;
651 calculateAnimatedValue(effectivePercent
, repeatCount
, resultElement
);
654 void SVGAnimationElement::computeCSSPropertyValue(SVGElement
* element
, CSSPropertyID id
, String
& value
)
657 // FIXME: StyleEngine doesn't support document without a frame.
658 // Refer to comment in Element::computedStyle.
659 ASSERT(element
->inActiveDocument());
661 // Don't include any properties resulting from CSS Transitions/Animations or SMIL animations, as we want to retrieve the "base value".
662 element
->setUseOverrideComputedStyle(true);
663 value
= CSSComputedStyleDeclaration::create(element
)->getPropertyValue(id
);
664 element
->setUseOverrideComputedStyle(false);
667 void SVGAnimationElement::adjustForInheritance(SVGElement
* targetElement
, const QualifiedName
& attributeName
, String
& value
)
669 // FIXME: At the moment the computed style gets returned as a String and needs to get parsed again.
670 // In the future we might want to work with the value type directly to avoid the String parsing.
671 ASSERT(targetElement
);
673 Element
* parent
= targetElement
->parentElement();
674 if (!parent
|| !parent
->isSVGElement())
677 SVGElement
* svgParent
= toSVGElement(parent
);
678 computeCSSPropertyValue(svgParent
, cssPropertyID(attributeName
.localName()), value
);
681 static bool inheritsFromProperty(SVGElement
* targetElement
, const QualifiedName
& attributeName
, const String
& value
)
683 ASSERT(targetElement
);
684 DEFINE_STATIC_LOCAL(const AtomicString
, inherit
, ("inherit", AtomicString::ConstructFromLiteral
));
686 if (value
.isEmpty() || value
!= inherit
)
688 return SVGElement::isAnimatableCSSProperty(attributeName
);
691 void SVGAnimationElement::determinePropertyValueTypes(const String
& from
, const String
& to
)
693 SVGElement
* targetElement
= this->targetElement();
694 ASSERT(targetElement
);
696 const QualifiedName
& attributeName
= this->attributeName();
697 if (inheritsFromProperty(targetElement
, attributeName
, from
))
698 m_fromPropertyValueType
= InheritValue
;
699 if (inheritsFromProperty(targetElement
, attributeName
, to
))
700 m_toPropertyValueType
= InheritValue
;
703 void SVGAnimationElement::setTargetElement(SVGElement
* target
)
705 SVGSMILElement::setTargetElement(target
);
706 checkInvalidCSSAttributeType();
709 void SVGAnimationElement::setAttributeName(const QualifiedName
& attributeName
)
711 SVGSMILElement::setAttributeName(attributeName
);
712 checkInvalidCSSAttributeType();
715 void SVGAnimationElement::checkInvalidCSSAttributeType()
717 bool hasInvalidCSSAttributeType
= targetElement() && hasValidAttributeName() && attributeType() == AttributeTypeCSS
&& !isTargetAttributeCSSProperty(targetElement(), attributeName());
719 if (hasInvalidCSSAttributeType
!= m_hasInvalidCSSAttributeType
) {
720 if (hasInvalidCSSAttributeType
)
721 unscheduleIfScheduled();
723 m_hasInvalidCSSAttributeType
= hasInvalidCSSAttributeType
;
725 if (!hasInvalidCSSAttributeType
)
729 // Clear values that may depend on the previous target.