• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
create(SVGSMILElement * animation,SVGSMILElement::Condition * condition)59     static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
60     {
61         return adoptRef(new ConditionEventListener(animation, condition));
62     }
63 
cast(const EventListener * listener)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 
disconnectAnimation()73     void disconnectAnimation()
74     {
75         m_animation = 0;
76     }
77 
78 private:
ConditionEventListener(SVGSMILElement * animation,SVGSMILElement::Condition * condition)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 
operator ==(const EventListener & listener)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 
handleEvent(ScriptExecutionContext *,Event * event)99 void ConditionEventListener::handleEvent(ScriptExecutionContext*, Event* event)
100 {
101     if (!m_animation)
102         return;
103     m_animation->handleConditionEvent(event, m_condition);
104 }
105 
Condition(Type type,BeginOrEnd beginOrEnd,const String & baseID,const String & name,SMILTime offset,int repeats)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 
SVGSMILElement(const QualifiedName & tagName,Document * doc)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 
~SVGSMILElement()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 
constructQualifiedName(const SVGElement * svgElement,const String & attributeName)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 
insertedIntoDocument()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 
removedFromDocument()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 
finishParsingChildren()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 
parseOffsetValue(const String & data)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 
parseClockValue(const String & data)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 
sortTimeList(Vector<SMILTime> & timeList)276 static void sortTimeList(Vector<SMILTime>& timeList)
277 {
278     std::sort(timeList.begin(), timeList.end());
279 }
280 
parseCondition(const String & value,BeginOrEnd beginOrEnd)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 
isSMILElement(Node * node)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 
parseBeginOrEnd(const String & parseString,BeginOrEnd beginOrEnd)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 
parseMappedAttribute(Attribute * attr)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 
attributeChanged(Attribute * attr,bool preserveDecls)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 
eventBaseFor(const Condition & condition) const428 inline Element* SVGSMILElement::eventBaseFor(const Condition& condition) const
429 {
430     return condition.m_baseID.isEmpty() ? targetElement() : document()->getElementById(condition.m_baseID);
431 }
432 
connectConditions()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 
disconnectConditions()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 
reschedule()492 void SVGSMILElement::reschedule()
493 {
494     if (m_timeContainer)
495         m_timeContainer->schedule(this);
496 }
497 
targetElement() const498 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 
elapsed() const513 SMILTime SVGSMILElement::elapsed() const
514 {
515     return m_timeContainer ? m_timeContainer->elapsed() : 0;
516 }
517 
isInactive() const518 bool SVGSMILElement::isInactive() const
519 {
520      return m_activeState == Inactive;
521 }
522 
isFrozen() const523 bool SVGSMILElement::isFrozen() const
524 {
525     return m_activeState == Frozen;
526 }
527 
restart() const528 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 
fill() const540 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 
xlinkHref() const547 String SVGSMILElement::xlinkHref() const
548 {
549     return getAttribute(XLinkNames::hrefAttr);
550 }
551 
dur() const552 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 
repeatDur() const561 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.
repeatCount() const572 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 
maxValue() const588 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 
minValue() const597 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 
simpleDuration() const606 SMILTime SVGSMILElement::simpleDuration() const
607 {
608     return min(dur(), SMILTime::indefinite());
609 }
610 
addBeginTime(SMILTime time)611 void SVGSMILElement::addBeginTime(SMILTime time)
612 {
613     m_beginTimes.append(time);
614     sortTimeList(m_beginTimes);
615     beginListChanged();
616 }
617 
addEndTime(SMILTime time)618 void SVGSMILElement::addEndTime(SMILTime time)
619 {
620     m_endTimes.append(time);
621     sortTimeList(m_endTimes);
622     endListChanged();
623 }
624 
findInstanceTime(BeginOrEnd beginOrEnd,SMILTime minimumTime,bool equalsMinimumOK) const625 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 
repeatingDuration() const646 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 
resolveActiveEnd(SMILTime resolvedBegin,SMILTime resolvedEnd) const659 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 
resolveInterval(bool first,SMILTime & beginResult,SMILTime & endResult) const682 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 
resolveFirstInterval()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 
resolveNextInterval()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 
nextProgressTime() const752 SMILTime SVGSMILElement::nextProgressTime() const
753 {
754     return m_nextProgressTime;
755 }
756 
beginListChanged()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 
endListChanged()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 
checkRestart(SMILTime elapsed)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 
calculateAnimationPercentAndRepeat(SMILTime elapsed,unsigned & repeat) const819 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 
calculateNextProgressTime(SMILTime elapsed) const846 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 
determineActiveState(SMILTime elapsed) const865 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 
isContributing(SMILTime elapsed) const876 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 
progress(SMILTime elapsed,SVGSMILElement * resultElement)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 
notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)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 
createInstanceTimesFromSyncbase(SVGSMILElement * syncbase,NewOrExistingInterval)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 
addTimeDependent(SVGSMILElement * animation)973 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
974 {
975     m_timeDependents.add(animation);
976     if (m_intervalBegin.isFinite())
977         animation->createInstanceTimesFromSyncbase(this, NewInterval);
978 }
979 
removeTimeDependent(SVGSMILElement * animation)980 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
981 {
982     m_timeDependents.remove(animation);
983 }
984 
handleConditionEvent(Event *,Condition * condition)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 
beginByLinkActivation()993 void SVGSMILElement::beginByLinkActivation()
994 {
995     addBeginTime(elapsed());
996 }
997 
998 }
999 
1000 #endif
1001