2011-02-15 Dirk Schulze <krit@webkit.org>
[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.f)
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.0f;
102         if (!parseNumber(cur, end, posA)) {
103             result.clear();
104             return;
105         }
106
107         float posB = 0.0f;
108         if (!parseNumber(cur, end, posB)) {
109             result.clear();
110             return;
111         }
112
113         float posC = 0.0f;
114         if (!parseNumber(cur, end, posC)) {
115             result.clear();
116             return;
117         }
118
119         float posD = 0.0f;
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 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
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
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
183 float SVGAnimationElement::getStartTime() const
184 {
185     return narrowPrecisionToFloat(intervalBegin().value());
186 }
187
188 float SVGAnimationElement::getCurrentTime() const
189 {
190     return narrowPrecisionToFloat(elapsed().value());
191 }
192
193 float SVGAnimationElement::getSimpleDuration(ExceptionCode&) const
194 {
195     return narrowPrecisionToFloat(simpleDuration().value());
196 }    
197     
198 void SVGAnimationElement::beginElement()
199 {
200     beginElementAt(0);
201 }
202
203 void SVGAnimationElement::beginElementAt(float offset)
204 {
205     addBeginTime(elapsed() + offset);
206 }
207
208 void SVGAnimationElement::endElement()
209 {
210     endElementAt(0);
211 }
212
213 void SVGAnimationElement::endElementAt(float offset)
214 {
215     addEndTime(elapsed() + offset);
216 }
217
218 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
234 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
252 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
264 String SVGAnimationElement::toValue() const
265 {    
266     return getAttribute(SVGNames::toAttr);
267 }
268
269 String SVGAnimationElement::byValue() const
270 {    
271     return getAttribute(SVGNames::byAttr);
272 }
273
274 String SVGAnimationElement::fromValue() const
275 {    
276     return getAttribute(SVGNames::fromAttr);
277 }
278
279 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
286 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
293 bool SVGAnimationElement::hasValidTarget() const
294 {
295     return targetElement();
296 }
297
298 bool SVGAnimationElement::isTargetAttributeCSSProperty(SVGElement* targetElement, const String& attributeName)
299 {
300     ASSERT(targetElement);
301     if (!targetElement->isStyled())
302         return false;
303
304     return SVGStyledElement::isAnimatableCSSProperty(QualifiedName(nullAtom, attributeName, nullAtom));
305 }
306
307 void SVGAnimationElement::setTargetAttributeAnimatedValue(const String& value)
308 {
309     if (!hasValidTarget())
310         return;
311     SVGElement* targetElement = this->targetElement();
312     String attributeName = this->attributeName();
313     if (!targetElement || attributeName.isEmpty() || value.isNull())
314         return;
315
316     // We don't want the instance tree to get rebuild. Instances are updated in the loop below.
317     if (targetElement->isStyled())
318         static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(true);
319         
320     bool attributeIsCSSProperty = isTargetAttributeCSSProperty(targetElement, attributeName);
321     // Stop animation, if attributeType is set to CSS by the user, but the attribute itself is not a CSS property.
322     if (!attributeIsCSSProperty && attributeType() == AttributeTypeCSS)
323         return;
324
325     ExceptionCode ec;
326     if (attributeIsCSSProperty) {
327         // FIXME: This should set the override style, not the inline style.
328         // Sadly override styles are not yet implemented.
329         targetElement->style()->setProperty(attributeName, value, "", ec);
330     } else {
331         // FIXME: This should set the 'presentation' value, not the actual 
332         // attribute value. Whatever that means in practice.
333         targetElement->setAttribute(attributeName, value, ec);
334     }
335     
336     if (targetElement->isStyled())
337         static_cast<SVGStyledElement*>(targetElement)->setInstanceUpdatesBlocked(false);
338     
339     // If the target element is used in an <use> instance tree, update that as well.
340     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
341     const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
342     for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
343         SVGElement* shadowTreeElement = (*it)->shadowTreeElement();
344         if (!shadowTreeElement)
345             continue;
346         if (attributeIsCSSProperty)
347             shadowTreeElement->style()->setProperty(attributeName, value, "", ec);
348         else
349             shadowTreeElement->setAttribute(attributeName, value, ec);
350         (*it)->correspondingUseElement()->setNeedsStyleRecalc();
351     }
352 }
353     
354 void SVGAnimationElement::calculateKeyTimesForCalcModePaced()
355 {
356     ASSERT(calcMode() == CalcModePaced);
357     ASSERT(animationMode() == ValuesAnimation);
358
359     unsigned valuesCount = m_values.size();
360     ASSERT(valuesCount > 1);
361     Vector<float> keyTimesForPaced;
362     float totalDistance = 0;
363     keyTimesForPaced.append(0);
364     for (unsigned n = 0; n < valuesCount - 1; ++n) {
365         // Distance in any units
366         float distance = calculateDistance(m_values[n], m_values[n + 1]);
367         if (distance < 0)
368             return;
369         totalDistance += distance;
370         keyTimesForPaced.append(distance);
371     }
372     if (!totalDistance)
373         return;
374
375     // Normalize.
376     for (unsigned n = 1; n < keyTimesForPaced.size() - 1; ++n)
377         keyTimesForPaced[n] = keyTimesForPaced[n - 1] + keyTimesForPaced[n] / totalDistance;
378     keyTimesForPaced[keyTimesForPaced.size() - 1] = 1.f;
379
380     // Use key times calculated based on pacing instead of the user provided ones.
381     m_keyTimes.swap(keyTimesForPaced);
382 }
383
384 static inline double solveEpsilon(double duration) { return 1. / (200. * duration); }
385
386 unsigned SVGAnimationElement::calculateKeyTimesIndex(float percent) const
387 {
388     unsigned index;
389     unsigned keyTimesCount = m_keyTimes.size();
390     for (index = 1; index < keyTimesCount; ++index) {
391         if (m_keyTimes[index] >= percent)
392             break;
393     }
394     return --index;
395 }
396
397 float SVGAnimationElement::calculatePercentForSpline(float percent, unsigned splineIndex) const
398 {
399     ASSERT(calcMode() == CalcModeSpline);
400     ASSERT(splineIndex < m_keySplines.size());
401     UnitBezier bezier = m_keySplines[splineIndex];
402     SMILTime duration = simpleDuration();
403     if (!duration.isFinite())
404         duration = 100.0;
405     return narrowPrecisionToFloat(bezier.solve(percent, solveEpsilon(duration.value())));
406 }
407
408 float SVGAnimationElement::calculatePercentFromKeyPoints(float percent) const
409 {
410     ASSERT(!m_keyPoints.isEmpty());
411     ASSERT(calcMode() != CalcModePaced);
412     ASSERT(m_keyTimes.size() > 1);
413     ASSERT(m_keyPoints.size() == m_keyTimes.size());
414
415     unsigned index = calculateKeyTimesIndex(percent);
416     float fromPercent = m_keyTimes[index];
417     float toPercent = m_keyTimes[index + 1];
418     float fromKeyPoint = m_keyPoints[index];
419     float toKeyPoint = m_keyPoints[index + 1];
420     
421     if (calcMode() == CalcModeDiscrete)
422         return percent == 1.0f ? toKeyPoint : fromKeyPoint;
423     
424     float keyPointPercent = percent == 1.0f ? 1.0f : (percent - fromPercent) / (toPercent - fromPercent);
425     
426     if (calcMode() == CalcModeSpline) {
427         ASSERT(m_keySplines.size() == m_keyPoints.size() - 1);
428         keyPointPercent = calculatePercentForSpline(keyPointPercent, index);
429     }
430     return (toKeyPoint - fromKeyPoint) * keyPointPercent + fromKeyPoint;
431 }
432     
433 void SVGAnimationElement::currentValuesFromKeyPoints(float percent, float& effectivePercent, String& from, String& to) const
434 {
435     ASSERT(!m_keyPoints.isEmpty());
436     ASSERT(m_keyPoints.size() == m_keyTimes.size());
437     ASSERT(calcMode() != CalcModePaced);
438     effectivePercent = calculatePercentFromKeyPoints(percent);
439     unsigned index = effectivePercent == 1.0f ? m_values.size() - 2 : static_cast<unsigned>(effectivePercent * (m_values.size() - 1));
440     from = m_values[index];
441     to = m_values[index + 1];
442 }
443     
444 void SVGAnimationElement::currentValuesForValuesAnimation(float percent, float& effectivePercent, String& from, String& to) const
445 {
446     unsigned valuesCount = m_values.size();
447     ASSERT(m_animationValid);
448     ASSERT(valuesCount > 1);
449     
450     CalcMode calcMode = this->calcMode();
451     if (!m_keyPoints.isEmpty() && calcMode != CalcModePaced)
452         return currentValuesFromKeyPoints(percent, effectivePercent, from, to);
453     
454     unsigned keyTimesCount = m_keyTimes.size();
455     ASSERT(!keyTimesCount || valuesCount == keyTimesCount);
456     ASSERT(!keyTimesCount || (keyTimesCount > 1 && !m_keyTimes[0]));
457
458     unsigned index = calculateKeyTimesIndex(percent);
459     if (calcMode == CalcModeDiscrete) {
460         if (!keyTimesCount) 
461             index = percent == 1.0f ? valuesCount - 1 : static_cast<unsigned>(percent * valuesCount);
462         from = m_values[index];
463         to = m_values[index];
464         effectivePercent = 0.0f;
465         return;
466     }
467     
468     float fromPercent;
469     float toPercent;
470     if (keyTimesCount) {
471         fromPercent = m_keyTimes[index];
472         toPercent = m_keyTimes[index + 1];
473     } else {        
474         index = static_cast<unsigned>(percent * (valuesCount - 1));
475         fromPercent =  static_cast<float>(index) / (valuesCount - 1);
476         toPercent =  static_cast<float>(index + 1) / (valuesCount - 1);
477     }
478     
479     if (index == valuesCount - 1)
480         --index;
481     from = m_values[index];
482     to = m_values[index + 1];
483     ASSERT(toPercent > fromPercent);
484     effectivePercent = percent == 1.0f ? 1.0f : (percent - fromPercent) / (toPercent - fromPercent);
485     
486     if (calcMode == CalcModeSpline) {
487         ASSERT(m_keySplines.size() == m_values.size() - 1);
488         effectivePercent = calculatePercentForSpline(effectivePercent, index);
489     }
490 }
491     
492 void SVGAnimationElement::startedActiveInterval()
493 {
494     m_animationValid = false;
495
496     if (!hasValidTarget())
497         return;
498
499     // These validations are appropriate for all animation modes.
500     if (hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != m_keyTimes.size())
501         return;
502
503     AnimationMode animationMode = this->animationMode();
504     CalcMode calcMode = this->calcMode();
505     if (calcMode == CalcModeSpline) {
506         unsigned splinesCount = m_keySplines.size() + 1;
507         if ((hasAttribute(SVGNames::keyPointsAttr) && m_keyPoints.size() != splinesCount)
508             || (animationMode == ValuesAnimation && m_values.size() != splinesCount))
509             return;
510     }
511
512     String from = fromValue();
513     String to = toValue();
514     String by = byValue();
515     if (animationMode == NoAnimation)
516         return;
517     if (animationMode == FromToAnimation)
518         m_animationValid = calculateFromAndToValues(from, to);
519     else if (animationMode == ToAnimation) {
520         // For to-animations the from value is the current accumulated value from lower priority animations.
521         // The value is not static and is determined during the animation.
522         m_animationValid = calculateFromAndToValues(String(), to);
523     } else if (animationMode == FromByAnimation)
524         m_animationValid = calculateFromAndByValues(from, by);
525     else if (animationMode == ByAnimation)
526         m_animationValid = calculateFromAndByValues(String(), by);
527     else if (animationMode == ValuesAnimation) {
528         m_animationValid = m_values.size() > 1
529             && (calcMode == CalcModePaced || !hasAttribute(SVGNames::keyTimesAttr) || hasAttribute(SVGNames::keyPointsAttr) || (m_values.size() == m_keyTimes.size()))
530             && (calcMode == CalcModeDiscrete || !m_keyTimes.size() || m_keyTimes.last() == 1)
531             && (calcMode != CalcModeSpline || ((m_keySplines.size() && (m_keySplines.size() == m_values.size() - 1)) || m_keySplines.size() == m_keyPoints.size() - 1))
532             && (!hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size()));
533         if (calcMode == CalcModePaced && m_animationValid)
534             calculateKeyTimesForCalcModePaced();
535     } else if (animationMode == PathAnimation)
536         m_animationValid = calcMode == CalcModePaced || !hasAttribute(SVGNames::keyPointsAttr) || (m_keyTimes.size() > 1 && m_keyTimes.size() == m_keyPoints.size());
537 }
538     
539 void SVGAnimationElement::updateAnimation(float percent, unsigned repeat, SVGSMILElement* resultElement)
540 {    
541     if (!m_animationValid)
542         return;
543     
544     float effectivePercent;
545     CalcMode mode = calcMode();
546     if (animationMode() == ValuesAnimation) {
547         String from;
548         String to;
549         currentValuesForValuesAnimation(percent, effectivePercent, from, to);
550         if (from != m_lastValuesAnimationFrom || to != m_lastValuesAnimationTo) {
551             m_animationValid = calculateFromAndToValues(from, to);
552             if (!m_animationValid)
553                 return;
554             m_lastValuesAnimationFrom = from;
555             m_lastValuesAnimationTo = to;
556         }
557     } else if (!m_keyPoints.isEmpty() && mode != CalcModePaced)
558         effectivePercent = calculatePercentFromKeyPoints(percent);
559     else if (m_keyPoints.isEmpty() && mode == CalcModeSpline && m_keyTimes.size() > 1)
560         effectivePercent = calculatePercentForSpline(percent, calculateKeyTimesIndex(percent));
561     else
562         effectivePercent = percent;
563
564     calculateAnimatedValue(effectivePercent, repeat, resultElement);
565 }
566
567 void SVGAnimationElement::endedActiveInterval()
568 {
569 }
570
571 }
572
573 // vim:ts=4:noet
574 #endif // ENABLE(SVG_ANIMATION)
575