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