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