File indexing completed on 2024-05-12 15:39:12

0001 /*
0002  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
0003  *
0004  * Redistribution and use in source and binary forms, with or without
0005  * modification, are permitted provided that the following conditions
0006  * are met:
0007  * 1. Redistributions of source code must retain the above copyright
0008  *    notice, this list of conditions and the following disclaimer.
0009  * 2. Redistributions in binary form must reproduce the above copyright
0010  *    notice, this list of conditions and the following disclaimer in the
0011  *    documentation and/or other materials provided with the distribution.
0012  *
0013  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
0014  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0015  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0016  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
0017  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0018  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0019  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0020  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
0021  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0022  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0023  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0024  */
0025 
0026 #if ENABLE(SVG_ANIMATION)
0027 #include "SVGSMILElement.h"
0028 
0029 #include "CSSPropertyNames.h"
0030 #include "Document.h"
0031 #include "Event.h"
0032 #include "EventListener.h"
0033 #include "FloatConversion.h"
0034 #include "FrameView.h"
0035 #include "HTMLNames.h"
0036 #include "SVGNames.h"
0037 #include "SVGParserUtilities.h"
0038 #include "SVGSVGElement.h"
0039 #include "SVGURIReference.h"
0040 #include "SMILTimeContainer.h"
0041 #include "XLinkNames.h"
0042 #include <math.h>
0043 #include <wtf/MathExtras.h>
0044 #include <wtf/Vector.h>
0045 
0046 using namespace std;
0047 
0048 namespace WebCore
0049 {
0050 
0051 // This is used for duration type time values that can't be negative.
0052 static const double invalidCachedTime = -1.;
0053 
0054 class ConditionEventListener : public EventListener
0055 {
0056 public:
0057     ConditionEventListener(SVGSMILElement *animation, Element *eventBase, SVGSMILElement::Condition *condition)
0058         : m_animation(animation)
0059         , m_condition(condition)
0060         , m_eventBase(eventBase)
0061     {
0062         m_eventBase->addEventListener(m_condition->m_name, this, false);
0063     }
0064 
0065     void unregister()
0066     {
0067         // If this has only one ref then the event base is dead already and we don't need to remove ourself.
0068         if (!hasOneRef()) {
0069             m_eventBase->removeEventListener(m_condition->m_name, this, false);
0070         }
0071     }
0072 
0073     virtual void handleEvent(Event *event, bool isWindowEvent)
0074     {
0075         m_animation->handleConditionEvent(event, m_condition);
0076     }
0077 private:
0078     SVGSMILElement *m_animation;
0079     SVGSMILElement::Condition *m_condition;
0080     Element *m_eventBase;
0081 };
0082 
0083 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String &baseID, const String &name, SMILTime offset, int repeats)
0084     : m_type(type)
0085     , m_beginOrEnd(beginOrEnd)
0086     , m_baseID(baseID)
0087     , m_name(name)
0088     , m_offset(offset)
0089     , m_repeats(repeats)
0090 {
0091 }
0092 
0093 SVGSMILElement::SVGSMILElement(const QualifiedName &tagName, Document *doc)
0094     : SVGElement(tagName, doc)
0095     , m_conditionsConnected(false)
0096     , m_hasEndEventConditions(false)
0097     , m_intervalBegin(SMILTime::unresolved())
0098     , m_intervalEnd(SMILTime::unresolved())
0099     , m_previousIntervalBegin(SMILTime::unresolved())
0100     , m_isWaitingForFirstInterval(true)
0101     , m_activeState(Inactive)
0102     , m_lastPercent(0)
0103     , m_lastRepeat(0)
0104     , m_nextProgressTime(0)
0105     , m_documentOrderIndex(0)
0106     , m_cachedDur(invalidCachedTime)
0107     , m_cachedRepeatDur(invalidCachedTime)
0108     , m_cachedRepeatCount(invalidCachedTime)
0109     , m_cachedMin(invalidCachedTime)
0110     , m_cachedMax(invalidCachedTime)
0111 {
0112 }
0113 
0114 SVGSMILElement::~SVGSMILElement()
0115 {
0116     disconnectConditions();
0117     if (m_timeContainer) {
0118         m_timeContainer->unschedule(this);
0119     }
0120 }
0121 
0122 void SVGSMILElement::insertedIntoDocument()
0123 {
0124     SVGElement::insertedIntoDocument();
0125 #ifndef NDEBUG
0126     // Verify we are not in <use> instance tree.
0127     for (Node *n = this; n; n = n->parent()) {
0128         ASSERT(!n->isShadowNode());
0129     }
0130 #endif
0131     SVGSVGElement *owner = ownerSVGElement();
0132     if (!owner) {
0133         return;
0134     }
0135     m_timeContainer = owner->timeContainer();
0136     ASSERT(m_timeContainer);
0137     m_timeContainer->setDocumentOrderIndexesDirty();
0138     reschedule();
0139 }
0140 
0141 void SVGSMILElement::removedFromDocument()
0142 {
0143     if (m_timeContainer) {
0144         m_timeContainer->unschedule(this);
0145         m_timeContainer = 0;
0146     }
0147     // Calling disconnectConditions() may kill us if there are syncbase conditions.
0148     // OK, but we don't want to die inside the call.
0149     RefPtr<SVGSMILElement> keepAlive(this);
0150     disconnectConditions();
0151     SVGElement::removedFromDocument();
0152 }
0153 
0154 void SVGSMILElement::finishParsingChildren()
0155 {
0156     SVGElement::finishParsingChildren();
0157 
0158     // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
0159     if (!hasAttribute(SVGNames::beginAttr)) {
0160         m_beginTimes.append(0);
0161     }
0162 
0163     if (m_isWaitingForFirstInterval) {
0164         resolveFirstInterval();
0165         reschedule();
0166     }
0167 }
0168 
0169 SMILTime SVGSMILElement::parseOffsetValue(const String &data)
0170 {
0171     bool ok;
0172     double result = 0;
0173     String parse = data.stripWhiteSpace();
0174     if (parse.endsWith("h")) {
0175         result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
0176     } else if (parse.endsWith("min")) {
0177         result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
0178     } else if (parse.endsWith("ms")) {
0179         result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
0180     } else if (parse.endsWith("s")) {
0181         result = parse.left(parse.length() - 1).toDouble(&ok);
0182     } else {
0183         result = parse.toDouble(&ok);
0184     }
0185     if (!ok) {
0186         return SMILTime::unresolved();
0187     }
0188     return result;
0189 }
0190 
0191 SMILTime SVGSMILElement::parseClockValue(const String &data)
0192 {
0193     if (data.isNull()) {
0194         return SMILTime::unresolved();
0195     }
0196 
0197     String parse = data.stripWhiteSpace();
0198 
0199     static const AtomicString indefiniteValue("indefinite");
0200     if (parse == indefiniteValue) {
0201         return SMILTime::indefinite();
0202     }
0203 
0204     double result = 0;
0205     bool ok;
0206     int doublePointOne = parse.find(':');
0207     int doublePointTwo = parse.find(':', doublePointOne + 1);
0208     if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) {
0209         result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
0210         if (!ok) {
0211             return SMILTime::unresolved();
0212         }
0213         result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
0214         if (!ok) {
0215             return SMILTime::unresolved();
0216         }
0217         result += parse.substring(6).toDouble(&ok);
0218     } else if (doublePointOne == 2 && doublePointTwo == -1 && parse.length() >= 5) {
0219         result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
0220         if (!ok) {
0221             return SMILTime::unresolved();
0222         }
0223         result += parse.substring(3).toDouble(&ok);
0224     } else {
0225         return parseOffsetValue(parse);
0226     }
0227 
0228     if (!ok) {
0229         return SMILTime::unresolved();
0230     }
0231     return result;
0232 }
0233 
0234 static void sortTimeList(Vector<SMILTime> &timeList)
0235 {
0236     std::sort(timeList.begin(), timeList.end());
0237 }
0238 
0239 bool SVGSMILElement::parseCondition(const String &value, BeginOrEnd beginOrEnd)
0240 {
0241     String parseString = value.stripWhiteSpace();
0242 
0243     double sign = 1.;
0244     bool ok;
0245     int pos = parseString.find('+');
0246     if (pos == -1) {
0247         pos = parseString.find('-');
0248         if (pos != -1) {
0249             sign = -1.;
0250         }
0251     }
0252     String conditionString;
0253     SMILTime offset = 0;
0254     if (pos == -1) {
0255         conditionString = parseString;
0256     } else {
0257         conditionString = parseString.left(pos).stripWhiteSpace();
0258         String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
0259         offset = parseOffsetValue(offsetString);
0260         if (offset.isUnresolved()) {
0261             return false;
0262         }
0263         offset = offset * sign;
0264     }
0265     if (conditionString.isEmpty()) {
0266         return false;
0267     }
0268     pos = conditionString.find('.');
0269 
0270     String baseID;
0271     String nameString;
0272     if (pos == -1) {
0273         nameString = conditionString;
0274     } else {
0275         baseID = conditionString.left(pos);
0276         nameString = conditionString.substring(pos + 1);
0277     }
0278     if (nameString.isEmpty()) {
0279         return false;
0280     }
0281 
0282     Condition::Type type;
0283     int repeats = -1;
0284     if (nameString.startsWith("repeat(") && nameString.endsWith(")")) {
0285         // FIXME: For repeat events we just need to add the data carrying TimeEvent class and
0286         // fire the events at appropriate times.
0287         repeats = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
0288         if (!ok) {
0289             return false;
0290         }
0291         nameString = "repeat";
0292         type = Condition::EventBase;
0293     } else if (nameString == "begin" || nameString == "end") {
0294         if (baseID.isEmpty()) {
0295             return false;
0296         }
0297         type = Condition::Syncbase;
0298     } else if (nameString.startsWith("accesskey(")) {
0299         // FIXME: accesskey() support.
0300         type = Condition::AccessKey;
0301     } else {
0302         type = Condition::EventBase;
0303     }
0304 
0305     m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeats));
0306 
0307     if (type == Condition::EventBase && beginOrEnd == End) {
0308         m_hasEndEventConditions = true;
0309     }
0310 
0311     return true;
0312 }
0313 
0314 bool SVGSMILElement::isSMILElement(Node *node)
0315 {
0316     if (!node) {
0317         return false;
0318     }
0319     return node->hasTagName(SVGNames::setTag) || node->hasTagName(SVGNames::animateTag) || node->hasTagName(SVGNames::animateMotionTag)
0320            || node->hasTagName(SVGNames::animateTransformTag) || node->hasTagName(SVGNames::animateColorTag);
0321 }
0322 
0323 void SVGSMILElement::parseBeginOrEnd(const String &parseString, BeginOrEnd beginOrEnd)
0324 {
0325     Vector<SMILTime> &timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
0326     if (beginOrEnd == End) {
0327         m_hasEndEventConditions = false;
0328     }
0329     HashSet<double> existing;
0330     for (unsigned n = 0; n < timeList.size(); ++n) {
0331         existing.add(timeList[n].value());
0332     }
0333     Vector<String> splitString;
0334     parseString.split(';', splitString);
0335     for (unsigned n = 0; n < splitString.size(); ++n) {
0336         SMILTime value = parseClockValue(splitString[n]);
0337         if (value.isUnresolved()) {
0338             parseCondition(splitString[n], beginOrEnd);
0339         } else if (!existing.contains(value.value())) {
0340             timeList.append(value);
0341         }
0342     }
0343     sortTimeList(timeList);
0344 }
0345 
0346 void SVGSMILElement::parseMappedAttribute(MappedAttribute *attr)
0347 {
0348     if (attr->name() == SVGNames::beginAttr) {
0349         if (!m_conditions.isEmpty()) {
0350             disconnectConditions();
0351             m_conditions.clear();
0352             parseBeginOrEnd(getAttribute(SVGNames::endAttr), End);
0353         }
0354         parseBeginOrEnd(attr->value().string(), Begin);
0355         if (inDocument()) {
0356             connectConditions();
0357         }
0358     } else if (attr->name() == SVGNames::endAttr) {
0359         if (!m_conditions.isEmpty()) {
0360             disconnectConditions();
0361             m_conditions.clear();
0362             parseBeginOrEnd(getAttribute(SVGNames::beginAttr), Begin);
0363         }
0364         parseBeginOrEnd(attr->value().string(), End);
0365         if (inDocument()) {
0366             connectConditions();
0367         }
0368     } else {
0369         SVGElement::parseMappedAttribute(attr);
0370     }
0371 }
0372 
0373 void SVGSMILElement::attributeChanged(Attribute *attr, bool preserveDecls)
0374 {
0375     SVGElement::attributeChanged(attr, preserveDecls);
0376 
0377     const QualifiedName &attrName = attr->name();
0378     if (attrName == SVGNames::durAttr) {
0379         m_cachedDur = invalidCachedTime;
0380     } else if (attrName == SVGNames::repeatDurAttr) {
0381         m_cachedRepeatDur = invalidCachedTime;
0382     } else if (attrName == SVGNames::repeatCountAttr) {
0383         m_cachedRepeatCount = invalidCachedTime;
0384     } else if (attrName == SVGNames::minAttr) {
0385         m_cachedMin = invalidCachedTime;
0386     } else if (attrName == SVGNames::maxAttr) {
0387         m_cachedMax = invalidCachedTime;
0388     }
0389 
0390     if (inDocument()) {
0391         if (attrName == SVGNames::beginAttr) {
0392             beginListChanged();
0393         } else if (attrName == SVGNames::endAttr) {
0394             endListChanged();
0395         }
0396     }
0397 }
0398 
0399 void SVGSMILElement::connectConditions()
0400 {
0401     if (m_conditionsConnected) {
0402         disconnectConditions();
0403     }
0404     m_conditionsConnected = true;
0405     for (unsigned n = 0; n < m_conditions.size(); ++n) {
0406         Condition &condition = m_conditions[n];
0407         if (condition.m_type == Condition::EventBase) {
0408             ASSERT(!condition.m_syncbase);
0409             Element *eventBase = condition.m_baseID.isEmpty() ? targetElement() : document()->getElementById(condition.m_baseID);
0410             if (!eventBase) {
0411                 continue;
0412             }
0413             ASSERT(!condition.m_eventListener);
0414             condition.m_eventListener = new ConditionEventListener(this, eventBase, &condition);
0415         } else if (condition.m_type == Condition::Syncbase) {
0416             ASSERT(!condition.m_baseID.isEmpty());
0417             condition.m_syncbase = document()->getElementById(condition.m_baseID);
0418             if (!isSMILElement(condition.m_syncbase.get())) {
0419                 condition.m_syncbase = 0;
0420                 continue;
0421             }
0422             SVGSMILElement *syncbase = static_cast<SVGSMILElement *>(condition.m_syncbase.get());
0423             syncbase->addTimeDependent(this);
0424         }
0425     }
0426 }
0427 
0428 void SVGSMILElement::disconnectConditions()
0429 {
0430     if (!m_conditionsConnected) {
0431         return;
0432     }
0433     m_conditionsConnected = false;
0434     for (unsigned n = 0; n < m_conditions.size(); ++n) {
0435         Condition &condition = m_conditions[n];
0436         if (condition.m_type == Condition::EventBase) {
0437             ASSERT(!condition.m_syncbase);
0438             if (condition.m_eventListener) {
0439                 condition.m_eventListener->unregister();
0440                 condition.m_eventListener = 0;
0441             }
0442         } else if (condition.m_type == Condition::Syncbase) {
0443             if (condition.m_syncbase) {
0444                 ASSERT(isSMILElement(condition.m_syncbase.get()));
0445                 static_cast<SVGSMILElement *>(condition.m_syncbase.get())->removeTimeDependent(this);
0446             }
0447         }
0448         condition.m_syncbase = 0;
0449     }
0450 }
0451 
0452 void SVGSMILElement::reschedule()
0453 {
0454     if (m_timeContainer) {
0455         m_timeContainer->schedule(this);
0456     }
0457 }
0458 
0459 SVGElement *SVGSMILElement::targetElement() const
0460 {
0461     String href = xlinkHref();
0462     Node *target = href.isEmpty() ? parentNode() : document()->getElementById(SVGURIReference::getTarget(href));
0463     if (target && target->isSVGElement()) {
0464         return static_cast<SVGElement *>(target);
0465     }
0466     return 0;
0467 }
0468 
0469 String SVGSMILElement::attributeName() const
0470 {
0471     return getAttribute(SVGNames::attributeNameAttr).string().stripWhiteSpace();
0472 }
0473 
0474 SMILTime SVGSMILElement::elapsed() const
0475 {
0476     return m_timeContainer ? m_timeContainer->elapsed() : 0;
0477 }
0478 
0479 bool SVGSMILElement::isInactive() const
0480 {
0481     return m_activeState == Inactive;
0482 }
0483 
0484 bool SVGSMILElement::isFrozen() const
0485 {
0486     return m_activeState == Frozen;
0487 }
0488 
0489 SVGSMILElement::Restart SVGSMILElement::restart() const
0490 {
0491     static const AtomicString never("never");
0492     static const AtomicString whenNotActive("whenNotActive");
0493     const AtomicString &value = getAttribute(SVGNames::restartAttr);
0494     if (value == never) {
0495         return RestartNever;
0496     }
0497     if (value == whenNotActive) {
0498         return RestartWhenNotActive;
0499     }
0500     return RestartAlways;
0501 }
0502 
0503 SVGSMILElement::FillMode SVGSMILElement::fill() const
0504 {
0505     static const AtomicString freeze("freeze");
0506     const AtomicString &value = getAttribute(SVGNames::fillAttr);
0507     return value == freeze ? FillFreeze : FillRemove;
0508 }
0509 
0510 String SVGSMILElement::xlinkHref() const
0511 {
0512     return getAttribute(XLinkNames::hrefAttr);
0513 }
0514 
0515 SMILTime SVGSMILElement::dur() const
0516 {
0517     if (m_cachedDur != invalidCachedTime) {
0518         return m_cachedDur;
0519     }
0520     const AtomicString &value = getAttribute(SVGNames::durAttr);
0521     SMILTime clockValue = parseClockValue(value);
0522     return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
0523 }
0524 
0525 SMILTime SVGSMILElement::repeatDur() const
0526 {
0527     if (m_cachedRepeatDur != invalidCachedTime) {
0528         return m_cachedRepeatDur;
0529     }
0530     const AtomicString &value = getAttribute(SVGNames::repeatDurAttr);
0531     SMILTime clockValue = parseClockValue(value);
0532     return m_cachedRepeatDur = clockValue < 0 ? SMILTime::unresolved() : clockValue;
0533 }
0534 
0535 // So a count is not really a time but let just all pretend we did not notice.
0536 SMILTime SVGSMILElement::repeatCount() const
0537 {
0538     if (m_cachedRepeatCount != invalidCachedTime) {
0539         return m_cachedRepeatCount;
0540     }
0541     const AtomicString &value = getAttribute(SVGNames::repeatCountAttr);
0542     if (value.isNull()) {
0543         return SMILTime::unresolved();
0544     }
0545 
0546     static const AtomicString indefiniteValue("indefinite");
0547     if (value == indefiniteValue) {
0548         return SMILTime::indefinite();
0549     }
0550     bool ok;
0551     double result = value.string().toDouble(&ok);
0552     return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved();
0553 }
0554 
0555 SMILTime SVGSMILElement::maxValue() const
0556 {
0557     if (m_cachedMax != invalidCachedTime) {
0558         return m_cachedMax;
0559     }
0560     const AtomicString &value = getAttribute(SVGNames::maxAttr);
0561     SMILTime result = parseClockValue(value);
0562     return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result;
0563 }
0564 
0565 SMILTime SVGSMILElement::minValue() const
0566 {
0567     if (m_cachedMin != invalidCachedTime) {
0568         return m_cachedMin;
0569     }
0570     const AtomicString &value = getAttribute(SVGNames::minAttr);
0571     SMILTime result = parseClockValue(value);
0572     return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
0573 }
0574 
0575 SMILTime SVGSMILElement::simpleDuration() const
0576 {
0577     return min(dur(), SMILTime::indefinite());
0578 }
0579 
0580 void SVGSMILElement::addBeginTime(SMILTime time)
0581 {
0582     m_beginTimes.append(time);
0583     sortTimeList(m_beginTimes);
0584     beginListChanged();
0585 }
0586 
0587 void SVGSMILElement::addEndTime(SMILTime time)
0588 {
0589     m_endTimes.append(time);
0590     sortTimeList(m_endTimes);
0591     endListChanged();
0592 }
0593 
0594 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
0595 {
0596     // FIXME: This searches from the beginning which is inefficient. The list is usually not long
0597     // (one entry in common cases) but you can construct a case where it does grow.
0598     const Vector<SMILTime> &list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
0599     for (unsigned n = 0; n < list.size(); ++n) {
0600         SMILTime time = list[n];
0601         ASSERT(!time.isUnresolved());
0602         if (time.isIndefinite() && beginOrEnd == Begin) {
0603             // "The special value "indefinite" does not yield an instance time in the begin list."
0604             continue;
0605         }
0606         if (equalsMinimumOK) {
0607             if (time >= minimumTime) {
0608                 return time;
0609             }
0610         } else if (time > minimumTime) {
0611             return time;
0612         }
0613     }
0614     return SMILTime::unresolved();
0615 }
0616 
0617 SMILTime SVGSMILElement::repeatingDuration() const
0618 {
0619     // Computing the active duration
0620     // https://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
0621     SMILTime repeatCount = this->repeatCount();
0622     SMILTime repeatDur = this->repeatDur();
0623     SMILTime simpleDuration = this->simpleDuration();
0624     if (simpleDuration == 0 || (repeatDur.isUnresolved() && repeatCount.isUnresolved())) {
0625         return simpleDuration;
0626     }
0627     SMILTime repeatCountDuration = simpleDuration * repeatCount;
0628     return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
0629 }
0630 
0631 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
0632 {
0633     // Computing the active duration
0634     // https://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
0635     SMILTime preliminaryActiveDuration;
0636     if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved()) {
0637         preliminaryActiveDuration = resolvedEnd - resolvedBegin;
0638     } else if (!resolvedEnd.isFinite()) {
0639         preliminaryActiveDuration = repeatingDuration();
0640     } else {
0641         preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
0642     }
0643 
0644     SMILTime minValue = this->minValue();
0645     SMILTime maxValue = this->maxValue();
0646     if (minValue > maxValue) {
0647         // Ignore both.
0648         // https://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
0649         minValue = 0;
0650         maxValue = SMILTime::indefinite();
0651     }
0652     return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
0653 }
0654 
0655 void SVGSMILElement::resolveInterval(bool first, SMILTime &beginResult, SMILTime &endResult) const
0656 {
0657     // See the pseudocode in
0658     // https://www.w3.org/TR/2001/REC-smil-animation-20010904/#Timing-BeginEnd-LifeCycle
0659     SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
0660     SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
0661     while (true) {
0662         SMILTime tempBegin = findInstanceTime(Begin, beginAfter, true);
0663         if (tempBegin.isUnresolved()) {
0664             break;
0665         }
0666         SMILTime tempEnd;
0667         if (m_endTimes.isEmpty()) {
0668             tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
0669         } else {
0670             tempEnd = findInstanceTime(End, tempBegin, true);
0671             if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd)) {
0672                 tempEnd = findInstanceTime(End, tempBegin, false);
0673             }
0674             if (tempEnd.isUnresolved()) {
0675                 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions) {
0676                     break;
0677                 }
0678             }
0679             tempEnd = resolveActiveEnd(tempBegin, tempEnd);
0680         }
0681         if (tempEnd > 0 || !first) {
0682             beginResult = tempBegin;
0683             endResult = tempEnd;
0684             return;
0685         } else if (restart() == RestartNever) {
0686             break;
0687         } else {
0688             beginAfter = tempEnd;
0689         }
0690         lastIntervalTempEnd = tempEnd;
0691     }
0692     beginResult = SMILTime::unresolved();
0693     endResult = SMILTime::unresolved();
0694 }
0695 
0696 void SVGSMILElement::resolveFirstInterval()
0697 {
0698     SMILTime begin;
0699     SMILTime end;
0700     resolveInterval(true, begin, end);
0701     ASSERT(!begin.isIndefinite());
0702 
0703     if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {
0704         bool wasUnresolved = m_intervalBegin.isUnresolved();
0705         m_intervalBegin = begin;
0706         m_intervalEnd = end;
0707         notifyDependentsIntervalChanged(wasUnresolved ? NewInterval : ExistingInterval);
0708         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
0709         reschedule();
0710     }
0711 }
0712 
0713 void SVGSMILElement::resolveNextInterval()
0714 {
0715     SMILTime begin;
0716     SMILTime end;
0717     resolveInterval(false, begin, end);
0718     ASSERT(!begin.isIndefinite());
0719 
0720     if (!begin.isUnresolved() && begin != m_intervalBegin) {
0721         m_intervalBegin = begin;
0722         m_intervalEnd = end;
0723         notifyDependentsIntervalChanged(NewInterval);
0724         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
0725     }
0726 }
0727 
0728 SMILTime SVGSMILElement::nextProgressTime() const
0729 {
0730     return m_nextProgressTime;
0731 }
0732 
0733 void SVGSMILElement::beginListChanged()
0734 {
0735     SMILTime elapsed = this->elapsed();
0736     if (m_isWaitingForFirstInterval) {
0737         resolveFirstInterval();
0738     } else if (elapsed < m_intervalBegin) {
0739         SMILTime newBegin = findInstanceTime(Begin, elapsed, false);
0740         if (newBegin < m_intervalBegin) {
0741             // Begin time changed, re-resolve the interval.
0742             SMILTime oldBegin = m_intervalBegin;
0743             m_intervalBegin = elapsed;
0744             resolveInterval(false, m_intervalBegin, m_intervalEnd);
0745             ASSERT(!m_intervalBegin.isUnresolved());
0746             if (m_intervalBegin != oldBegin) {
0747                 notifyDependentsIntervalChanged(ExistingInterval);
0748             }
0749         }
0750     }
0751     m_nextProgressTime = elapsed;
0752     reschedule();
0753 }
0754 
0755 void SVGSMILElement::endListChanged()
0756 {
0757     SMILTime elapsed = this->elapsed();
0758     if (m_isWaitingForFirstInterval) {
0759         resolveFirstInterval();
0760     } else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
0761         SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
0762         if (newEnd < m_intervalEnd) {
0763             newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
0764             if (newEnd != m_intervalEnd) {
0765                 m_intervalEnd = newEnd;
0766                 notifyDependentsIntervalChanged(ExistingInterval);
0767             }
0768         }
0769     }
0770     m_nextProgressTime = elapsed;
0771     reschedule();
0772 }
0773 
0774 void SVGSMILElement::checkRestart(SMILTime elapsed)
0775 {
0776     ASSERT(!m_isWaitingForFirstInterval);
0777     ASSERT(elapsed >= m_intervalBegin);
0778 
0779     Restart restart = this->restart();
0780     if (restart == RestartNever) {
0781         return;
0782     }
0783 
0784     if (elapsed < m_intervalEnd) {
0785         if (restart != RestartAlways) {
0786             return;
0787         }
0788         SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
0789         if (nextBegin < m_intervalEnd) {
0790             m_intervalEnd = nextBegin;
0791             notifyDependentsIntervalChanged(ExistingInterval);
0792         }
0793     }
0794     if (elapsed >= m_intervalEnd) {
0795         resolveNextInterval();
0796     }
0797 }
0798 
0799 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned &repeat) const
0800 {
0801     SMILTime simpleDuration = this->simpleDuration();
0802     repeat = 0;
0803     if (simpleDuration.isIndefinite()) {
0804         repeat = 0;
0805         return 0.f;
0806     }
0807     if (simpleDuration == 0) {
0808         repeat = 0;
0809         return 1.f;
0810     }
0811     ASSERT(m_intervalBegin.isFinite());
0812     ASSERT(simpleDuration.isFinite());
0813     SMILTime activeTime = elapsed - m_intervalBegin;
0814     SMILTime repeatingDuration = this->repeatingDuration();
0815     if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
0816         repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value());
0817         if (fmod(repeatingDuration.value(), simpleDuration.value() == 0.)) {
0818             repeat--;
0819         }
0820         return 1.f;
0821     }
0822     repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
0823     SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
0824     return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
0825 }
0826 
0827 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
0828 {
0829     if (m_activeState == Active) {
0830         // If duration is indefinite the value does not actually change over time. Same is true for <set>.
0831         SMILTime simpleDuration = this->simpleDuration();
0832         if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) {
0833             SMILTime repeatCount = this->repeatCount();
0834             SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
0835             // We are supposed to do freeze semantics when repeating ends, even if the element is still active.
0836             // Take care that we get a timer callback at that point.
0837             if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite()) {
0838                 return repeatingDurationEnd;
0839             }
0840             return m_intervalEnd;
0841         }
0842         return elapsed + 0.025;
0843     }
0844     return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
0845 }
0846 
0847 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
0848 {
0849     if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd) {
0850         return Active;
0851     }
0852 
0853     if (m_activeState == Active) {
0854         return fill() == FillFreeze ? Frozen : Inactive;
0855     }
0856 
0857     return m_activeState;
0858 }
0859 
0860 bool SVGSMILElement::isContributing(SMILTime elapsed) const
0861 {
0862     // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
0863     return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
0864 }
0865 
0866 void SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement *resultElement)
0867 {
0868     ASSERT(m_timeContainer);
0869     ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
0870 
0871     if (!m_conditionsConnected) {
0872         connectConditions();
0873     }
0874 
0875     if (!m_intervalBegin.isFinite()) {
0876         ASSERT(m_activeState == Inactive);
0877         m_nextProgressTime = SMILTime::unresolved();
0878         return;
0879     }
0880 
0881     if (elapsed < m_intervalBegin) {
0882         ASSERT(m_activeState != Active);
0883         if (m_activeState == Frozen && resultElement) {
0884             updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
0885         }
0886         m_nextProgressTime = m_intervalBegin;
0887         return;
0888     }
0889 
0890     m_previousIntervalBegin = m_intervalBegin;
0891 
0892     if (m_activeState == Inactive) {
0893         m_isWaitingForFirstInterval = false;
0894         m_activeState = Active;
0895         startedActiveInterval();
0896     }
0897 
0898     unsigned repeat;
0899     float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
0900 
0901     checkRestart(elapsed);
0902 
0903     ActiveState oldActiveState = m_activeState;
0904     m_activeState = determineActiveState(elapsed);
0905 
0906     if (isContributing(elapsed)) {
0907         if (resultElement) {
0908             updateAnimation(percent, repeat, resultElement);
0909         }
0910         m_lastPercent = percent;
0911         m_lastRepeat = repeat;
0912     }
0913 
0914     if (oldActiveState == Active && m_activeState != Active) {
0915         endedActiveInterval();
0916     }
0917 
0918     m_nextProgressTime = calculateNextProgressTime(elapsed);
0919 }
0920 
0921 void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)
0922 {
0923     ASSERT(m_intervalBegin.isFinite());
0924     static HashSet<SVGSMILElement *> loopBreaker;
0925     if (loopBreaker.contains(this)) {
0926         return;
0927     }
0928     loopBreaker.add(this);
0929 
0930     TimeDependentSet::iterator end = m_timeDependents.end();
0931     for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) {
0932         SVGSMILElement *dependent = *it;
0933         dependent->createInstanceTimesFromSyncbase(this, newOrExisting);
0934     }
0935 
0936     loopBreaker.remove(this);
0937 }
0938 
0939 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement *syncbase, NewOrExistingInterval newOrExisting)
0940 {
0941     // FIXME: To be really correct, this should handle updating exising interval by changing
0942     // the associated times instead of creating new ones.
0943     for (unsigned n = 0; n < m_conditions.size(); ++n) {
0944         Condition &condition = m_conditions[n];
0945         if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
0946             ASSERT(condition.m_name == "begin" || condition.m_name == "end");
0947             // No nested time containers in SVG, no need for crazy time space conversions. Phew!
0948             SMILTime time = 0;
0949             if (condition.m_name == "begin") {
0950                 time = syncbase->m_intervalBegin + condition.m_offset;
0951             } else {
0952                 time = syncbase->m_intervalEnd + condition.m_offset;
0953             }
0954             ASSERT(time.isFinite());
0955             if (condition.m_beginOrEnd == Begin) {
0956                 addBeginTime(time);
0957             } else {
0958                 addEndTime(time);
0959             }
0960         }
0961     }
0962 }
0963 
0964 void SVGSMILElement::addTimeDependent(SVGSMILElement *animation)
0965 {
0966     m_timeDependents.add(animation);
0967     if (m_intervalBegin.isFinite()) {
0968         animation->createInstanceTimesFromSyncbase(this, NewInterval);
0969     }
0970 }
0971 
0972 void SVGSMILElement::removeTimeDependent(SVGSMILElement *animation)
0973 {
0974     m_timeDependents.remove(animation);
0975 }
0976 
0977 void SVGSMILElement::handleConditionEvent(Event *event, Condition *condition)
0978 {
0979     if (condition->m_beginOrEnd == Begin) {
0980         addBeginTime(elapsed() + condition->m_offset);
0981     } else {
0982         addEndTime(elapsed() + condition->m_offset);
0983     }
0984 }
0985 
0986 void SVGSMILElement::beginByLinkActivation()
0987 {
0988     addBeginTime(elapsed());
0989 }
0990 
0991 }
0992 
0993 #endif
0994