2011-05-21 Nikolas Zimmermann <nzimmermann@rim.com>
[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 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
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
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
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
139 bool SVGAnimationElement::isSupportedAttribute(const QualifiedName& attrName)
140 {
141     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
142     if (supportedAttributes.isEmpty()) {
143         SVGTests::addSupportedAttributes(supportedAttributes);
144         SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
145         supportedAttributes.add(SVGNames::valuesAttr);
146         supportedAttributes.add(SVGNames::keyTimesAttr);
147         supportedAttributes.add(SVGNames::keyPointsAttr);
148         supportedAttributes.add(SVGNames::keySplinesAttr);
149     }
150     return supportedAttributes.contains(attrName);
151 }
152      
153 void SVGAnimationElement::parseMappedAttribute(Attribute* attr)
154 {
155     if (!isSupportedAttribute(attr->name())) {
156         SVGSMILElement::parseMappedAttribute(attr);
157         return;
158     }
159
160     if (attr->name() == SVGNames::valuesAttr) {
161         attr->value().string().split(';', m_values);
162         return;
163     }
164
165     if (attr->name() == SVGNames::keyTimesAttr) {
166         parseKeyTimes(attr->value(), m_keyTimes, true);
167         return;
168     }
169
170     if (attr->name() == SVGNames::keyPointsAttr) {
171         if (hasTagName(SVGNames::animateMotionTag)) {
172             // This is specified to be an animateMotion attribute only but it is simpler to put it here 
173             // where the other timing calculatations are.
174             parseKeyTimes(attr->value(), m_keyPoints, false);
175         }
176         return;
177     }
178
179     if (attr->name() == SVGNames::keySplinesAttr) {
180         parseKeySplines(attr->value(), m_keySplines);
181         return;
182     }
183
184     if (SVGTests::parseMappedAttribute(attr))
185         return;
186     if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
187         return;
188
189     ASSERT_NOT_REACHED();
190 }
191
192 void SVGAnimationElement::attributeChanged(Attribute* attr, bool preserveDecls)
193 {
194     // Assumptions may not hold after an attribute change.
195     m_animationValid = false;
196     setInactive();
197     SVGSMILElement::attributeChanged(attr, preserveDecls);
198 }
199
200 void SVGAnimationElement::synchronizeProperty(const QualifiedName& attrName)
201 {
202     SVGSMILElement::synchronizeProperty(attrName);
203
204     if (attrName == anyQName()) {
205         synchronizeExternalResourcesRequired();
206         SVGTests::synchronizeProperties(this, attrName);
207         return;
208     }
209
210     if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
211         synchronizeExternalResourcesRequired();
212     else if (SVGTests::isKnownAttribute(attrName))
213         SVGTests::synchronizeProperties(this, attrName);
214 }
215
216 float SVGAnimationElement::getStartTime() const
217 {
218     return narrowPrecisionToFloat(intervalBegin().value());
219 }
220
221 float SVGAnimationElement::getCurrentTime() const
222 {
223     return narrowPrecisionToFloat(elapsed().value());
224 }
225
226 float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const
227 {
228     return narrowPrecisionToFloat(simpleDuration().value());
229 }    
230     
231 void SVGAnimationElement::beginElement()
232 {
233     beginElementAt(0);
234 }
235
236 void SVGAnimationElement::beginElementAt(float offset)
237 {
238     addBeginTime(elapsed() + offset);
239 }
240
241 void SVGAnimationElement::endElement()
242 {
243     endElementAt(0);
244 }
245
246 void SVGAnimationElement::endElementAt(float offset)
247 {
248     addEndTime(elapsed() + offset);
249 }
250
251 SVGAnimationElement::AnimationMode SVGAnimationElement::animationMode() const
252 {
253     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
254     if (hasTagName(SVGNames::setTag))
255         return ToAnimation;
256     if (!animationPath().isEmpty())
257         return PathAnimation;
258     if (hasAttribute(SVGNames::valuesAttr))
259         return ValuesAnimation;
260     if (!toValue().isEmpty())
261         return fromValue().isEmpty() ? ToAnimation : FromToAnimation;
262     if (!byValue().isEmpty())
263         return fromValue().isEmpty() ? ByAnimation : FromByAnimation;
264     return NoAnimation;
265 }
266
267 SVGAnimationElement::CalcMode SVGAnimationElement::calcMode() const
268 {    
269     DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete"));
270     DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear"));
271     DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced"));
272     DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline"));
273     const AtomicString& value = getAttribute(SVGNames::calcModeAttr);
274     if (value == discrete)
275         return CalcModeDiscrete;
276     if (value == linear)
277         return CalcModeLinear;
278     if (value == paced)
279         return CalcModePaced;
280     if (value == spline)
281         return CalcModeSpline;
282     return hasTagName(SVGNames::animateMotionTag) ? CalcModePaced : CalcModeLinear;
283 }
284
285 SVGAnimationElement::AttributeType SVGAnimationElement::attributeType() const
286 {    
287     DEFINE_STATIC_LOCAL(const AtomicString, css, ("CSS"));
288     DEFINE_STATIC_LOCAL(const AtomicString, xml, ("XML"));
289     const AtomicString& value = getAttribute(SVGNames::attributeTypeAttr);
290     if (value == css)
291         return AttributeTypeCSS;
292     if (value == xml)
293         return AttributeTypeXML;
294     return AttributeTypeAuto;
295 }
296
297 String SVGAnimationElement::toValue() const
298 {    
299     return getAttribute(SVGNames::toAttr);
300 }
301
302 String SVGAnimationElement::byValue() const
303 {    
304     return getAttribute(SVGNames::byAttr);
305 }
306
307 String SVGAnimationElement::fromValue() const
308 {    
309     return getAttribute(SVGNames::fromAttr);
310 }
311
312 bool SVGAnimationElement::isAdditive() const
313 {
314     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
315     const AtomicString& value = getAttribute(SVGNames::additiveAttr);
316     return value == sum || animationMode() == ByAnimation;
317 }
318
319 bool SVGAnimationElement::isAccumulated() const
320 {
321     DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum"));
322     const AtomicString& value = getAttribute(SVGNames::accumulateAttr);
323     return value == sum && animationMode() != ToAnimation;
324 }
325
326 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const QualifiedName& attributeName)
327 {
328     ASSERT(targetElement);
329     if (!targetElement->isStyled())
330         return false;
331
332     return SVGStyledElement::isAnimatableCSSProperty(attributeName);
333 }
334
335 void SVGAnimationElement::setTargetAttributeAnimatedValue(const String& value)
336 {
337     if (!hasValidAttributeType())
338         return;
339     SVGElement* targetElement = this->targetElement();
340     QualifiedName attributeName = this->attributeName();
341     if (!targetElement || attributeName == anyQName() || value.isNull())
342         return;
343
344     // We don't want the instance tree to get rebuild. Instances are updated in the loop below.
345     if (targetElement->isStyled())
346         static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(true);
347         
348     bool attributeIsCSSProperty = isTargetAttributeCSSProperty(targetElement, attributeName);
349     // Stop animation, if attributeType is set to CSS by the user, but the attribute itself is not a CSS property.
350     if (!attributeIsCSSProperty && attributeType() == AttributeTypeCSS)
351         return;
352
353     ExceptionCode ec;
354     if (attributeIsCSSProperty) {
355         // FIXME: This should set the override style, not the inline style.
356         // Sadly override styles are not yet implemented.
357         targetElement->style()->setProperty(attributeName.localName(), value, "", ec);
358     } else {
359         // FIXME: This should set the 'presentation' value, not the actual 
360         // attribute value. Whatever that means in practice.
361         targetElement->setAttribute(attributeName, value, ec);
362     }
363     
364     if (targetElement->isStyled())
365         static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(false);
366     
367     // If the target element is used in an <use> instance tree, update that as well.
368     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
369     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
370     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
371         SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
372         if (!shadowTreeElement)
373             continue;
374         if (attributeIsCSSProperty)
375             shadowTreeElement->style()->setProperty(attributeName.localName(), value, "", ec);
376         else
377             shadowTreeElement->setAttribute(attributeName, value, ec);
378         (*it)->correspondingUseElement()->setNeedsStyleRecalc();
379     }
380 }
381     
382 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
383 {
384     ASSERT(calcMode() == CalcModePaced);
385     ASSERT(animationMode() == ValuesAnimation);
386
387     unsigned valuesCount = m_values.size();
388     ASSERT(valuesCount > 1);
389     Vector<float> keyTimesForPaced;
390     float totalDistance = 0;
391     keyTimesForPaced.append(0);
392     for (unsigned n = 0; n < valuesCount - 1; ++n) {
393         // Distance in any units
394         float distance = calculateDistance(m_values[n], m_values[n + 1]);
395         if (distance < 0)
396             return;
397         totalDistance += distance;
398         keyTimesForPaced.append(distance);
399     }
400     if (!totalDistance)
401         return;
402
403     // Normalize.
404     for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
405         keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
406     keyTimesForPaced[keyTimesForPaced.size() - 1] = 1;
407
408     // Use key times calculated based on pacing instead of the user provided ones.
409     m_keyTimes.swap(keyTimesForPaced);
410 }
411
412 static inline double solveEpsilon(double duration) { return 1 / (200 * duration); }
413
414 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
415 {
416     unsigned index;
417     unsigned keyTimesCount = m_keyTimes.size();
418     for (index = 1; index < 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() == CalcModeSpline);
428     ASSERT(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() != CalcModePaced);
440     ASSERT(m_keyTimes.size() > 1);
441     ASSERT(m_keyPoints.size() == m_keyTimes.size());
442
443     unsigned index = calculateKeyTimesIndex(percent);
444     float fromPercent = m_keyTimes[index];
445     float toPercent = m_keyTimes[index + 1];
446     float fromKeyPoint = m_keyPoints[index];
447     float toKeyPoint = m_keyPoints[index + 1];
448     
449     if (calcMode() == CalcModeDiscrete)
450         return percent == 1 ? toKeyPoint : fromKeyPoint;
451     
452     float keyPointPercent = percent == 1 ? 1 : (percent - fromPercent) / (toPercent - fromPercent);
453     
454     if (calcMode() == CalcModeSpline) {
455         ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
456         keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
457     }
458     return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
459 }
460     
461 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
462 {
463     ASSERT(!m_keyPoints.isEmpty());
464     ASSERT(m_keyPoints.size() == m_keyTimes.size());
465     ASSERT(calcMode() != CalcModePaced);
466     effectivePercent = calculatePercentFromKeyPoints(percent);
467     unsigned index = effectivePercent == 1 ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
468     from = m_values[index];
469     to = m_values[index + 1];
470 }
471     
472 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to) const
473 {
474     unsigned valuesCount = m_values.size();
475     ASSERT(m_animationValid);
476     ASSERT(valuesCount > 1);
477     
478     CalcMode calcMode = this->calcMode();
479     if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
480         return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
481     
482     unsigned keyTimesCount = m_keyTimes.size();
483     ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
484     ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
485
486     unsigned index = calculateKeyTimesIndex(percent);
487     if (calcMode == CalcModeDiscrete) {
488         if (!keyTimesCount) 
489             index = percent == 1 ? valuesCount - 1 : static_cast<unsigned>(percent * valuesCount);
490         from = m_values[index];
491         to = m_values[index];
492         effectivePercent = 0;
493         return;
494     }
495     
496     float fromPercent;
497     float toPercent;
498     if (keyTimesCount) {
499         fromPercent = m_keyTimes[index];
500         toPercent = m_keyTimes[index + 1];
501     } else {        
502         index = static_cast<unsigned>(percent * (valuesCount - 1));
503         fromPercent =  static_cast<float>(index) / (valuesCount - 1);
504         toPercent =  static_cast<float>(index + 1) / (valuesCount - 1);
505     }
506     
507     if (index == valuesCount - 1)
508         --index;
509     from = m_values[index];
510     to = m_values[index + 1];
511     ASSERT(toPercent > fromPercent);
512     effectivePercent = percent == 1 ? 1 : (percent - fromPercent) / (toPercent - fromPercent);
513     
514     if (calcMode == CalcModeSpline) {
515         ASSERT(m_keySplines.size() == m_values.size() - 1);
516         effectivePercent = calculatePercentForSpline(effectivePercent, index);
517     }
518 }
519     
520 void SVGAnimationElement::startedActiveInterval()
521 {
522     m_animationValid = false;
523
524     if (!hasValidAttributeType())
525         return;
526
527     // These validations are appropriate for all animation modes.
528     if (hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
529         return;
530
531     AnimationMode animationMode = this->animationMode();
532     CalcMode calcMode = this->calcMode();
533     if (calcMode == CalcModeSpline) {
534         unsigned splinesCount = m_keySplines.size() + 1;
535         if ((hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != splinesCount)
536             || (animationMode == ValuesAnimation && m_values.size() != splinesCount))
537             return;
538     }
539
540     String from = fromValue();
541     String to = toValue();
542     String by = byValue();
543     if (animationMode == NoAnimation)
544         return;
545     if (animationMode == FromToAnimation)
546         m_animationValid = calculateFromAndToValues(from, to);
547     else if (animationMode == ToAnimation) {
548         // For to-animations the from value is the current accumulated value from lower priority animations.
549         // The value is not static and is determined during the animation.
550         m_animationValid = calculateFromAndToValues(String(), to);
551     } else if (animationMode == FromByAnimation)
552         m_animationValid = calculateFromAndByValues(from, by);
553     else if (animationMode == ByAnimation)
554         m_animationValid = calculateFromAndByValues(String(), by);
555     else if (animationMode == ValuesAnimation) {
556         m_animationValid = m_values.size() > 1
557             && (calcMode == CalcModePaced || !hasAttribute(SVGNames::keyTimesAttr) || hasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
558             && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
559             && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
560             && (!hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
561         if (calcMode == CalcModePaced && m_animationValid)
562             calculateKeyTimesForCalcModePaced();
563     } else if (animationMode == PathAnimation)
564         m_animationValid = calcMode == CalcModePaced || !hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
565 }
566     
567 void SVGAnimationElement::updateAnimation(float percent, unsigned repeat, SVGSMILElement* resultElement)
568 {    
569     if (!m_animationValid)
570         return;
571     
572     float effectivePercent;
573     CalcMode mode = calcMode();
574     if (animationMode() == ValuesAnimation) {
575         String from;
576         String to;
577         currentValuesForValuesAnimation(percent, effectivePercent, from, to);
578         if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
579             m_animationValid = calculateFromAndToValues(from, to);
580             if (!m_animationValid)
581                 return;
582             m_lastValuesAnimationFrom = from;
583             m_lastValuesAnimationTo = to;
584         }
585     } else if (!m_keyPoints.isEmpty() && mode != CalcModePaced)
586         effectivePercent = calculatePercentFromKeyPoints(percent);
587     else if (m_keyPoints.isEmpty() && mode == CalcModeSpline && m_keyTimes.size() > 1)
588         effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
589     else
590         effectivePercent = percent;
591
592     calculateAnimatedValue(effectivePercent, repeat, resultElement);
593 }
594
595 void SVGAnimationElement::endedActiveInterval()
596 {
597 }
598
599 }
600 #endif // ENABLE(SVG_ANIMATION)
601