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