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 #include "core/svg/animation/SVGSMILElement.h"
     28 
     29 #include "SVGNames.h"
     30 #include "XLinkNames.h"
     31 #include "bindings/v8/ExceptionStatePlaceholder.h"
     32 #include "core/dom/Document.h"
     33 #include "core/dom/EventListener.h"
     34 #include "core/platform/FloatConversion.h"
     35 #include "core/svg/SVGDocumentExtensions.h"
     36 #include "core/svg/SVGSVGElement.h"
     37 #include "core/svg/SVGURIReference.h"
     38 #include "core/svg/animation/SMILTimeContainer.h"
     39 #include "wtf/MathExtras.h"
     40 #include "wtf/StdLibExtras.h"
     41 #include "wtf/Vector.h"
     42 
     43 using namespace std;
     44 
     45 namespace WebCore {
     46 
     47 // This is used for duration type time values that can't be negative.
     48 static const double invalidCachedTime = -1.;
     49 
     50 class ConditionEventListener : public EventListener {
     51 public:
     52     static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
     53     {
     54         return adoptRef(new ConditionEventListener(animation, condition));
     55     }
     56 
     57     static const ConditionEventListener* cast(const EventListener* listener)
     58     {
     59         return listener->type() == ConditionEventListenerType
     60             ? static_cast<const ConditionEventListener*>(listener)
     61             : 0;
     62     }
     63 
     64     virtual bool operator==(const EventListener& other);
     65 
     66     void disconnectAnimation()
     67     {
     68         m_animation = 0;
     69     }
     70 
     71 private:
     72     ConditionEventListener(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
     73         : EventListener(ConditionEventListenerType)
     74         , m_animation(animation)
     75         , m_condition(condition)
     76     {
     77     }
     78 
     79     virtual void handleEvent(ScriptExecutionContext*, Event*);
     80 
     81     SVGSMILElement* m_animation;
     82     SVGSMILElement::Condition* m_condition;
     83 };
     84 
     85 bool ConditionEventListener::operator==(const EventListener& listener)
     86 {
     87     if (const ConditionEventListener* conditionEventListener = ConditionEventListener::cast(&listener))
     88         return m_animation == conditionEventListener->m_animation && m_condition == conditionEventListener->m_condition;
     89     return false;
     90 }
     91 
     92 void ConditionEventListener::handleEvent(ScriptExecutionContext*, Event* event)
     93 {
     94     if (!m_animation)
     95         return;
     96     m_animation->handleConditionEvent(event, m_condition);
     97 }
     98 
     99 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeats)
    100     : m_type(type)
    101     , m_beginOrEnd(beginOrEnd)
    102     , m_baseID(baseID)
    103     , m_name(name)
    104     , m_offset(offset)
    105     , m_repeats(repeats)
    106 {
    107 }
    108 
    109 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document* doc)
    110     : SVGElement(tagName, doc)
    111     , m_attributeName(anyQName())
    112     , m_targetElement(0)
    113     , m_conditionsConnected(false)
    114     , m_hasEndEventConditions(false)
    115     , m_isWaitingForFirstInterval(true)
    116     , m_intervalBegin(SMILTime::unresolved())
    117     , m_intervalEnd(SMILTime::unresolved())
    118     , m_previousIntervalBegin(SMILTime::unresolved())
    119     , m_activeState(Inactive)
    120     , m_lastPercent(0)
    121     , m_lastRepeat(0)
    122     , m_nextProgressTime(0)
    123     , m_documentOrderIndex(0)
    124     , m_cachedDur(invalidCachedTime)
    125     , m_cachedRepeatDur(invalidCachedTime)
    126     , m_cachedRepeatCount(invalidCachedTime)
    127     , m_cachedMin(invalidCachedTime)
    128     , m_cachedMax(invalidCachedTime)
    129 {
    130     resolveFirstInterval();
    131 }
    132 
    133 SVGSMILElement::~SVGSMILElement()
    134 {
    135     clearResourceReferences();
    136     disconnectConditions();
    137     if (m_timeContainer && m_targetElement && hasValidAttributeName())
    138         m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
    139 }
    140 
    141 void SVGSMILElement::clearResourceReferences()
    142 {
    143     ASSERT(document());
    144     document()->accessSVGExtensions()->removeAllTargetReferencesForElement(this);
    145 }
    146 
    147 void SVGSMILElement::buildPendingResource()
    148 {
    149     clearResourceReferences();
    150 
    151     if (!inDocument()) {
    152         // Reset the target element if we are no longer in the document.
    153         setTargetElement(0);
    154         return;
    155     }
    156 
    157     String id;
    158     String href = getAttribute(XLinkNames::hrefAttr);
    159     Element* target;
    160     if (href.isEmpty())
    161         target = parentNode() && parentNode()->isElementNode() ? toElement(parentNode()) : 0;
    162     else
    163         target = SVGURIReference::targetElementFromIRIString(href, document(), &id);
    164     SVGElement* svgTarget = target && target->isSVGElement() ? toSVGElement(target) : 0;
    165 
    166     if (svgTarget && !svgTarget->inDocument())
    167         svgTarget = 0;
    168 
    169     if (svgTarget != targetElement())
    170         setTargetElement(svgTarget);
    171 
    172     if (!svgTarget) {
    173         // Do not register as pending if we are already pending this resource.
    174         if (document()->accessSVGExtensions()->isElementPendingResource(this, id))
    175             return;
    176 
    177         if (!id.isEmpty()) {
    178             document()->accessSVGExtensions()->addPendingResource(id, this);
    179             ASSERT(hasPendingResources());
    180         }
    181     } else {
    182         // Register us with the target in the dependencies map. Any change of hrefElement
    183         // that leads to relayout/repainting now informs us, so we can react to it.
    184         document()->accessSVGExtensions()->addElementReferencingTarget(this, svgTarget);
    185     }
    186 }
    187 
    188 static inline QualifiedName constructQualifiedName(const SVGElement* svgElement, const String& attributeName)
    189 {
    190     ASSERT(svgElement);
    191     if (attributeName.isEmpty())
    192         return anyQName();
    193     if (!attributeName.contains(':'))
    194         return QualifiedName(nullAtom, attributeName, nullAtom);
    195 
    196     String prefix;
    197     String localName;
    198     if (!Document::parseQualifiedName(attributeName, prefix, localName, ASSERT_NO_EXCEPTION))
    199         return anyQName();
    200 
    201     String namespaceURI = svgElement->lookupNamespaceURI(prefix);
    202     if (namespaceURI.isEmpty())
    203         return anyQName();
    204 
    205     return QualifiedName(nullAtom, localName, namespaceURI);
    206 }
    207 
    208 static inline void clearTimesWithDynamicOrigins(Vector<SMILTimeWithOrigin>& timeList)
    209 {
    210     for (int i = timeList.size() - 1; i >= 0; --i) {
    211         if (timeList[i].originIsScript())
    212             timeList.remove(i);
    213     }
    214 }
    215 
    216 void SVGSMILElement::reset()
    217 {
    218     clearAnimatedType(m_targetElement);
    219 
    220     m_activeState = Inactive;
    221     m_isWaitingForFirstInterval = true;
    222     m_intervalBegin = SMILTime::unresolved();
    223     m_intervalEnd = SMILTime::unresolved();
    224     m_previousIntervalBegin = SMILTime::unresolved();
    225     m_lastPercent = 0;
    226     m_lastRepeat = 0;
    227     m_nextProgressTime = 0;
    228     resolveFirstInterval();
    229 }
    230 
    231 Node::InsertionNotificationRequest SVGSMILElement::insertedInto(ContainerNode* rootParent)
    232 {
    233     SVGElement::insertedInto(rootParent);
    234     if (!rootParent->inDocument())
    235         return InsertionDone;
    236 
    237     // Verify we are not in <use> instance tree.
    238     ASSERT(!isInShadowTree() || !parentOrShadowHostElement() || !parentOrShadowHostElement()->isSVGElement());
    239 
    240     setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
    241     SVGSVGElement* owner = ownerSVGElement();
    242     if (!owner)
    243         return InsertionDone;
    244 
    245     m_timeContainer = owner->timeContainer();
    246     ASSERT(m_timeContainer);
    247     m_timeContainer->setDocumentOrderIndexesDirty();
    248 
    249     // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
    250     if (!fastHasAttribute(SVGNames::beginAttr))
    251         m_beginTimes.append(SMILTimeWithOrigin());
    252 
    253     if (m_isWaitingForFirstInterval)
    254         resolveFirstInterval();
    255 
    256     if (m_timeContainer)
    257         m_timeContainer->notifyIntervalsChanged();
    258 
    259     buildPendingResource();
    260 
    261     return InsertionDone;
    262 }
    263 
    264 void SVGSMILElement::removedFrom(ContainerNode* rootParent)
    265 {
    266     if (rootParent->inDocument()) {
    267         clearResourceReferences();
    268         disconnectConditions();
    269         setTargetElement(0);
    270         setAttributeName(anyQName());
    271         animationAttributeChanged();
    272         m_timeContainer = 0;
    273     }
    274 
    275     SVGElement::removedFrom(rootParent);
    276 }
    277 
    278 bool SVGSMILElement::hasValidAttributeName()
    279 {
    280     return attributeName() != anyQName();
    281 }
    282 
    283 SMILTime SVGSMILElement::parseOffsetValue(const String& data)
    284 {
    285     bool ok;
    286     double result = 0;
    287     String parse = data.stripWhiteSpace();
    288     if (parse.endsWith('h'))
    289         result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
    290     else if (parse.endsWith("min"))
    291         result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
    292     else if (parse.endsWith("ms"))
    293         result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
    294     else if (parse.endsWith('s'))
    295         result = parse.left(parse.length() - 1).toDouble(&ok);
    296     else
    297         result = parse.toDouble(&ok);
    298     if (!ok)
    299         return SMILTime::unresolved();
    300     return result;
    301 }
    302 
    303 SMILTime SVGSMILElement::parseClockValue(const String& data)
    304 {
    305     if (data.isNull())
    306         return SMILTime::unresolved();
    307 
    308     String parse = data.stripWhiteSpace();
    309 
    310     DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
    311     if (parse == indefiniteValue)
    312         return SMILTime::indefinite();
    313 
    314     double result = 0;
    315     bool ok;
    316     size_t doublePointOne = parse.find(':');
    317     size_t doublePointTwo = parse.find(':', doublePointOne + 1);
    318     if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) {
    319         result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
    320         if (!ok)
    321             return SMILTime::unresolved();
    322         result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
    323         if (!ok)
    324             return SMILTime::unresolved();
    325         result += parse.substring(6).toDouble(&ok);
    326     } else if (doublePointOne == 2 && doublePointTwo == notFound && parse.length() >= 5) {
    327         result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
    328         if (!ok)
    329             return SMILTime::unresolved();
    330         result += parse.substring(3).toDouble(&ok);
    331     } else
    332         return parseOffsetValue(parse);
    333 
    334     if (!ok)
    335         return SMILTime::unresolved();
    336     return result;
    337 }
    338 
    339 static void sortTimeList(Vector<SMILTimeWithOrigin>& timeList)
    340 {
    341     std::sort(timeList.begin(), timeList.end());
    342 }
    343 
    344 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd)
    345 {
    346     String parseString = value.stripWhiteSpace();
    347 
    348     double sign = 1.;
    349     bool ok;
    350     size_t pos = parseString.find('+');
    351     if (pos == notFound) {
    352         pos = parseString.find('-');
    353         if (pos != notFound)
    354             sign = -1.;
    355     }
    356     String conditionString;
    357     SMILTime offset = 0;
    358     if (pos == notFound)
    359         conditionString = parseString;
    360     else {
    361         conditionString = parseString.left(pos).stripWhiteSpace();
    362         String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
    363         offset = parseOffsetValue(offsetString);
    364         if (offset.isUnresolved())
    365             return false;
    366         offset = offset * sign;
    367     }
    368     if (conditionString.isEmpty())
    369         return false;
    370     pos = conditionString.find('.');
    371 
    372     String baseID;
    373     String nameString;
    374     if (pos == notFound)
    375         nameString = conditionString;
    376     else {
    377         baseID = conditionString.left(pos);
    378         nameString = conditionString.substring(pos + 1);
    379     }
    380     if (nameString.isEmpty())
    381         return false;
    382 
    383     Condition::Type type;
    384     int repeats = -1;
    385     if (nameString.startsWith("repeat(") && nameString.endsWith(')')) {
    386         // FIXME: For repeat events we just need to add the data carrying TimeEvent class and
    387         // fire the events at appropiate times.
    388         repeats = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
    389         if (!ok)
    390             return false;
    391         nameString = "repeat";
    392         type = Condition::EventBase;
    393     } else if (nameString == "begin" || nameString == "end") {
    394         if (baseID.isEmpty())
    395             return false;
    396         type = Condition::Syncbase;
    397     } else if (nameString.startsWith("accesskey(")) {
    398         // FIXME: accesskey() support.
    399         type = Condition::AccessKey;
    400     } else
    401         type = Condition::EventBase;
    402 
    403     m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeats));
    404 
    405     if (type == Condition::EventBase && beginOrEnd == End)
    406         m_hasEndEventConditions = true;
    407 
    408     return true;
    409 }
    410 
    411 bool SVGSMILElement::isSMILElement(Node* node)
    412 {
    413     if (!node)
    414         return false;
    415     return node->hasTagName(SVGNames::setTag) || node->hasTagName(SVGNames::animateTag) || node->hasTagName(SVGNames::animateMotionTag)
    416             || node->hasTagName(SVGNames::animateTransformTag) || node->hasTagName(SVGNames::animateColorTag);
    417 }
    418 
    419 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd)
    420 {
    421     Vector<SMILTimeWithOrigin>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
    422     if (beginOrEnd == End)
    423         m_hasEndEventConditions = false;
    424     HashSet<double> existing;
    425     for (unsigned n = 0; n < timeList.size(); ++n)
    426         existing.add(timeList[n].time().value());
    427     Vector<String> splitString;
    428     parseString.split(';', splitString);
    429     for (unsigned n = 0; n < splitString.size(); ++n) {
    430         SMILTime value = parseClockValue(splitString[n]);
    431         if (value.isUnresolved())
    432             parseCondition(splitString[n], beginOrEnd);
    433         else if (!existing.contains(value.value()))
    434             timeList.append(SMILTimeWithOrigin(value, SMILTimeWithOrigin::ParserOrigin));
    435     }
    436     sortTimeList(timeList);
    437 }
    438 
    439 bool SVGSMILElement::isSupportedAttribute(const QualifiedName& attrName)
    440 {
    441     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
    442     if (supportedAttributes.isEmpty()) {
    443         supportedAttributes.add(SVGNames::beginAttr);
    444         supportedAttributes.add(SVGNames::endAttr);
    445         supportedAttributes.add(SVGNames::durAttr);
    446         supportedAttributes.add(SVGNames::repeatDurAttr);
    447         supportedAttributes.add(SVGNames::repeatCountAttr);
    448         supportedAttributes.add(SVGNames::minAttr);
    449         supportedAttributes.add(SVGNames::maxAttr);
    450         supportedAttributes.add(SVGNames::attributeNameAttr);
    451         supportedAttributes.add(XLinkNames::hrefAttr);
    452     }
    453     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
    454 }
    455 
    456 void SVGSMILElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    457 {
    458     if (name == SVGNames::beginAttr) {
    459         if (!m_conditions.isEmpty()) {
    460             disconnectConditions();
    461             m_conditions.clear();
    462             parseBeginOrEnd(fastGetAttribute(SVGNames::endAttr), End);
    463         }
    464         parseBeginOrEnd(value.string(), Begin);
    465         if (inDocument())
    466             connectConditions();
    467     } else if (name == SVGNames::endAttr) {
    468         if (!m_conditions.isEmpty()) {
    469             disconnectConditions();
    470             m_conditions.clear();
    471             parseBeginOrEnd(fastGetAttribute(SVGNames::beginAttr), Begin);
    472         }
    473         parseBeginOrEnd(value.string(), End);
    474         if (inDocument())
    475             connectConditions();
    476     } else
    477         SVGElement::parseAttribute(name, value);
    478 }
    479 
    480 void SVGSMILElement::svgAttributeChanged(const QualifiedName& attrName)
    481 {
    482     if (!isSupportedAttribute(attrName)) {
    483         SVGElement::svgAttributeChanged(attrName);
    484         return;
    485     }
    486 
    487     if (attrName == SVGNames::durAttr)
    488         m_cachedDur = invalidCachedTime;
    489     else if (attrName == SVGNames::repeatDurAttr)
    490         m_cachedRepeatDur = invalidCachedTime;
    491     else if (attrName == SVGNames::repeatCountAttr)
    492         m_cachedRepeatCount = invalidCachedTime;
    493     else if (attrName == SVGNames::minAttr)
    494         m_cachedMin = invalidCachedTime;
    495     else if (attrName == SVGNames::maxAttr)
    496         m_cachedMax = invalidCachedTime;
    497     else if (attrName == SVGNames::attributeNameAttr)
    498         setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
    499     else if (attrName.matches(XLinkNames::hrefAttr)) {
    500         SVGElementInstance::InvalidationGuard invalidationGuard(this);
    501         buildPendingResource();
    502     } else if (inDocument()) {
    503         if (attrName == SVGNames::beginAttr)
    504             beginListChanged(elapsed());
    505         else if (attrName == SVGNames::endAttr)
    506             endListChanged(elapsed());
    507     }
    508 
    509     animationAttributeChanged();
    510 }
    511 
    512 inline Element* SVGSMILElement::eventBaseFor(const Condition& condition)
    513 {
    514     return condition.m_baseID.isEmpty() ? targetElement() : treeScope()->getElementById(condition.m_baseID);
    515 }
    516 
    517 void SVGSMILElement::connectConditions()
    518 {
    519     if (m_conditionsConnected)
    520         disconnectConditions();
    521     m_conditionsConnected = true;
    522     for (unsigned n = 0; n < m_conditions.size(); ++n) {
    523         Condition& condition = m_conditions[n];
    524         if (condition.m_type == Condition::EventBase) {
    525             ASSERT(!condition.m_syncbase);
    526             Element* eventBase = eventBaseFor(condition);
    527             if (!eventBase)
    528                 continue;
    529             ASSERT(!condition.m_eventListener);
    530             condition.m_eventListener = ConditionEventListener::create(this, &condition);
    531             eventBase->addEventListener(condition.m_name, condition.m_eventListener, false);
    532         } else if (condition.m_type == Condition::Syncbase) {
    533             ASSERT(!condition.m_baseID.isEmpty());
    534             condition.m_syncbase = treeScope()->getElementById(condition.m_baseID);
    535             if (!isSMILElement(condition.m_syncbase.get())) {
    536                 condition.m_syncbase = 0;
    537                 continue;
    538             }
    539             toSVGSMILElement(condition.m_syncbase.get())->addTimeDependent(this);
    540         }
    541     }
    542 }
    543 
    544 void SVGSMILElement::disconnectConditions()
    545 {
    546     if (!m_conditionsConnected)
    547         return;
    548     m_conditionsConnected = false;
    549     for (unsigned n = 0; n < m_conditions.size(); ++n) {
    550         Condition& condition = m_conditions[n];
    551         if (condition.m_type == Condition::EventBase) {
    552             ASSERT(!condition.m_syncbase);
    553             if (!condition.m_eventListener)
    554                 continue;
    555             // Note: It's a memory optimization to try to remove our condition
    556             // event listener, but it's not guaranteed to work, since we have
    557             // no guarantee that eventBaseFor() will be able to find our condition's
    558             // original eventBase. So, we also have to disconnect ourselves from
    559             // our condition event listener, in case it later fires.
    560             Element* eventBase = eventBaseFor(condition);
    561             if (eventBase)
    562                 eventBase->removeEventListener(condition.m_name, condition.m_eventListener.get(), false);
    563             condition.m_eventListener->disconnectAnimation();
    564             condition.m_eventListener = 0;
    565         } else if (condition.m_type == Condition::Syncbase) {
    566             if (condition.m_syncbase)
    567                 toSVGSMILElement(condition.m_syncbase.get())->removeTimeDependent(this);
    568         }
    569         condition.m_syncbase = 0;
    570     }
    571 }
    572 
    573 void SVGSMILElement::setAttributeName(const QualifiedName& attributeName)
    574 {
    575     if (m_timeContainer && m_targetElement && m_attributeName != attributeName) {
    576         if (hasValidAttributeName())
    577             m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
    578         m_attributeName = attributeName;
    579         if (hasValidAttributeName())
    580             m_timeContainer->schedule(this, m_targetElement, m_attributeName);
    581     } else
    582         m_attributeName = attributeName;
    583 
    584     // Only clear the animated type, if we had a target before.
    585     if (m_targetElement)
    586         clearAnimatedType(m_targetElement);
    587 }
    588 
    589 void SVGSMILElement::setTargetElement(SVGElement* target)
    590 {
    591     if (m_timeContainer && hasValidAttributeName()) {
    592         if (m_targetElement)
    593             m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
    594         if (target)
    595             m_timeContainer->schedule(this, target, m_attributeName);
    596     }
    597 
    598     if (m_targetElement) {
    599         // Clear values that may depend on the previous target.
    600         clearAnimatedType(m_targetElement);
    601         disconnectConditions();
    602     }
    603 
    604     // If the animation state is not Inactive, always reset to a clear state before leaving the old target element.
    605     if (m_activeState != Inactive)
    606         endedActiveInterval();
    607 
    608     m_targetElement = target;
    609 }
    610 
    611 SMILTime SVGSMILElement::elapsed() const
    612 {
    613     return m_timeContainer ? m_timeContainer->elapsed() : 0;
    614 }
    615 
    616 bool SVGSMILElement::isInactive() const
    617 {
    618      return m_activeState == Inactive;
    619 }
    620 
    621 bool SVGSMILElement::isFrozen() const
    622 {
    623     return m_activeState == Frozen;
    624 }
    625 
    626 SVGSMILElement::Restart SVGSMILElement::restart() const
    627 {
    628     DEFINE_STATIC_LOCAL(const AtomicString, never, ("never", AtomicString::ConstructFromLiteral));
    629     DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive", AtomicString::ConstructFromLiteral));
    630     const AtomicString& value = fastGetAttribute(SVGNames::restartAttr);
    631     if (value == never)
    632         return RestartNever;
    633     if (value == whenNotActive)
    634         return RestartWhenNotActive;
    635     return RestartAlways;
    636 }
    637 
    638 SVGSMILElement::FillMode SVGSMILElement::fill() const
    639 {
    640     DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze", AtomicString::ConstructFromLiteral));
    641     const AtomicString& value = fastGetAttribute(SVGNames::fillAttr);
    642     return value == freeze ? FillFreeze : FillRemove;
    643 }
    644 
    645 SMILTime SVGSMILElement::dur() const
    646 {
    647     if (m_cachedDur != invalidCachedTime)
    648         return m_cachedDur;
    649     const AtomicString& value = fastGetAttribute(SVGNames::durAttr);
    650     SMILTime clockValue = parseClockValue(value);
    651     return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
    652 }
    653 
    654 SMILTime SVGSMILElement::repeatDur() const
    655 {
    656     if (m_cachedRepeatDur != invalidCachedTime)
    657         return m_cachedRepeatDur;
    658     const AtomicString& value = fastGetAttribute(SVGNames::repeatDurAttr);
    659     SMILTime clockValue = parseClockValue(value);
    660     m_cachedRepeatDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
    661     return m_cachedRepeatDur;
    662 }
    663 
    664 // So a count is not really a time but let just all pretend we did not notice.
    665 SMILTime SVGSMILElement::repeatCount() const
    666 {
    667     if (m_cachedRepeatCount != invalidCachedTime)
    668         return m_cachedRepeatCount;
    669     const AtomicString& value = fastGetAttribute(SVGNames::repeatCountAttr);
    670     if (value.isNull())
    671         return SMILTime::unresolved();
    672 
    673     DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
    674     if (value == indefiniteValue)
    675         return SMILTime::indefinite();
    676     bool ok;
    677     double result = value.string().toDouble(&ok);
    678     return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved();
    679 }
    680 
    681 SMILTime SVGSMILElement::maxValue() const
    682 {
    683     if (m_cachedMax != invalidCachedTime)
    684         return m_cachedMax;
    685     const AtomicString& value = fastGetAttribute(SVGNames::maxAttr);
    686     SMILTime result = parseClockValue(value);
    687     return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result;
    688 }
    689 
    690 SMILTime SVGSMILElement::minValue() const
    691 {
    692     if (m_cachedMin != invalidCachedTime)
    693         return m_cachedMin;
    694     const AtomicString& value = fastGetAttribute(SVGNames::minAttr);
    695     SMILTime result = parseClockValue(value);
    696     return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
    697 }
    698 
    699 SMILTime SVGSMILElement::simpleDuration() const
    700 {
    701     return min(dur(), SMILTime::indefinite());
    702 }
    703 
    704 void SVGSMILElement::addBeginTime(SMILTime eventTime, SMILTime beginTime, SMILTimeWithOrigin::Origin origin)
    705 {
    706     ASSERT(!std::isnan(beginTime.value()));
    707     m_beginTimes.append(SMILTimeWithOrigin(beginTime, origin));
    708     sortTimeList(m_beginTimes);
    709     beginListChanged(eventTime);
    710 }
    711 
    712 void SVGSMILElement::addEndTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin origin)
    713 {
    714     ASSERT(!std::isnan(endTime.value()));
    715     m_endTimes.append(SMILTimeWithOrigin(endTime, origin));
    716     sortTimeList(m_endTimes);
    717     endListChanged(eventTime);
    718 }
    719 
    720 inline SMILTime extractTimeFromVector(const SMILTimeWithOrigin* position)
    721 {
    722     return position->time();
    723 }
    724 
    725 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
    726 {
    727     const Vector<SMILTimeWithOrigin>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
    728     int sizeOfList = list.size();
    729 
    730     if (!sizeOfList)
    731         return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
    732 
    733     const SMILTimeWithOrigin* result = approximateBinarySearch<const SMILTimeWithOrigin, SMILTime>(list, sizeOfList, minimumTime, extractTimeFromVector);
    734     int indexOfResult = result - list.begin();
    735     ASSERT_WITH_SECURITY_IMPLICATION(indexOfResult < sizeOfList);
    736 
    737     if (list[indexOfResult].time() < minimumTime && indexOfResult < sizeOfList - 1)
    738         ++indexOfResult;
    739 
    740     const SMILTime& currentTime = list[indexOfResult].time();
    741 
    742     // The special value "indefinite" does not yield an instance time in the begin list.
    743     if (currentTime.isIndefinite() && beginOrEnd == Begin)
    744         return SMILTime::unresolved();
    745 
    746     if (currentTime < minimumTime)
    747         return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
    748     if (currentTime > minimumTime)
    749         return currentTime;
    750 
    751     ASSERT(currentTime == minimumTime);
    752     if (equalsMinimumOK)
    753         return currentTime;
    754 
    755     // If the equals is not accepted, return the next bigger item in the list.
    756     SMILTime nextTime = currentTime;
    757     while (indexOfResult < sizeOfList - 1) {
    758         nextTime = list[indexOfResult + 1].time();
    759         if (nextTime > minimumTime)
    760             return nextTime;
    761         ++indexOfResult;
    762     }
    763 
    764     return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
    765 }
    766 
    767 SMILTime SVGSMILElement::repeatingDuration() const
    768 {
    769     // Computing the active duration
    770     // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
    771     SMILTime repeatCount = this->repeatCount();
    772     SMILTime repeatDur = this->repeatDur();
    773     SMILTime simpleDuration = this->simpleDuration();
    774     if (!simpleDuration || (repeatDur.isUnresolved() && repeatCount.isUnresolved()))
    775         return simpleDuration;
    776     SMILTime repeatCountDuration = simpleDuration * repeatCount;
    777     return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
    778 }
    779 
    780 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
    781 {
    782     // Computing the active duration
    783     // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
    784     SMILTime preliminaryActiveDuration;
    785     if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
    786         preliminaryActiveDuration = resolvedEnd - resolvedBegin;
    787     else if (!resolvedEnd.isFinite())
    788         preliminaryActiveDuration = repeatingDuration();
    789     else
    790         preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
    791 
    792     SMILTime minValue = this->minValue();
    793     SMILTime maxValue = this->maxValue();
    794     if (minValue > maxValue) {
    795         // Ignore both.
    796         // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
    797         minValue = 0;
    798         maxValue = SMILTime::indefinite();
    799     }
    800     return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
    801 }
    802 
    803 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const
    804 {
    805     // See the pseudocode in http://www.w3.org/TR/SMIL3/smil-timing.html#q90.
    806     SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
    807     SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
    808     while (true) {
    809         bool equalsMinimumOK = !first || m_intervalEnd > m_intervalBegin;
    810         SMILTime tempBegin = findInstanceTime(Begin, beginAfter, equalsMinimumOK);
    811         if (tempBegin.isUnresolved())
    812             break;
    813         SMILTime tempEnd;
    814         if (m_endTimes.isEmpty())
    815             tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
    816         else {
    817             tempEnd = findInstanceTime(End, tempBegin, true);
    818             if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd))
    819                 tempEnd = findInstanceTime(End, tempBegin, false);
    820             if (tempEnd.isUnresolved()) {
    821                 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions)
    822                     break;
    823             }
    824             tempEnd = resolveActiveEnd(tempBegin, tempEnd);
    825         }
    826         if (!first || (tempEnd > 0 || (!tempBegin.value() && !tempEnd.value()))) {
    827             beginResult = tempBegin;
    828             endResult = tempEnd;
    829             return;
    830         }
    831 
    832         beginAfter = tempEnd;
    833         lastIntervalTempEnd = tempEnd;
    834     }
    835     beginResult = SMILTime::unresolved();
    836     endResult = SMILTime::unresolved();
    837 }
    838 
    839 void SVGSMILElement::resolveFirstInterval()
    840 {
    841     SMILTime begin;
    842     SMILTime end;
    843     resolveInterval(true, begin, end);
    844     ASSERT(!begin.isIndefinite());
    845 
    846     if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {
    847         bool wasUnresolved = m_intervalBegin.isUnresolved();
    848         m_intervalBegin = begin;
    849         m_intervalEnd = end;
    850         notifyDependentsIntervalChanged(wasUnresolved ? NewInterval : ExistingInterval);
    851         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
    852 
    853         if (m_timeContainer)
    854             m_timeContainer->notifyIntervalsChanged();
    855     }
    856 }
    857 
    858 void SVGSMILElement::resolveNextInterval(bool notifyDependents)
    859 {
    860     SMILTime begin;
    861     SMILTime end;
    862     resolveInterval(false, begin, end);
    863     ASSERT(!begin.isIndefinite());
    864 
    865     if (!begin.isUnresolved() && begin != m_intervalBegin) {
    866         m_intervalBegin = begin;
    867         m_intervalEnd = end;
    868         if (notifyDependents)
    869             notifyDependentsIntervalChanged(NewInterval);
    870         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
    871     }
    872 }
    873 
    874 SMILTime SVGSMILElement::nextProgressTime() const
    875 {
    876     return m_nextProgressTime;
    877 }
    878 
    879 void SVGSMILElement::beginListChanged(SMILTime eventTime)
    880 {
    881     if (m_isWaitingForFirstInterval)
    882         resolveFirstInterval();
    883     else {
    884         SMILTime newBegin = findInstanceTime(Begin, eventTime, true);
    885         if (newBegin.isFinite() && (m_intervalEnd <= eventTime || newBegin < m_intervalBegin)) {
    886             // Begin time changed, re-resolve the interval.
    887             SMILTime oldBegin = m_intervalBegin;
    888             m_intervalEnd = eventTime;
    889             resolveInterval(false, m_intervalBegin, m_intervalEnd);
    890             ASSERT(!m_intervalBegin.isUnresolved());
    891             if (m_intervalBegin != oldBegin) {
    892                 if (m_activeState == Active && m_intervalBegin > eventTime) {
    893                     m_activeState = determineActiveState(eventTime);
    894                     if (m_activeState != Active)
    895                         endedActiveInterval();
    896                 }
    897                 notifyDependentsIntervalChanged(ExistingInterval);
    898             }
    899         }
    900     }
    901     m_nextProgressTime = elapsed();
    902 
    903     if (m_timeContainer)
    904         m_timeContainer->notifyIntervalsChanged();
    905 }
    906 
    907 void SVGSMILElement::endListChanged(SMILTime)
    908 {
    909     SMILTime elapsed = this->elapsed();
    910     if (m_isWaitingForFirstInterval)
    911         resolveFirstInterval();
    912     else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
    913         SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
    914         if (newEnd < m_intervalEnd) {
    915             newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
    916             if (newEnd != m_intervalEnd) {
    917                 m_intervalEnd = newEnd;
    918                 notifyDependentsIntervalChanged(ExistingInterval);
    919             }
    920         }
    921     }
    922     m_nextProgressTime = elapsed;
    923 
    924     if (m_timeContainer)
    925         m_timeContainer->notifyIntervalsChanged();
    926 }
    927 
    928 void SVGSMILElement::checkRestart(SMILTime elapsed)
    929 {
    930     ASSERT(!m_isWaitingForFirstInterval);
    931     ASSERT(elapsed >= m_intervalBegin);
    932 
    933     Restart restart = this->restart();
    934     if (restart == RestartNever)
    935         return;
    936 
    937     if (elapsed < m_intervalEnd) {
    938         if (restart != RestartAlways)
    939             return;
    940         SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
    941         if (nextBegin < m_intervalEnd) {
    942             m_intervalEnd = nextBegin;
    943             notifyDependentsIntervalChanged(ExistingInterval);
    944         }
    945     }
    946 
    947     if (elapsed >= m_intervalEnd)
    948         resolveNextInterval(true);
    949 }
    950 
    951 void SVGSMILElement::seekToIntervalCorrespondingToTime(SMILTime elapsed)
    952 {
    953     ASSERT(!m_isWaitingForFirstInterval);
    954     ASSERT(elapsed >= m_intervalBegin);
    955 
    956     // Manually seek from interval to interval, just as if the animation would run regulary.
    957     while (true) {
    958         // Figure out the next value in the begin time list after the current interval begin.
    959         SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
    960 
    961         // If the 'nextBegin' time is unresolved (eg. just one defined interval), we're done seeking.
    962         if (nextBegin.isUnresolved())
    963             return;
    964 
    965         // If the 'nextBegin' time is larger than or equal to the current interval end time, we're done seeking.
    966         // If the 'elapsed' time is smaller than the next begin interval time, we're done seeking.
    967         if (nextBegin < m_intervalEnd && elapsed >= nextBegin) {
    968             // End current interval, and start a new interval from the 'nextBegin' time.
    969             m_intervalEnd = nextBegin;
    970             resolveNextInterval(false);
    971             continue;
    972         }
    973 
    974         // If the desired 'elapsed' time is past the current interval, advance to the next.
    975         if (elapsed >= m_intervalEnd) {
    976             resolveNextInterval(false);
    977             continue;
    978         }
    979 
    980         return;
    981     }
    982 }
    983 
    984 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const
    985 {
    986     SMILTime simpleDuration = this->simpleDuration();
    987     repeat = 0;
    988     if (simpleDuration.isIndefinite()) {
    989         repeat = 0;
    990         return 0.f;
    991     }
    992     if (!simpleDuration) {
    993         repeat = 0;
    994         return 1.f;
    995     }
    996     ASSERT(m_intervalBegin.isFinite());
    997     ASSERT(simpleDuration.isFinite());
    998     SMILTime activeTime = elapsed - m_intervalBegin;
    999     SMILTime repeatingDuration = this->repeatingDuration();
   1000     if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
   1001         repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value());
   1002         if (!fmod(repeatingDuration.value(), simpleDuration.value()))
   1003             repeat--;
   1004 
   1005         double percent = (m_intervalEnd.value() - m_intervalBegin.value()) / simpleDuration.value();
   1006         percent = percent - floor(percent);
   1007         if (percent < numeric_limits<float>::epsilon() || 1 - percent < numeric_limits<float>::epsilon())
   1008             return 1.0f;
   1009         return narrowPrecisionToFloat(percent);
   1010     }
   1011     repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
   1012     SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
   1013     return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
   1014 }
   1015 
   1016 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
   1017 {
   1018     if (m_activeState == Active) {
   1019         // If duration is indefinite the value does not actually change over time. Same is true for <set>.
   1020         SMILTime simpleDuration = this->simpleDuration();
   1021         if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) {
   1022             SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
   1023             // We are supposed to do freeze semantics when repeating ends, even if the element is still active.
   1024             // Take care that we get a timer callback at that point.
   1025             if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite())
   1026                 return repeatingDurationEnd;
   1027             return m_intervalEnd;
   1028         }
   1029         return elapsed + 0.025;
   1030     }
   1031     return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
   1032 }
   1033 
   1034 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
   1035 {
   1036     if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd)
   1037         return Active;
   1038 
   1039     return fill() == FillFreeze ? Frozen : Inactive;
   1040 }
   1041 
   1042 bool SVGSMILElement::isContributing(SMILTime elapsed) const
   1043 {
   1044     // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
   1045     return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
   1046 }
   1047 
   1048 bool SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement, bool seekToTime)
   1049 {
   1050     ASSERT(resultElement);
   1051     ASSERT(m_timeContainer);
   1052     ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
   1053 
   1054     if (!m_conditionsConnected)
   1055         connectConditions();
   1056 
   1057     if (!m_intervalBegin.isFinite()) {
   1058         ASSERT(m_activeState == Inactive);
   1059         m_nextProgressTime = SMILTime::unresolved();
   1060         return false;
   1061     }
   1062 
   1063     if (elapsed < m_intervalBegin) {
   1064         ASSERT(m_activeState != Active);
   1065         if (m_activeState == Frozen) {
   1066             if (this == resultElement)
   1067                 resetAnimatedType();
   1068             updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
   1069         }
   1070         m_nextProgressTime = m_intervalBegin;
   1071         return false;
   1072     }
   1073 
   1074     m_previousIntervalBegin = m_intervalBegin;
   1075 
   1076     if (m_isWaitingForFirstInterval) {
   1077         m_isWaitingForFirstInterval = false;
   1078         resolveFirstInterval();
   1079     }
   1080 
   1081     // This call may obtain a new interval -- never call calculateAnimationPercentAndRepeat() before!
   1082     if (seekToTime) {
   1083         seekToIntervalCorrespondingToTime(elapsed);
   1084         if (elapsed < m_intervalBegin) {
   1085             // elapsed is not within an interval.
   1086             m_nextProgressTime = m_intervalBegin;
   1087             return false;
   1088         }
   1089     }
   1090 
   1091     unsigned repeat = 0;
   1092     float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
   1093     checkRestart(elapsed);
   1094 
   1095     ActiveState oldActiveState = m_activeState;
   1096     m_activeState = determineActiveState(elapsed);
   1097     bool animationIsContributing = isContributing(elapsed);
   1098 
   1099     // Only reset the animated type to the base value once for the lowest priority animation that animates and contributes to a particular element/attribute pair.
   1100     if (this == resultElement && animationIsContributing)
   1101         resetAnimatedType();
   1102 
   1103     if (animationIsContributing) {
   1104         if (oldActiveState == Inactive)
   1105             startedActiveInterval();
   1106 
   1107         updateAnimation(percent, repeat, resultElement);
   1108         m_lastPercent = percent;
   1109         m_lastRepeat = repeat;
   1110     }
   1111 
   1112     if (oldActiveState == Active && m_activeState != Active) {
   1113         endedActiveInterval();
   1114         if (m_activeState != Frozen && this == resultElement)
   1115             clearAnimatedType(m_targetElement);
   1116     }
   1117 
   1118     m_nextProgressTime = calculateNextProgressTime(elapsed);
   1119     return animationIsContributing;
   1120 }
   1121 
   1122 void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)
   1123 {
   1124     ASSERT(m_intervalBegin.isFinite());
   1125     DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ());
   1126     if (!loopBreaker.add(this).isNewEntry)
   1127         return;
   1128 
   1129     TimeDependentSet::iterator end = m_timeDependents.end();
   1130     for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) {
   1131         SVGSMILElement* dependent = *it;
   1132         dependent->createInstanceTimesFromSyncbase(this, newOrExisting);
   1133     }
   1134 
   1135     loopBreaker.remove(this);
   1136 }
   1137 
   1138 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval)
   1139 {
   1140     // FIXME: To be really correct, this should handle updating exising interval by changing
   1141     // the associated times instead of creating new ones.
   1142     for (unsigned n = 0; n < m_conditions.size(); ++n) {
   1143         Condition& condition = m_conditions[n];
   1144         if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
   1145             ASSERT(condition.m_name == "begin" || condition.m_name == "end");
   1146             // No nested time containers in SVG, no need for crazy time space conversions. Phew!
   1147             SMILTime time = 0;
   1148             if (condition.m_name == "begin")
   1149                 time = syncbase->m_intervalBegin + condition.m_offset;
   1150             else
   1151                 time = syncbase->m_intervalEnd + condition.m_offset;
   1152             ASSERT(time.isFinite());
   1153             if (condition.m_beginOrEnd == Begin)
   1154                 addBeginTime(elapsed(), time);
   1155             else
   1156                 addEndTime(elapsed(), time);
   1157         }
   1158     }
   1159 }
   1160 
   1161 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
   1162 {
   1163     m_timeDependents.add(animation);
   1164     if (m_intervalBegin.isFinite())
   1165         animation->createInstanceTimesFromSyncbase(this, NewInterval);
   1166 }
   1167 
   1168 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
   1169 {
   1170     m_timeDependents.remove(animation);
   1171 }
   1172 
   1173 void SVGSMILElement::handleConditionEvent(Event*, Condition* condition)
   1174 {
   1175     SMILTime elapsed = this->elapsed();
   1176     if (condition->m_beginOrEnd == Begin)
   1177         addBeginTime(elapsed, elapsed + condition->m_offset);
   1178     else
   1179         addEndTime(elapsed, elapsed + condition->m_offset);
   1180 }
   1181 
   1182 void SVGSMILElement::beginByLinkActivation()
   1183 {
   1184     SMILTime elapsed = this->elapsed();
   1185     addBeginTime(elapsed, elapsed);
   1186 }
   1187 
   1188 void SVGSMILElement::endedActiveInterval()
   1189 {
   1190     clearTimesWithDynamicOrigins(m_beginTimes);
   1191     clearTimesWithDynamicOrigins(m_endTimes);
   1192 }
   1193 
   1194 }
   1195