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