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