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