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