2011-05-26 Rob Buis <rbuis@rim.com>
[WebKit-https.git] / Source / WebCore / svg / animation / SVGSMILElement.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #if ENABLE(SVG_ANIMATION)
29 #include "SVGSMILElement.h"
30
31 #include "Attribute.h"
32 #include "CSSPropertyNames.h"
33 #include "Document.h"
34 #include "Event.h"
35 #include "EventListener.h"
36 #include "FloatConversion.h"
37 #include "FrameView.h"
38 #include "HTMLNames.h"
39 #include "SMILTimeContainer.h"
40 #include "SVGDocumentExtensions.h"
41 #include "SVGNames.h"
42 #include "SVGParserUtilities.h"
43 #include "SVGSVGElement.h"
44 #include "SVGURIReference.h"
45 #include "XLinkNames.h"
46 #include <wtf/MathExtras.h>
47 #include <wtf/StdLibExtras.h>
48 #include <wtf/Vector.h>
49
50 using namespace std;
51
52 namespace WebCore {
53     
54 // This is used for duration type time values that can't be negative.
55 static const double invalidCachedTime = -1.;
56     
57 class ConditionEventListener : public EventListener {
58 public:
59     static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
60     {
61         return adoptRef(new ConditionEventListener(animation, condition));
62     }
63
64     static const ConditionEventListener* cast(const EventListener* listener)
65     {
66         return listener->type() == ConditionEventListenerType
67             ? static_cast<const ConditionEventListener*>(listener)
68             : 0;
69     }
70
71     virtual bool operator==(const EventListener& other);
72     
73     void disconnectAnimation()
74     {
75         m_animation = 0;
76     }
77
78 private:
79     ConditionEventListener(SVGSMILElement* animation, SVGSMILElement::Condition* condition) 
80         : EventListener(ConditionEventListenerType)
81         , m_animation(animation)
82         , m_condition(condition) 
83     {
84     }
85
86     virtual void handleEvent(ScriptExecutionContext*, Event*);
87
88     SVGSMILElement* m_animation;
89     SVGSMILElement::Condition* m_condition;
90 };
91
92 bool ConditionEventListener::operator==(const EventListener& listener)
93 {
94     if (const ConditionEventListener* conditionEventListener = ConditionEventListener::cast(&listener))
95         return m_animation == conditionEventListener->m_animation && m_condition == conditionEventListener->m_condition;
96     return false;
97 }
98
99 void ConditionEventListener::handleEvent(ScriptExecutionContext*, Event* event) 
100 {
101     if (!m_animation)
102         return;
103     m_animation->handleConditionEvent(event, m_condition);
104 }
105
106 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeats)
107     : m_type(type)
108     , m_beginOrEnd(beginOrEnd)
109     , m_baseID(baseID)
110     , m_name(name)
111     , m_offset(offset)
112     , m_repeats(repeats) 
113 {
114 }
115     
116 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document* doc)
117     : SVGElement(tagName, doc)
118     , m_attributeName(anyQName())
119     , m_targetElement(0)
120     , m_conditionsConnected(false)
121     , m_hasEndEventConditions(false)
122     , m_intervalBegin(SMILTime::unresolved())
123     , m_intervalEnd(SMILTime::unresolved())
124     , m_previousIntervalBegin(SMILTime::unresolved())
125     , m_isWaitingForFirstInterval(true)
126     , m_activeState(Inactive)
127     , m_lastPercent(0)
128     , m_lastRepeat(0)
129     , m_nextProgressTime(0)
130     , m_documentOrderIndex(0)
131     , m_cachedDur(invalidCachedTime)
132     , m_cachedRepeatDur(invalidCachedTime)
133     , m_cachedRepeatCount(invalidCachedTime)
134     , m_cachedMin(invalidCachedTime)
135     , m_cachedMax(invalidCachedTime)
136 {
137 }
138
139 SVGSMILElement::~SVGSMILElement()
140 {
141     disconnectConditions();
142     if (m_timeContainer)
143         m_timeContainer->unschedule(this);
144     if (m_targetElement)
145         document()->accessSVGExtensions()->removeAnimationElementFromTarget(this, m_targetElement);
146 }
147
148 static inline QualifiedName constructQualifiedName(const SVGElement* svgElement, const String& attributeName)
149 {
150     ASSERT(svgElement);
151     if (attributeName.isEmpty())
152         return anyQName();
153     if (!attributeName.contains(':'))
154         return QualifiedName(nullAtom, attributeName, nullAtom);
155     
156     String prefix;
157     String localName;
158     ExceptionCode ec = 0;
159     if (!Document::parseQualifiedName(attributeName, prefix, localName, ec))
160         return anyQName();
161     ASSERT(!ec);
162     
163     String namespaceURI = svgElement->lookupNamespaceURI(prefix);    
164     if (namespaceURI.isEmpty())
165         return anyQName();
166     
167     return QualifiedName(nullAtom, localName, namespaceURI);
168 }
169     
170 void SVGSMILElement::insertedIntoDocument()
171 {
172     SVGElement::insertedIntoDocument();
173 #ifndef NDEBUG
174     // Verify we are not in <use> instance tree.
175     for (ContainerNode* n = this; n; n = n->parentNode())
176         ASSERT(!n->isSVGShadowRoot());
177 #endif
178     m_attributeName = constructQualifiedName(this, getAttribute(SVGNames::attributeNameAttr));
179     SVGSVGElement* owner = ownerSVGElement();
180     if (!owner)
181         return;
182     m_timeContainer = owner->timeContainer();
183     ASSERT(m_timeContainer);
184     m_timeContainer->setDocumentOrderIndexesDirty();
185
186     // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
187     if (!hasAttribute(SVGNames::beginAttr))
188         m_beginTimes.append(0);
189
190     if (m_isWaitingForFirstInterval) {
191         resolveFirstInterval();
192         reschedule();
193     }
194 }
195
196 void SVGSMILElement::removedFromDocument()
197 {
198     m_attributeName = anyQName();
199     if (m_timeContainer) {
200         m_timeContainer->unschedule(this);
201         m_timeContainer = 0;
202     }
203     if (m_targetElement) {
204         document()->accessSVGExtensions()->removeAnimationElementFromTarget(this, m_targetElement);
205         m_targetElement = 0;
206     }
207     // Calling disconnectConditions() may kill us if there are syncbase conditions.
208     // OK, but we don't want to die inside the call.
209     RefPtr<SVGSMILElement> keepAlive(this);
210     disconnectConditions();
211     SVGElement::removedFromDocument();
212 }
213    
214 SMILTime SVGSMILElement::parseOffsetValue(const String& data)
215 {
216     bool ok;
217     double result = 0;
218     String parse = data.stripWhiteSpace();
219     if (parse.endsWith("h"))
220         result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
221     else if (parse.endsWith("min"))
222         result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
223     else if (parse.endsWith("ms"))
224         result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
225     else if (parse.endsWith("s"))
226         result = parse.left(parse.length() - 1).toDouble(&ok);
227     else
228         result = parse.toDouble(&ok);
229     if (!ok)
230         return SMILTime::unresolved();
231     return result;
232 }
233     
234 SMILTime SVGSMILElement::parseClockValue(const String& data)
235 {
236     if (data.isNull())
237         return SMILTime::unresolved();
238     
239     String parse = data.stripWhiteSpace();
240
241     DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite"));
242     if (parse == indefiniteValue)
243         return SMILTime::indefinite();
244
245     double result = 0;
246     bool ok;
247     size_t doublePointOne = parse.find(':');
248     size_t doublePointTwo = parse.find(':', doublePointOne + 1);
249     if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) { 
250         result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
251         if (!ok)
252             return SMILTime::unresolved();
253         result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
254         if (!ok)
255             return SMILTime::unresolved();
256         result += parse.substring(6).toDouble(&ok);
257     } else if (doublePointOne == 2 && doublePointTwo == notFound && parse.length() >= 5) { 
258         result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
259         if (!ok)
260             return SMILTime::unresolved();
261         result += parse.substring(3).toDouble(&ok);
262     } else
263         return parseOffsetValue(parse);
264
265     if (!ok)
266         return SMILTime::unresolved();
267     return result;
268 }
269     
270 static void sortTimeList(Vector<SMILTime>& timeList)
271 {
272     std::sort(timeList.begin(), timeList.end());
273 }
274     
275 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd)
276 {
277     String parseString = value.stripWhiteSpace();
278     
279     double sign = 1.;
280     bool ok;
281     size_t pos = parseString.find('+');
282     if (pos == notFound) {
283         pos = parseString.find('-');
284         if (pos != notFound)
285             sign = -1.;
286     }
287     String conditionString;
288     SMILTime offset = 0;
289     if (pos == notFound)
290         conditionString = parseString;
291     else {
292         conditionString = parseString.left(pos).stripWhiteSpace();
293         String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
294         offset = parseOffsetValue(offsetString);
295         if (offset.isUnresolved())
296             return false;
297         offset = offset * sign;
298     }
299     if (conditionString.isEmpty())
300         return false;
301     pos = conditionString.find('.');
302     
303     String baseID;
304     String nameString;
305     if (pos == notFound)
306         nameString = conditionString;
307     else {
308         baseID = conditionString.left(pos);
309         nameString = conditionString.substring(pos + 1);
310     }
311     if (nameString.isEmpty())
312         return false;
313
314     Condition::Type type;
315     int repeats = -1;
316     if (nameString.startsWith("repeat(") && nameString.endsWith(")")) {
317         // FIXME: For repeat events we just need to add the data carrying TimeEvent class and 
318         // fire the events at appropiate times.
319         repeats = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
320         if (!ok)
321             return false;
322         nameString = "repeat";
323         type = Condition::EventBase;
324     } else if (nameString == "begin" || nameString == "end") {
325         if (baseID.isEmpty())
326             return false;
327         type = Condition::Syncbase;
328     } else if (nameString.startsWith("accesskey(")) {
329         // FIXME: accesskey() support.
330         type = Condition::AccessKey;
331     } else
332         type = Condition::EventBase;
333     
334     m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeats));
335
336     if (type == Condition::EventBase && beginOrEnd == End)
337         m_hasEndEventConditions = true;
338
339     return true;
340 }
341
342 bool SVGSMILElement::isSMILElement(Node* node)
343 {
344     if (!node)
345         return false;
346     return node->hasTagName(SVGNames::setTag) || node->hasTagName(SVGNames::animateTag) || node->hasTagName(SVGNames::animateMotionTag)
347             || node->hasTagName(SVGNames::animateTransformTag) || node->hasTagName(SVGNames::animateColorTag);
348 }
349     
350 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd)
351 {
352     Vector<SMILTime>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
353     if (beginOrEnd == End)
354         m_hasEndEventConditions = false;
355     HashSet<double> existing;
356     for (unsigned n = 0; n < timeList.size(); ++n)
357         existing.add(timeList[n].value());
358     Vector<String> splitString;
359     parseString.split(';', splitString);
360     for (unsigned n = 0; n < splitString.size(); ++n) {
361         SMILTime value = parseClockValue(splitString[n]);
362         if (value.isUnresolved())
363             parseCondition(splitString[n], beginOrEnd);
364         else if (!existing.contains(value.value()))
365             timeList.append(value);
366     }
367     sortTimeList(timeList);
368 }
369
370 void SVGSMILElement::parseMappedAttribute(Attribute* attr)
371 {
372     if (attr->name() == SVGNames::beginAttr) {
373         if (!m_conditions.isEmpty()) {
374             disconnectConditions();
375             m_conditions.clear();
376             parseBeginOrEnd(getAttribute(SVGNames::endAttr), End);
377         }
378         parseBeginOrEnd(attr->value().string(), Begin);
379         if (inDocument())
380             connectConditions();
381     } else if (attr->name() == SVGNames::endAttr) {
382         if (!m_conditions.isEmpty()) {
383             disconnectConditions();
384             m_conditions.clear();
385             parseBeginOrEnd(getAttribute(SVGNames::beginAttr), Begin);
386         }
387         parseBeginOrEnd(attr->value().string(), End);
388         if (inDocument())
389             connectConditions();
390     } else
391         SVGElement::parseMappedAttribute(attr);
392 }
393
394 void SVGSMILElement::attributeChanged(Attribute* attr, bool preserveDecls)
395 {
396     SVGElement::attributeChanged(attr, preserveDecls);
397     
398     const QualifiedName& attrName = attr->name();
399     if (attrName == SVGNames::durAttr)
400         m_cachedDur = invalidCachedTime;
401     else if (attrName == SVGNames::repeatDurAttr)
402         m_cachedRepeatDur = invalidCachedTime;
403     else if (attrName == SVGNames::repeatCountAttr)
404         m_cachedRepeatCount = invalidCachedTime;
405     else if (attrName == SVGNames::minAttr)
406         m_cachedMin = invalidCachedTime;
407     else if (attrName == SVGNames::maxAttr)
408         m_cachedMax = invalidCachedTime;
409     else if (attrName == SVGNames::attributeNameAttr) {
410         if (inDocument())
411             m_attributeName = constructQualifiedName(this, attr->value());
412     }
413
414     if (inDocument()) {
415         if (attrName == SVGNames::beginAttr)
416             beginListChanged();
417         else if (attrName == SVGNames::endAttr)
418             endListChanged();
419     }
420 }
421
422 inline Element* SVGSMILElement::eventBaseFor(const Condition& condition) const
423 {
424     return condition.m_baseID.isEmpty() ? targetElement() : treeScope()->getElementById(condition.m_baseID);
425 }
426
427 void SVGSMILElement::connectConditions()
428 {
429     if (m_conditionsConnected)
430         disconnectConditions();
431     m_conditionsConnected = true;
432     for (unsigned n = 0; n < m_conditions.size(); ++n) {
433         Condition& condition = m_conditions[n];
434         if (condition.m_type == Condition::EventBase) {
435             ASSERT(!condition.m_syncbase);
436             Element* eventBase = eventBaseFor(condition);
437             if (!eventBase)
438                 continue;
439             ASSERT(!condition.m_eventListener);
440             condition.m_eventListener = ConditionEventListener::create(this, &condition);
441             eventBase->addEventListener(condition.m_name, condition.m_eventListener, false);
442         } else if (condition.m_type == Condition::Syncbase) {
443             ASSERT(!condition.m_baseID.isEmpty());
444             condition.m_syncbase = treeScope()->getElementById(condition.m_baseID);
445             if (!isSMILElement(condition.m_syncbase.get())) {
446                 condition.m_syncbase = 0;
447                 continue;
448             }
449             SVGSMILElement* syncbase = static_cast<SVGSMILElement*>(condition.m_syncbase.get());
450             syncbase->addTimeDependent(this);
451         }
452     }
453 }
454
455 void SVGSMILElement::disconnectConditions()
456 {
457     if (!m_conditionsConnected)
458         return;
459     m_conditionsConnected = false;
460     for (unsigned n = 0; n < m_conditions.size(); ++n) {
461         Condition& condition = m_conditions[n];
462         if (condition.m_type == Condition::EventBase) {
463             ASSERT(!condition.m_syncbase);
464             if (!condition.m_eventListener)
465                 continue;
466             // Note: It's a memory optimization to try to remove our condition
467             // event listener, but it's not guaranteed to work, since we have
468             // no guarantee that eventBaseFor() will be able to find our condition's
469             // original eventBase. So, we also have to disconnect ourselves from
470             // our condition event listener, in case it later fires.
471             Element* eventBase = eventBaseFor(condition);
472             if (eventBase)
473                 eventBase->removeEventListener(condition.m_name, condition.m_eventListener.get(), false);
474             condition.m_eventListener->disconnectAnimation();
475             condition.m_eventListener = 0;
476         } else if (condition.m_type == Condition::Syncbase) {
477             if (condition.m_syncbase) {
478                 ASSERT(isSMILElement(condition.m_syncbase.get()));
479                 static_cast<SVGSMILElement*>(condition.m_syncbase.get())->removeTimeDependent(this);
480             }
481         }
482         condition.m_syncbase = 0;
483     }
484 }
485
486 void SVGSMILElement::reschedule()
487 {
488     if (m_timeContainer)
489         m_timeContainer->schedule(this);
490 }
491
492 SVGElement* SVGSMILElement::targetElement() const
493 {
494     if (m_targetElement)
495         return m_targetElement;
496
497     String href = xlinkHref();
498     ContainerNode* target = href.isEmpty() ? parentNode() : treeScope()->getElementById(SVGURIReference::getTarget(href));
499     if (!target || !target->isSVGElement())
500         return 0;
501     
502     m_targetElement = static_cast<SVGElement*>(target);
503     document()->accessSVGExtensions()->addAnimationElementToTarget(const_cast<SVGSMILElement*>(this), m_targetElement);
504     return m_targetElement;
505 }
506
507 SMILTime SVGSMILElement::elapsed() const 
508 {
509     return m_timeContainer ? m_timeContainer->elapsed() : 0;
510
511     
512 bool SVGSMILElement::isInactive() const
513 {
514      return m_activeState == Inactive;
515 }
516
517 bool SVGSMILElement::isFrozen() const
518 {
519     return m_activeState == Frozen;
520 }
521     
522 SVGSMILElement::Restart SVGSMILElement::restart() const
523 {    
524     DEFINE_STATIC_LOCAL(const AtomicString, never, ("never"));
525     DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive"));
526     const AtomicString& value = getAttribute(SVGNames::restartAttr);
527     if (value == never)
528         return RestartNever;
529     if (value == whenNotActive)
530         return RestartWhenNotActive;
531     return RestartAlways;
532 }
533     
534 SVGSMILElement::FillMode SVGSMILElement::fill() const
535 {   
536     DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze"));
537     const AtomicString& value = getAttribute(SVGNames::fillAttr);
538     return value == freeze ? FillFreeze : FillRemove;
539 }
540     
541 String SVGSMILElement::xlinkHref() const
542 {    
543     return getAttribute(XLinkNames::hrefAttr);
544 }
545     
546 SMILTime SVGSMILElement::dur() const
547 {   
548     if (m_cachedDur != invalidCachedTime)
549         return m_cachedDur;
550     const AtomicString& value = getAttribute(SVGNames::durAttr);
551     SMILTime clockValue = parseClockValue(value);
552     return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
553 }
554
555 SMILTime SVGSMILElement::repeatDur() const
556 {    
557     if (m_cachedRepeatDur != invalidCachedTime)
558         return m_cachedRepeatDur;
559     const AtomicString& value = getAttribute(SVGNames::repeatDurAttr);
560     SMILTime clockValue = parseClockValue(value);
561     m_cachedRepeatDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
562     return m_cachedRepeatDur;
563 }
564     
565 // So a count is not really a time but let just all pretend we did not notice.
566 SMILTime SVGSMILElement::repeatCount() const
567 {    
568     if (m_cachedRepeatCount != invalidCachedTime)
569         return m_cachedRepeatCount;
570     const AtomicString& value = getAttribute(SVGNames::repeatCountAttr);
571     if (value.isNull())
572         return SMILTime::unresolved();
573
574     DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite"));
575     if (value == indefiniteValue)
576         return SMILTime::indefinite();
577     bool ok;
578     double result = value.string().toDouble(&ok);
579     return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved();
580 }
581
582 SMILTime SVGSMILElement::maxValue() const
583 {    
584     if (m_cachedMax != invalidCachedTime)
585         return m_cachedMax;
586     const AtomicString& value = getAttribute(SVGNames::maxAttr);
587     SMILTime result = parseClockValue(value);
588     return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result;
589 }
590     
591 SMILTime SVGSMILElement::minValue() const
592 {    
593     if (m_cachedMin != invalidCachedTime)
594         return m_cachedMin;
595     const AtomicString& value = getAttribute(SVGNames::minAttr);
596     SMILTime result = parseClockValue(value);
597     return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
598 }
599                  
600 SMILTime SVGSMILElement::simpleDuration() const
601 {
602     return min(dur(), SMILTime::indefinite());
603 }
604     
605 void SVGSMILElement::addBeginTime(SMILTime time)
606 {
607     m_beginTimes.append(time);
608     sortTimeList(m_beginTimes);
609     beginListChanged();
610 }
611     
612 void SVGSMILElement::addEndTime(SMILTime time)
613 {
614     m_endTimes.append(time);
615     sortTimeList(m_endTimes);
616     endListChanged();
617 }
618     
619 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
620 {
621     // FIXME: This searches from the beginning which is inefficient. The list is usually not long
622     // (one entry in common cases) but you can construct a case where it does grow.
623     const Vector<SMILTime>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
624     for (unsigned n = 0; n < list.size(); ++n) {
625         SMILTime time = list[n];
626         ASSERT(!time.isUnresolved());
627         if (time.isIndefinite() && beginOrEnd == Begin) {
628             // "The special value "indefinite" does not yield an instance time in the begin list."
629             continue;
630         }
631         if (equalsMinimumOK) {
632             if (time >= minimumTime)
633                 return time;
634         } else if (time > minimumTime)
635             return time;
636     }
637     return SMILTime::unresolved();
638 }
639
640 SMILTime SVGSMILElement::repeatingDuration() const
641 {
642     // Computing the active duration
643     // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
644     SMILTime repeatCount = this->repeatCount();
645     SMILTime repeatDur = this->repeatDur();
646     SMILTime simpleDuration = this->simpleDuration();
647     if (!simpleDuration || (repeatDur.isUnresolved() && repeatCount.isUnresolved()))
648         return simpleDuration;
649     SMILTime repeatCountDuration = simpleDuration * repeatCount;
650     return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
651 }
652
653 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
654 {
655     // Computing the active duration
656     // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
657     SMILTime preliminaryActiveDuration;
658     if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
659         preliminaryActiveDuration = resolvedEnd - resolvedBegin;
660     else if (!resolvedEnd.isFinite())
661         preliminaryActiveDuration = repeatingDuration();
662     else
663         preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
664     
665     SMILTime minValue = this->minValue();
666     SMILTime maxValue = this->maxValue();
667     if (minValue > maxValue) {
668         // Ignore both.
669         // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
670         minValue = 0;
671         maxValue = SMILTime::indefinite();
672     }
673     return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
674 }
675
676 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const
677 {
678     // See the pseudocode in
679     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#Timing-BeginEnd-LifeCycle
680     SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
681     SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
682     while (true) {
683         SMILTime tempBegin = findInstanceTime(Begin, beginAfter, true);
684         if (tempBegin.isUnresolved())
685             break;
686         SMILTime tempEnd;
687         if (m_endTimes.isEmpty())
688             tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
689         else {
690             tempEnd = findInstanceTime(End, tempBegin, true);
691             if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd)) 
692                 tempEnd = findInstanceTime(End, tempBegin, false);    
693             if (tempEnd.isUnresolved()) {
694                 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions)
695                     break;
696             }
697             tempEnd = resolveActiveEnd(tempBegin, tempEnd);
698         }
699         if (tempEnd > 0 || !first) {
700             beginResult = tempBegin;
701             endResult = tempEnd;
702             return;
703         }
704         if (restart() == RestartNever)
705             break;
706
707         beginAfter = tempEnd;
708         lastIntervalTempEnd = tempEnd;
709     }
710     beginResult = SMILTime::unresolved();
711     endResult = SMILTime::unresolved();
712 }
713     
714 void SVGSMILElement::resolveFirstInterval()
715 {
716     SMILTime begin;
717     SMILTime end;
718     resolveInterval(true, begin, end);
719     ASSERT(!begin.isIndefinite());
720
721     if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {   
722         bool wasUnresolved = m_intervalBegin.isUnresolved();
723         m_intervalBegin = begin;
724         m_intervalEnd = end;
725         notifyDependentsIntervalChanged(wasUnresolved ? NewInterval : ExistingInterval);
726         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
727         reschedule();
728     }
729 }
730
731 void SVGSMILElement::resolveNextInterval()
732 {
733     SMILTime begin;
734     SMILTime end;
735     resolveInterval(false, begin, end);
736     ASSERT(!begin.isIndefinite());
737     
738     if (!begin.isUnresolved() && begin != m_intervalBegin) {
739         m_intervalBegin = begin;
740         m_intervalEnd = end;
741         notifyDependentsIntervalChanged(NewInterval);
742         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
743     }
744 }
745
746 SMILTime SVGSMILElement::nextProgressTime() const
747 {
748     return m_nextProgressTime;
749 }
750     
751 void SVGSMILElement::beginListChanged()
752 {
753     SMILTime elapsed = this->elapsed();
754     if (m_isWaitingForFirstInterval)
755         resolveFirstInterval();
756     else if (elapsed < m_intervalBegin) {
757         SMILTime newBegin = findInstanceTime(Begin, elapsed, false);
758         if (newBegin < m_intervalBegin) {
759             // Begin time changed, re-resolve the interval.
760             SMILTime oldBegin = m_intervalBegin;
761             m_intervalBegin = elapsed;
762             resolveInterval(false, m_intervalBegin, m_intervalEnd);  
763             ASSERT(!m_intervalBegin.isUnresolved());
764             if (m_intervalBegin != oldBegin)
765                 notifyDependentsIntervalChanged(ExistingInterval);
766         }
767     }
768     m_nextProgressTime = elapsed;
769     reschedule();
770 }
771
772 void SVGSMILElement::endListChanged()
773 {
774     SMILTime elapsed = this->elapsed();
775     if (m_isWaitingForFirstInterval)
776         resolveFirstInterval();
777     else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
778         SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
779         if (newEnd < m_intervalEnd) {
780             newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
781             if (newEnd != m_intervalEnd) {
782                 m_intervalEnd = newEnd;
783                 notifyDependentsIntervalChanged(ExistingInterval);
784             }
785         }
786     }
787     m_nextProgressTime = elapsed;
788     reschedule();
789 }
790     
791 void SVGSMILElement::checkRestart(SMILTime elapsed)
792 {
793     ASSERT(!m_isWaitingForFirstInterval);
794     ASSERT(elapsed >= m_intervalBegin);
795     
796     Restart restart = this->restart();
797     if (restart == RestartNever)
798         return;
799     
800     if (elapsed < m_intervalEnd) {
801         if (restart != RestartAlways)
802             return;
803         SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
804         if (nextBegin < m_intervalEnd) { 
805             m_intervalEnd = nextBegin;
806             notifyDependentsIntervalChanged(ExistingInterval);
807         }
808     } 
809     if (elapsed >= m_intervalEnd)
810         resolveNextInterval();
811 }
812     
813 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const
814 {
815     SMILTime simpleDuration = this->simpleDuration();
816     repeat = 0;
817     if (simpleDuration.isIndefinite()) {
818         repeat = 0;
819         return 0.f;
820     }
821     if (!simpleDuration) {
822         repeat = 0;
823         return 1.f;
824     }
825     ASSERT(m_intervalBegin.isFinite());
826     ASSERT(simpleDuration.isFinite());
827     SMILTime activeTime = elapsed - m_intervalBegin;
828     SMILTime repeatingDuration = this->repeatingDuration();
829     if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
830         repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value());
831         if (fmod(repeatingDuration.value(), !simpleDuration.value()))
832             repeat--;
833         return 1.f;
834     }
835     repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
836     SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
837     return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
838 }
839     
840 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
841 {
842     if (m_activeState == Active) {
843         // If duration is indefinite the value does not actually change over time. Same is true for <set>.
844         SMILTime simpleDuration = this->simpleDuration();
845         if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) {
846             SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
847             // We are supposed to do freeze semantics when repeating ends, even if the element is still active. 
848             // Take care that we get a timer callback at that point.
849             if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite())
850                 return repeatingDurationEnd;
851             return m_intervalEnd;
852         } 
853         return elapsed + 0.025;
854     }
855     return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
856 }
857     
858 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
859 {
860     if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd)
861         return Active;
862
863     if (m_activeState == Active)
864         return fill() == FillFreeze ? Frozen : Inactive;
865
866     return m_activeState;
867 }
868     
869 bool SVGSMILElement::isContributing(SMILTime elapsed) const 
870
871     // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
872     return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
873 }
874     
875 void SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement)
876 {
877     ASSERT(m_timeContainer);
878     ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
879     
880     if (!m_conditionsConnected)
881         connectConditions();
882     
883     if (!m_intervalBegin.isFinite()) {
884         ASSERT(m_activeState == Inactive);
885         m_nextProgressTime = SMILTime::unresolved();
886         return;
887     }
888     
889     if (elapsed < m_intervalBegin) {
890         ASSERT(m_activeState != Active);
891         if (m_activeState == Frozen && resultElement)
892             updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
893         m_nextProgressTime = m_intervalBegin;
894         return;
895     }
896     
897     m_previousIntervalBegin = m_intervalBegin;
898     
899     if (m_activeState == Inactive) {
900         m_isWaitingForFirstInterval = false;
901         m_activeState = Active;
902         startedActiveInterval();
903     }
904     
905     unsigned repeat;
906     float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
907
908     checkRestart(elapsed);
909
910     ActiveState oldActiveState = m_activeState;
911     m_activeState = determineActiveState(elapsed);
912     
913     if (isContributing(elapsed)) {
914         if (resultElement)
915             updateAnimation(percent, repeat, resultElement);
916         m_lastPercent = percent;
917         m_lastRepeat = repeat;
918     }
919
920     if (oldActiveState == Active && m_activeState != Active)
921         endedActiveInterval();
922
923     m_nextProgressTime = calculateNextProgressTime(elapsed);
924 }
925     
926 void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)
927 {
928     ASSERT(m_intervalBegin.isFinite());
929     DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ());
930     if (loopBreaker.contains(this))
931         return;
932     loopBreaker.add(this);
933     
934     TimeDependentSet::iterator end = m_timeDependents.end();
935     for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) {
936         SVGSMILElement* dependent = *it;
937         dependent->createInstanceTimesFromSyncbase(this, newOrExisting);
938     }
939
940     loopBreaker.remove(this);
941 }
942     
943 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval)
944 {
945     // FIXME: To be really correct, this should handle updating exising interval by changing 
946     // the associated times instead of creating new ones.
947     for (unsigned n = 0; n < m_conditions.size(); ++n) {
948         Condition& condition = m_conditions[n];
949         if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
950             ASSERT(condition.m_name == "begin" || condition.m_name == "end");
951             // No nested time containers in SVG, no need for crazy time space conversions. Phew!
952             SMILTime time = 0;
953             if (condition.m_name == "begin")
954                 time = syncbase->m_intervalBegin + condition.m_offset;
955             else
956                 time = syncbase->m_intervalEnd + condition.m_offset;
957             ASSERT(time.isFinite());
958             if (condition.m_beginOrEnd == Begin)
959                 addBeginTime(time);
960             else
961                 addEndTime(time);
962         }
963     }
964 }
965     
966 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
967 {
968     m_timeDependents.add(animation);
969     if (m_intervalBegin.isFinite())
970         animation->createInstanceTimesFromSyncbase(this, NewInterval);
971 }
972     
973 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
974 {
975     m_timeDependents.remove(animation);
976 }
977     
978 void SVGSMILElement::handleConditionEvent(Event*, Condition* condition)
979 {
980     if (condition->m_beginOrEnd == Begin)
981         addBeginTime(elapsed() + condition->m_offset);
982     else
983         addEndTime(elapsed() + condition->m_offset);
984 }
985
986 void SVGSMILElement::beginByLinkActivation()
987 {
988     addBeginTime(elapsed());
989 }
990     
991 }
992
993 #endif