• 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 #if ENABLE(SVG_ANIMATION)
28 #include "SVGAnimationElement.h"
29 
30 #include "Attribute.h"
31 #include "CSSComputedStyleDeclaration.h"
32 #include "CSSParser.h"
33 #include "CSSPropertyNames.h"
34 #include "Color.h"
35 #include "Document.h"
36 #include "Event.h"
37 #include "EventListener.h"
38 #include "FloatConversion.h"
39 #include "HTMLNames.h"
40 #include "PlatformString.h"
41 #include "SVGElementInstance.h"
42 #include "SVGNames.h"
43 #include "SVGParserUtilities.h"
44 #include "SVGStyledElement.h"
45 #include "SVGURIReference.h"
46 #include "SVGUseElement.h"
47 #include "XLinkNames.h"
48 #include <wtf/StdLibExtras.h>
49 
50 using namespace std;
51 
52 namespace WebCore {
53 
54 // Animated property definitions
DEFINE_ANIMATED_BOOLEAN(SVGAnimationElement,SVGNames::externalResourcesRequiredAttr,ExternalResourcesRequired,externalResourcesRequired)55 DEFINE_ANIMATED_BOOLEAN(SVGAnimationElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
56 
57 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document* document)
58     : SVGSMILElement(tagName, document)
59     , m_animationValid(false)
60 {
61 }
62 
parseKeyTimes(const String & parse,Vector<float> & result,bool verifyOrder)63 static void parseKeyTimes(const String& parse, Vector<float>& result, bool verifyOrder)
64 {
65     result.clear();
66     Vector<String> parseList;
67     parse.split(';', parseList);
68     for (unsigned n = 0; n < parseList.size(); ++n) {
69         String timeString = parseList[n];
70         bool ok;
71         float time = timeString.toFloat(&ok);
72         if (!ok || time < 0 || time > 1)
73             goto fail;
74         if (verifyOrder) {
75             if (!n) {
76                 if (time)
77                     goto fail;
78             } else if (time < result.last())
79                 goto fail;
80         }
81         result.append(time);
82     }
83     return;
84 fail:
85     result.clear();
86 }
87 
parseKeySplines(const String & parse,Vector<UnitBezier> & result)88 static void parseKeySplines(const String& parse, Vector<UnitBezier>& result)
89 {
90     result.clear();
91     if (parse.isEmpty())
92         return;
93     const UChar* cur = parse.characters();
94     const UChar* end = cur + parse.length();
95 
96     skipOptionalSpaces(cur, end);
97 
98     bool delimParsed = false;
99     while (cur < end) {
100         delimParsed = false;
101         float posA = 0;
102         if (!parseNumber(cur, end, posA)) {
103             result.clear();
104             return;
105         }
106 
107         float posB = 0;
108         if (!parseNumber(cur, end, posB)) {
109             result.clear();
110             return;
111         }
112 
113         float posC = 0;
114         if (!parseNumber(cur, end, posC)) {
115             result.clear();
116             return;
117         }
118 
119         float posD = 0;
120         if (!parseNumber(cur, end, posD, false)) {
121             result.clear();
122             return;
123         }
124 
125         skipOptionalSpaces(cur, end);
126 
127         if (cur < end && *cur == ';') {
128             delimParsed = true;
129             cur++;
130         }
131         skipOptionalSpaces(cur, end);
132 
133         result.append(UnitBezier(posA, posB, posC, posD));
134     }
135     if (!(cur == end && !delimParsed))
136         result.clear();
137 }
138 
parseMappedAttribute(Attribute * attr)139 void SVGAnimationElement::parseMappedAttribute(Attribute* attr)
140 {
141     if (attr->name() == SVGNames::valuesAttr)
142         attr->value().string().split(';', m_values);
143     else if (attr->name() == SVGNames::keyTimesAttr)
144         parseKeyTimes(attr->value(), m_keyTimes, true);
145     else if (attr->name() == SVGNames::keyPointsAttr && hasTagName(SVGNames::animateMotionTag)) {
146         // This is specified to be an animateMotion attribute only but it is simpler to put it here
147         // where the other timing calculatations are.
148         parseKeyTimes(attr->value(), m_keyPoints, false);
149     } else if (attr->name() == SVGNames::keySplinesAttr)
150         parseKeySplines(attr->value(), m_keySplines);
151     else {
152         if (SVGTests::parseMappedAttribute(attr))
153             return;
154         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
155             return;
156         SVGSMILElement::parseMappedAttribute(attr);
157     }
158 }
159 
attributeChanged(Attribute * attr,bool preserveDecls)160 void SVGAnimationElement::attributeChanged(Attribute* attr, bool preserveDecls)
161 {
162     // Assumptions may not hold after an attribute change.
163     m_animationValid = false;
164     SVGSMILElement::attributeChanged(attr, preserveDecls);
165 }
166 
synchronizeProperty(const QualifiedName & attrName)167 void SVGAnimationElement::synchronizeProperty(const QualifiedName& attrName)
168 {
169     SVGSMILElement::synchronizeProperty(attrName);
170 
171     if (attrName == anyQName()) {
172         synchronizeExternalResourcesRequired();
173         SVGTests::synchronizeProperties(this, attrName);
174         return;
175     }
176 
177     if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
178         synchronizeExternalResourcesRequired();
179     else if (SVGTests::isKnownAttribute(attrName))
180         SVGTests::synchronizeProperties(this, attrName);
181 }
182 
getStartTime() const183 float SVGAnimationElement::getStartTime() const
184 {
185     return narrowPrecisionToFloat(intervalBegin().value());
186 }
187 
getCurrentTime() const188 float SVGAnimationElement::getCurrentTime() const
189 {
190     return narrowPrecisionToFloat(elapsed().value());
191 }
192 
getSimpleDuration(ExceptionCode &) const193 float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const
194 {
195     return narrowPrecisionToFloat(simpleDuration().value());
196 }
197 
beginElement()198 void SVGAnimationElement::beginElement()
199 {
200     beginElementAt(0);
201 }
202 
beginElementAt(float offset)203 void SVGAnimationElement::beginElementAt(float offset)
204 {
205     addBeginTime(elapsed() + offset);
206 }
207 
endElement()208 void SVGAnimationElement::endElement()
209 {
210     endElementAt(0);
211 }
212 
endElementAt(float offset)213 void SVGAnimationElement::endElementAt(float offset)
214 {
215     addEndTime(elapsed() + offset);
216 }
217 
animationMode() const218 SVGAnimationElement::AnimationMode SVGAnimationElement::animationMode() const
219 {
220     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
221     if (hasTagName(SVGNames::setTag))
222         return ToAnimation;
223     if (!animationPath().isEmpty())
224         return PathAnimation;
225     if (hasAttribute(SVGNames::valuesAttr))
226         return ValuesAnimation;
227     if (!toValue().isEmpty())
228         return fromValue().isEmpty() ? ToAnimation : FromToAnimation;
229     if (!byValue().isEmpty())
230         return fromValue().isEmpty() ? ByAnimation : FromByAnimation;
231     return NoAnimation;
232 }
233 
calcMode() const234 SVGAnimationElement::CalcMode SVGAnimationElement::calcMode() const
235 {
236     DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete"));
237     DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear"));
238     DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced"));
239     DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline"));
240     const AtomicString& value = getAttribute(SVGNames::calcModeAttr);
241     if (value == discrete)
242         return CalcModeDiscrete;
243     if (value == linear)
244         return CalcModeLinear;
245     if (value == paced)
246         return CalcModePaced;
247     if (value == spline)
248         return CalcModeSpline;
249     return hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear;
250 }
251 
attributeType() const252 SVGAnimationElement::AttributeType SVGAnimationElement::attributeType() const
253 {
254     DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS"));
255     DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML"));
256     const AtomicString& value = getAttribute(SVGNames::attributeTypeAttr);
257     if (value == css)
258         return AttributeTypeCSS;
259     if (value == xml)
260         return AttributeTypeXML;
261     return AttributeTypeAuto;
262 }
263 
toValue() const264 String SVGAnimationElement::toValue() const
265 {
266     return getAttribute(SVGNames::toAttr);
267 }
268 
byValue() const269 String SVGAnimationElement::byValue() const
270 {
271     return getAttribute(SVGNames::byAttr);
272 }
273 
fromValue() const274 String SVGAnimationElement::fromValue() const
275 {
276     return getAttribute(SVGNames::fromAttr);
277 }
278 
isAdditive() const279 bool SVGAnimationElement::isAdditive() const
280 {
281     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
282     const AtomicString& value = getAttribute(SVGNames::additiveAttr);
283     return value == sum || animationMode() == ByAnimation;
284 }
285 
isAccumulated() const286 bool SVGAnimationElement::isAccumulated() const
287 {
288     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
289     const AtomicString& value = getAttribute(SVGNames::accumulateAttr);
290     return value == sum && animationMode() != ToAnimation;
291 }
292 
isTargetAttributeCSSProperty(SVGElement * targetElement,const QualifiedName & attributeName)293 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName)
294 {
295     ASSERT(targetElement);
296     if (!targetElement->isStyled())
297         return false;
298 
299     return SVGStyledElement::isAnimatableCSSProperty(attributeName);
300 }
301 
setTargetAttributeAnimatedValue(const String & value)302 void SVGAnimationElement::setTargetAttributeAnimatedValue(const String& value)
303 {
304     if (!hasValidAttributeType())
305         return;
306     SVGElement* targetElement = this->targetElement();
307     QualifiedName attributeName = this->attributeName();
308     if (!targetElement || attributeName == anyQName() || value.isNull())
309         return;
310 
311     // We don't want the instance tree to get rebuild. Instances are updated in the loop below.
312     if (targetElement->isStyled())
313         static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(true);
314 
315     bool attributeIsCSSProperty = isTargetAttributeCSSProperty(targetElement, attributeName);
316     // Stop animation, if attributeType is set to CSS by the user, but the attribute itself is not a CSS property.
317     if (!attributeIsCSSProperty && attributeType() == AttributeTypeCSS)
318         return;
319 
320     ExceptionCode ec;
321     if (attributeIsCSSProperty) {
322         // FIXME: This should set the override style, not the inline style.
323         // Sadly override styles are not yet implemented.
324         targetElement->style()->setProperty(attributeName.localName(), value, "", ec);
325     } else {
326         // FIXME: This should set the 'presentation' value, not the actual
327         // attribute value. Whatever that means in practice.
328         targetElement->setAttribute(attributeName, value, ec);
329     }
330 
331     if (targetElement->isStyled())
332         static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(false);
333 
334     // If the target element is used in an <use> instance tree, update that as well.
335     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
336     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
337     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
338         SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
339         if (!shadowTreeElement)
340             continue;
341         if (attributeIsCSSProperty)
342             shadowTreeElement->style()->setProperty(attributeName.localName(), value, "", ec);
343         else
344             shadowTreeElement->setAttribute(attributeName, value, ec);
345         (*it)->correspondingUseElement()->setNeedsStyleRecalc();
346     }
347 }
348 
calculateKeyTimesForCalcModePaced()349 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
350 {
351     ASSERT(calcMode() == CalcModePaced);
352     ASSERT(animationMode() == ValuesAnimation);
353 
354     unsigned valuesCount = m_values.size();
355     ASSERT(valuesCount > 1);
356     Vector<float> keyTimesForPaced;
357     float totalDistance = 0;
358     keyTimesForPaced.append(0);
359     for (unsigned n = 0; n < valuesCount - 1; ++n) {
360         // Distance in any units
361         float distance = calculateDistance(m_values[n], m_values[n + 1]);
362         if (distance < 0)
363             return;
364         totalDistance += distance;
365         keyTimesForPaced.append(distance);
366     }
367     if (!totalDistance)
368         return;
369 
370     // Normalize.
371     for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
372         keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
373     keyTimesForPaced[keyTimesForPaced.size() - 1] = 1;
374 
375     // Use key times calculated based on pacing instead of the user provided ones.
376     m_keyTimes.swap(keyTimesForPaced);
377 }
378 
solveEpsilon(double duration)379 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
380 
calculateKeyTimesIndex(float percent) const381 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
382 {
383     unsigned index;
384     unsigned keyTimesCount = m_keyTimes.size();
385     for (index = 1; index < keyTimesCount; ++index) {
386         if (m_keyTimes[index] >= percent)
387             break;
388     }
389     return --index;
390 }
391 
calculatePercentForSpline(float percent,unsigned splineIndex) const392 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
393 {
394     ASSERT(calcMode() == CalcModeSpline);
395     ASSERT(splineIndex < m_keySplines.size());
396     UnitBezier bezier = m_keySplines[splineIndex];
397     SMILTime duration = simpleDuration();
398     if (!duration.isFinite())
399         duration = 100.0;
400     return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
401 }
402 
calculatePercentFromKeyPoints(float percent) const403 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
404 {
405     ASSERT(!m_keyPoints.isEmpty());
406     ASSERT(calcMode() != CalcModePaced);
407     ASSERT(m_keyTimes.size() > 1);
408     ASSERT(m_keyPoints.size() == m_keyTimes.size());
409 
410     unsigned index = calculateKeyTimesIndex(percent);
411     float fromPercent = m_keyTimes[index];
412     float toPercent = m_keyTimes[index + 1];
413     float fromKeyPoint = m_keyPoints[index];
414     float toKeyPoint = m_keyPoints[index + 1];
415 
416     if (calcMode() == CalcModeDiscrete)
417         return percent == 1 ? toKeyPoint : fromKeyPoint;
418 
419     float keyPointPercent = percent == 1 ? 1 : (percent - fromPercent) / (toPercent - fromPercent);
420 
421     if (calcMode() == CalcModeSpline) {
422         ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
423         keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
424     }
425     return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
426 }
427 
currentValuesFromKeyPoints(float percent,float & effectivePercent,String & from,String & to) const428 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
429 {
430     ASSERT(!m_keyPoints.isEmpty());
431     ASSERT(m_keyPoints.size() == m_keyTimes.size());
432     ASSERT(calcMode() != CalcModePaced);
433     effectivePercent = calculatePercentFromKeyPoints(percent);
434     unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
435     from = m_values[index];
436     to = m_values[index + 1];
437 }
438 
currentValuesForValuesAnimation(float percent,float & effectivePercent,String & from,String & to) const439 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to) const
440 {
441     unsigned valuesCount = m_values.size();
442     ASSERT(m_animationValid);
443     ASSERT(valuesCount > 1);
444 
445     CalcMode calcMode = this->calcMode();
446     if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
447         return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
448 
449     unsigned keyTimesCount = m_keyTimes.size();
450     ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
451     ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
452 
453     unsigned index = calculateKeyTimesIndex(percent);
454     if (calcMode == CalcModeDiscrete) {
455         if (!keyTimesCount)
456             index = percent == 1 ? valuesCount - 1 : static_cast<unsigned>(percent * valuesCount);
457         from = m_values[index];
458         to = m_values[index];
459         effectivePercent = 0;
460         return;
461     }
462 
463     float fromPercent;
464     float toPercent;
465     if (keyTimesCount) {
466         fromPercent = m_keyTimes[index];
467         toPercent = m_keyTimes[index + 1];
468     } else {
469         index = static_cast<unsigned>(percent * (valuesCount - 1));
470         fromPercent =  static_cast<float>(index) / (valuesCount - 1);
471         toPercent =  static_cast<float>(index + 1) / (valuesCount - 1);
472     }
473 
474     if (index == valuesCount - 1)
475         --index;
476     from = m_values[index];
477     to = m_values[index + 1];
478     ASSERT(toPercent > fromPercent);
479     effectivePercent = percent == 1 ? 1 : (percent - fromPercent) / (toPercent - fromPercent);
480 
481     if (calcMode == CalcModeSpline) {
482         ASSERT(m_keySplines.size() == m_values.size() - 1);
483         effectivePercent = calculatePercentForSpline(effectivePercent, index);
484     }
485 }
486 
startedActiveInterval()487 void SVGAnimationElement::startedActiveInterval()
488 {
489     m_animationValid = false;
490 
491     if (!hasValidAttributeType())
492         return;
493 
494     // These validations are appropriate for all animation modes.
495     if (hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
496         return;
497 
498     AnimationMode animationMode = this->animationMode();
499     CalcMode calcMode = this->calcMode();
500     if (calcMode == CalcModeSpline) {
501         unsigned splinesCount = m_keySplines.size() + 1;
502         if ((hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != splinesCount)
503             || (animationMode == ValuesAnimation && m_values.size() != splinesCount))
504             return;
505     }
506 
507     String from = fromValue();
508     String to = toValue();
509     String by = byValue();
510     if (animationMode == NoAnimation)
511         return;
512     if (animationMode == FromToAnimation)
513         m_animationValid = calculateFromAndToValues(from, to);
514     else if (animationMode == ToAnimation) {
515         // For to-animations the from value is the current accumulated value from lower priority animations.
516         // The value is not static and is determined during the animation.
517         m_animationValid = calculateFromAndToValues(String(), to);
518     } else if (animationMode == FromByAnimation)
519         m_animationValid = calculateFromAndByValues(from, by);
520     else if (animationMode == ByAnimation)
521         m_animationValid = calculateFromAndByValues(String(), by);
522     else if (animationMode == ValuesAnimation) {
523         m_animationValid = m_values.size() > 1
524             && (calcMode == CalcModePaced || !hasAttribute(SVGNames::keyTimesAttr) || hasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
525             && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
526             && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
527             && (!hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
528         if (calcMode == CalcModePaced && m_animationValid)
529             calculateKeyTimesForCalcModePaced();
530     } else if (animationMode == PathAnimation)
531         m_animationValid = calcMode == CalcModePaced || !hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
532 }
533 
updateAnimation(float percent,unsigned repeat,SVGSMILElement * resultElement)534 void SVGAnimationElement::updateAnimation(float percent, unsigned repeat, SVGSMILElement* resultElement)
535 {
536     if (!m_animationValid)
537         return;
538 
539     float effectivePercent;
540     CalcMode mode = calcMode();
541     if (animationMode() == ValuesAnimation) {
542         String from;
543         String to;
544         currentValuesForValuesAnimation(percent, effectivePercent, from, to);
545         if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
546             m_animationValid = calculateFromAndToValues(from, to);
547             if (!m_animationValid)
548                 return;
549             m_lastValuesAnimationFrom = from;
550             m_lastValuesAnimationTo = to;
551         }
552     } else if (!m_keyPoints.isEmpty() && mode != CalcModePaced)
553         effectivePercent = calculatePercentFromKeyPoints(percent);
554     else if (m_keyPoints.isEmpty() && mode == CalcModeSpline && m_keyTimes.size() > 1)
555         effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
556     else
557         effectivePercent = percent;
558 
559     calculateAnimatedValue(effectivePercent, repeat, resultElement);
560 }
561 
endedActiveInterval()562 void SVGAnimationElement::endedActiveInterval()
563 {
564 }
565 
566 }
567 #endif // ENABLE(SVG_ANIMATION)
568 
569