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