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