Home | History | Annotate | Download | only in animation
      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->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 
    188 void 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 
    206 void 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 
    220 SMILTime 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 
    240 SMILTime 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 
    276 static void sortTimeList(Vector<SMILTime>& timeList)
    277 {
    278     std::sort(timeList.begin(), timeList.end());
    279 }
    280 
    281 bool 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 
    348 bool 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 
    356 void 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 
    376 void 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 
    400 void 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 
    428 inline Element* SVGSMILElement::eventBaseFor(const Condition& condition) const
    429 {
    430     return condition.m_baseID.isEmpty() ? targetElement() : document()->getElementById(condition.m_baseID);
    431 }
    432 
    433 void 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 
    461 void 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 
    492 void SVGSMILElement::reschedule()
    493 {
    494     if (m_timeContainer)
    495         m_timeContainer->schedule(this);
    496 }
    497 
    498 SVGElement* 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 
    513 SMILTime SVGSMILElement::elapsed() const
    514 {
    515     return m_timeContainer ? m_timeContainer->elapsed() : 0;
    516 }
    517 
    518 bool SVGSMILElement::isInactive() const
    519 {
    520      return m_activeState == Inactive;
    521 }
    522 
    523 bool SVGSMILElement::isFrozen() const
    524 {
    525     return m_activeState == Frozen;
    526 }
    527 
    528 SVGSMILElement::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 
    540 SVGSMILElement::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 
    547 String SVGSMILElement::xlinkHref() const
    548 {
    549     return getAttribute(XLinkNames::hrefAttr);
    550 }
    551 
    552 SMILTime 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 
    561 SMILTime 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.
    572 SMILTime 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 
    588 SMILTime 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 
    597 SMILTime 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 
    606 SMILTime SVGSMILElement::simpleDuration() const
    607 {
    608     return min(dur(), SMILTime::indefinite());
    609 }
    610 
    611 void SVGSMILElement::addBeginTime(SMILTime time)
    612 {
    613     m_beginTimes.append(time);
    614     sortTimeList(m_beginTimes);
    615     beginListChanged();
    616 }
    617 
    618 void SVGSMILElement::addEndTime(SMILTime time)
    619 {
    620     m_endTimes.append(time);
    621     sortTimeList(m_endTimes);
    622     endListChanged();
    623 }
    624 
    625 SMILTime 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 
    646 SMILTime 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 
    659 SMILTime 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 
    682 void 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 
    720 void 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 
    737 void 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 
    752 SMILTime SVGSMILElement::nextProgressTime() const
    753 {
    754     return m_nextProgressTime;
    755 }
    756 
    757 void 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 
    778 void 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 
    797 void 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 
    819 float 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 
    846 SMILTime 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 
    865 SVGSMILElement::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 
    876 bool 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 
    882 void 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 
    933 void 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 
    950 void 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 
    973 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
    974 {
    975     m_timeDependents.add(animation);
    976     if (m_intervalBegin.isFinite())
    977         animation->createInstanceTimesFromSyncbase(this, NewInterval);
    978 }
    979 
    980 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
    981 {
    982     m_timeDependents.remove(animation);
    983 }
    984 
    985 void 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 
    993 void SVGSMILElement::beginByLinkActivation()
    994 {
    995     addBeginTime(elapsed());
    996 }
    997 
    998 }
    999 
   1000 #endif
   1001