• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 
28 #if ENABLE(SVG_ANIMATION)
29 #include "SVGSMILElement.h"
30 
31 #include "CSSPropertyNames.h"
32 #include "Document.h"
33 #include "Event.h"
34 #include "EventListener.h"
35 #include "FloatConversion.h"
36 #include "FrameView.h"
37 #include "HTMLNames.h"
38 #include "MappedAttribute.h"
39 #include "SMILTimeContainer.h"
40 #include "SVGNames.h"
41 #include "SVGParserUtilities.h"
42 #include "SVGSVGElement.h"
43 #include "SVGURIReference.h"
44 #include "XLinkNames.h"
45 #include <math.h>
46 #include <wtf/MathExtras.h>
47 #include <wtf/StdLibExtras.h>
48 #include <wtf/Vector.h>
49 
50 using namespace std;
51 
52 namespace WebCore {
53 
54 // This is used for duration type time values that can't be negative.
55 static const double invalidCachedTime = -1.;
56 
57 class ConditionEventListener : public EventListener {
58 public:
create(SVGSMILElement * animation,Element * eventBase,SVGSMILElement::Condition * condition)59     static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, Element* eventBase, SVGSMILElement::Condition* condition)
60     {
61         return adoptRef(new ConditionEventListener(animation, eventBase, condition));
62     }
63 
unregister()64     void unregister()
65     {
66         // If this has only one ref then the event base is dead already and we don't need to remove ourself.
67         if (!hasOneRef())
68             m_eventBase->removeEventListener(m_condition->m_name, this, false);
69     }
70 
handleEvent(Event * event,bool)71     virtual void handleEvent(Event* event, bool)
72     {
73         m_animation->handleConditionEvent(event, m_condition);
74     }
75 
76 private:
ConditionEventListener(SVGSMILElement * animation,Element * eventBase,SVGSMILElement::Condition * condition)77     ConditionEventListener(SVGSMILElement* animation, Element* eventBase, SVGSMILElement::Condition* condition)
78         : m_animation(animation)
79         , m_condition(condition)
80         , m_eventBase(eventBase)
81     {
82         m_eventBase->addEventListener(m_condition->m_name, this, false);
83     }
84 
85     SVGSMILElement* m_animation;
86     SVGSMILElement::Condition* m_condition;
87     Element* m_eventBase;
88 };
89 
Condition(Type type,BeginOrEnd beginOrEnd,const String & baseID,const String & name,SMILTime offset,int repeats)90 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeats)
91     : m_type(type)
92     , m_beginOrEnd(beginOrEnd)
93     , m_baseID(baseID)
94     , m_name(name)
95     , m_offset(offset)
96     , m_repeats(repeats)
97 {
98 }
99 
SVGSMILElement(const QualifiedName & tagName,Document * doc)100 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document* doc)
101     : SVGElement(tagName, doc)
102     , m_conditionsConnected(false)
103     , m_hasEndEventConditions(false)
104     , m_intervalBegin(SMILTime::unresolved())
105     , m_intervalEnd(SMILTime::unresolved())
106     , m_previousIntervalBegin(SMILTime::unresolved())
107     , m_isWaitingForFirstInterval(true)
108     , m_activeState(Inactive)
109     , m_lastPercent(0)
110     , m_lastRepeat(0)
111     , m_nextProgressTime(0)
112     , m_documentOrderIndex(0)
113     , m_cachedDur(invalidCachedTime)
114     , m_cachedRepeatDur(invalidCachedTime)
115     , m_cachedRepeatCount(invalidCachedTime)
116     , m_cachedMin(invalidCachedTime)
117     , m_cachedMax(invalidCachedTime)
118 {
119 }
120 
~SVGSMILElement()121 SVGSMILElement::~SVGSMILElement()
122 {
123     disconnectConditions();
124     if (m_timeContainer)
125         m_timeContainer->unschedule(this);
126 }
127 
insertedIntoDocument()128 void SVGSMILElement::insertedIntoDocument()
129 {
130     SVGElement::insertedIntoDocument();
131 #ifndef NDEBUG
132     // Verify we are not in <use> instance tree.
133     for (Node* n = this; n; n = n->parent())
134         ASSERT(!n->isShadowNode());
135 #endif
136     SVGSVGElement* owner = ownerSVGElement();
137     if (!owner)
138         return;
139     m_timeContainer = owner->timeContainer();
140     ASSERT(m_timeContainer);
141     m_timeContainer->setDocumentOrderIndexesDirty();
142     reschedule();
143 }
144 
removedFromDocument()145 void SVGSMILElement::removedFromDocument()
146 {
147     if (m_timeContainer) {
148         m_timeContainer->unschedule(this);
149         m_timeContainer = 0;
150     }
151     // Calling disconnectConditions() may kill us if there are syncbase conditions.
152     // OK, but we don't want to die inside the call.
153     RefPtr<SVGSMILElement> keepAlive(this);
154     disconnectConditions();
155     SVGElement::removedFromDocument();
156 }
157 
finishParsingChildren()158 void SVGSMILElement::finishParsingChildren()
159 {
160     SVGElement::finishParsingChildren();
161 
162     // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
163     if (!hasAttribute(SVGNames::beginAttr))
164         m_beginTimes.append(0);
165 
166     if (m_isWaitingForFirstInterval) {
167         resolveFirstInterval();
168         reschedule();
169     }
170 }
171 
parseOffsetValue(const String & data)172 SMILTime SVGSMILElement::parseOffsetValue(const String& data)
173 {
174     bool ok;
175     double result = 0;
176     String parse = data.stripWhiteSpace();
177     if (parse.endsWith("h"))
178         result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
179     else if (parse.endsWith("min"))
180         result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
181     else if (parse.endsWith("ms"))
182         result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
183     else if (parse.endsWith("s"))
184         result = parse.left(parse.length() - 1).toDouble(&ok);
185     else
186         result = parse.toDouble(&ok);
187     if (!ok)
188         return SMILTime::unresolved();
189     return result;
190 }
191 
parseClockValue(const String & data)192 SMILTime SVGSMILElement::parseClockValue(const String& data)
193 {
194     if (data.isNull())
195         return SMILTime::unresolved();
196 
197     String parse = data.stripWhiteSpace();
198 
199     DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite"));
200     if (parse == indefiniteValue)
201         return SMILTime::indefinite();
202 
203     double result = 0;
204     bool ok;
205     int doublePointOne = parse.find(':');
206     int doublePointTwo = parse.find(':', doublePointOne + 1);
207     if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) {
208         result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
209         if (!ok)
210             return SMILTime::unresolved();
211         result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
212         if (!ok)
213             return SMILTime::unresolved();
214         result += parse.substring(6).toDouble(&ok);
215     } else if (doublePointOne == 2 && doublePointTwo == -1 && parse.length() >= 5) {
216         result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
217         if (!ok)
218             return SMILTime::unresolved();
219         result += parse.substring(3).toDouble(&ok);
220     } else
221         return parseOffsetValue(parse);
222 
223     if (!ok)
224         return SMILTime::unresolved();
225     return result;
226 }
227 
sortTimeList(Vector<SMILTime> & timeList)228 static void sortTimeList(Vector<SMILTime>& timeList)
229 {
230     std::sort(timeList.begin(), timeList.end());
231 }
232 
parseCondition(const String & value,BeginOrEnd beginOrEnd)233 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd)
234 {
235     String parseString = value.stripWhiteSpace();
236 
237     double sign = 1.;
238     bool ok;
239     int pos = parseString.find('+');
240     if (pos == -1) {
241         pos = parseString.find('-');
242         if (pos != -1)
243             sign = -1.;
244     }
245     String conditionString;
246     SMILTime offset = 0;
247     if (pos == -1)
248         conditionString = parseString;
249     else {
250         conditionString = parseString.left(pos).stripWhiteSpace();
251         String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
252         offset = parseOffsetValue(offsetString);
253         if (offset.isUnresolved())
254             return false;
255         offset = offset * sign;
256     }
257     if (conditionString.isEmpty())
258         return false;
259     pos = conditionString.find('.');
260 
261     String baseID;
262     String nameString;
263     if (pos == -1)
264         nameString = conditionString;
265     else {
266         baseID = conditionString.left(pos);
267         nameString = conditionString.substring(pos + 1);
268     }
269     if (nameString.isEmpty())
270         return false;
271 
272     Condition::Type type;
273     int repeats = -1;
274     if (nameString.startsWith("repeat(") && nameString.endsWith(")")) {
275         // FIXME: For repeat events we just need to add the data carrying TimeEvent class and
276         // fire the events at appropiate times.
277         repeats = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
278         if (!ok)
279             return false;
280         nameString = "repeat";
281         type = Condition::EventBase;
282     } else if (nameString == "begin" || nameString == "end") {
283         if (baseID.isEmpty())
284             return false;
285         type = Condition::Syncbase;
286     } else if (nameString.startsWith("accesskey(")) {
287         // FIXME: accesskey() support.
288         type = Condition::AccessKey;
289     } else
290         type = Condition::EventBase;
291 
292     m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeats));
293 
294     if (type == Condition::EventBase && beginOrEnd == End)
295         m_hasEndEventConditions = true;
296 
297     return true;
298 }
299 
isSMILElement(Node * node)300 bool SVGSMILElement::isSMILElement(Node* node)
301 {
302     if (!node)
303         return false;
304     return node->hasTagName(SVGNames::setTag) || node->hasTagName(SVGNames::animateTag) || node->hasTagName(SVGNames::animateMotionTag)
305             || node->hasTagName(SVGNames::animateTransformTag) || node->hasTagName(SVGNames::animateColorTag);
306 }
307 
parseBeginOrEnd(const String & parseString,BeginOrEnd beginOrEnd)308 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd)
309 {
310     Vector<SMILTime>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
311     if (beginOrEnd == End)
312         m_hasEndEventConditions = false;
313     HashSet<double> existing;
314     for (unsigned n = 0; n < timeList.size(); ++n)
315         existing.add(timeList[n].value());
316     Vector<String> splitString;
317     parseString.split(';', splitString);
318     for (unsigned n = 0; n < splitString.size(); ++n) {
319         SMILTime value = parseClockValue(splitString[n]);
320         if (value.isUnresolved())
321             parseCondition(splitString[n], beginOrEnd);
322         else if (!existing.contains(value.value()))
323             timeList.append(value);
324     }
325     sortTimeList(timeList);
326 }
327 
parseMappedAttribute(MappedAttribute * attr)328 void SVGSMILElement::parseMappedAttribute(MappedAttribute* attr)
329 {
330     if (attr->name() == SVGNames::beginAttr) {
331         if (!m_conditions.isEmpty()) {
332             disconnectConditions();
333             m_conditions.clear();
334             parseBeginOrEnd(getAttribute(SVGNames::endAttr), End);
335         }
336         parseBeginOrEnd(attr->value().string(), Begin);
337         if (inDocument())
338             connectConditions();
339     } else if (attr->name() == SVGNames::endAttr) {
340         if (!m_conditions.isEmpty()) {
341             disconnectConditions();
342             m_conditions.clear();
343             parseBeginOrEnd(getAttribute(SVGNames::beginAttr), Begin);
344         }
345         parseBeginOrEnd(attr->value().string(), End);
346         if (inDocument())
347             connectConditions();
348     } else
349         SVGElement::parseMappedAttribute(attr);
350 }
351 
attributeChanged(Attribute * attr,bool preserveDecls)352 void SVGSMILElement::attributeChanged(Attribute* attr, bool preserveDecls)
353 {
354     SVGElement::attributeChanged(attr, preserveDecls);
355 
356     const QualifiedName& attrName = attr->name();
357     if (attrName == SVGNames::durAttr)
358         m_cachedDur = invalidCachedTime;
359     else if (attrName == SVGNames::repeatDurAttr)
360         m_cachedRepeatDur = invalidCachedTime;
361     else if (attrName == SVGNames::repeatCountAttr)
362         m_cachedRepeatCount = invalidCachedTime;
363     else if (attrName == SVGNames::minAttr)
364         m_cachedMin = invalidCachedTime;
365     else if (attrName == SVGNames::maxAttr)
366         m_cachedMax = invalidCachedTime;
367 
368     if (inDocument()) {
369         if (attrName == SVGNames::beginAttr)
370             beginListChanged();
371         else if (attrName == SVGNames::endAttr)
372             endListChanged();
373     }
374 }
375 
connectConditions()376 void SVGSMILElement::connectConditions()
377 {
378     if (m_conditionsConnected)
379         disconnectConditions();
380     m_conditionsConnected = true;
381     for (unsigned n = 0; n < m_conditions.size(); ++n) {
382         Condition& condition = m_conditions[n];
383         if (condition.m_type == Condition::EventBase) {
384             ASSERT(!condition.m_syncbase);
385             Element* eventBase = condition.m_baseID.isEmpty() ? targetElement() : document()->getElementById(condition.m_baseID);
386             if (!eventBase)
387                 continue;
388             ASSERT(!condition.m_eventListener);
389             condition.m_eventListener = ConditionEventListener::create(this, eventBase, &condition);
390         } else if (condition.m_type == Condition::Syncbase) {
391             ASSERT(!condition.m_baseID.isEmpty());
392             condition.m_syncbase = document()->getElementById(condition.m_baseID);
393             if (!isSMILElement(condition.m_syncbase.get())) {
394                 condition.m_syncbase = 0;
395                 continue;
396             }
397             SVGSMILElement* syncbase = static_cast<SVGSMILElement*>(condition.m_syncbase.get());
398             syncbase->addTimeDependent(this);
399         }
400     }
401 }
402 
disconnectConditions()403 void SVGSMILElement::disconnectConditions()
404 {
405     if (!m_conditionsConnected)
406         return;
407     m_conditionsConnected = false;
408     for (unsigned n = 0; n < m_conditions.size(); ++n) {
409         Condition& condition = m_conditions[n];
410         if (condition.m_type == Condition::EventBase) {
411             ASSERT(!condition.m_syncbase);
412             if (condition.m_eventListener) {
413                 condition.m_eventListener->unregister();
414                 condition.m_eventListener = 0;
415             }
416         } else if (condition.m_type == Condition::Syncbase) {
417             if (condition.m_syncbase) {
418                 ASSERT(isSMILElement(condition.m_syncbase.get()));
419                 static_cast<SVGSMILElement*>(condition.m_syncbase.get())->removeTimeDependent(this);
420             }
421         }
422         condition.m_syncbase = 0;
423     }
424 }
425 
reschedule()426 void SVGSMILElement::reschedule()
427 {
428     if (m_timeContainer)
429         m_timeContainer->schedule(this);
430 }
431 
targetElement() const432 SVGElement* SVGSMILElement::targetElement() const
433 {
434     String href = xlinkHref();
435     Node* target = href.isEmpty() ? parentNode() : document()->getElementById(SVGURIReference::getTarget(href));
436     if (target && target->isSVGElement())
437         return static_cast<SVGElement*>(target);
438     return 0;
439 }
440 
attributeName() const441 String SVGSMILElement::attributeName() const
442 {
443     return getAttribute(SVGNames::attributeNameAttr).string().stripWhiteSpace();
444 }
445 
elapsed() const446 SMILTime SVGSMILElement::elapsed() const
447 {
448     return m_timeContainer ? m_timeContainer->elapsed() : 0;
449 }
450 
isInactive() const451 bool SVGSMILElement::isInactive() const
452 {
453      return m_activeState == Inactive;
454 }
455 
isFrozen() const456 bool SVGSMILElement::isFrozen() const
457 {
458     return m_activeState == Frozen;
459 }
460 
restart() const461 SVGSMILElement::Restart SVGSMILElement::restart() const
462 {
463     DEFINE_STATIC_LOCAL(const AtomicString, never, ("never"));
464     DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive"));
465     const AtomicString& value = getAttribute(SVGNames::restartAttr);
466     if (value == never)
467         return RestartNever;
468     if (value == whenNotActive)
469         return RestartWhenNotActive;
470     return RestartAlways;
471 }
472 
fill() const473 SVGSMILElement::FillMode SVGSMILElement::fill() const
474 {
475     DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze"));
476     const AtomicString& value = getAttribute(SVGNames::fillAttr);
477     return value == freeze ? FillFreeze : FillRemove;
478 }
479 
xlinkHref() const480 String SVGSMILElement::xlinkHref() const
481 {
482     return getAttribute(XLinkNames::hrefAttr);
483 }
484 
dur() const485 SMILTime SVGSMILElement::dur() const
486 {
487     if (m_cachedDur != invalidCachedTime)
488         return m_cachedDur;
489     const AtomicString& value = getAttribute(SVGNames::durAttr);
490     SMILTime clockValue = parseClockValue(value);
491     return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
492 }
493 
repeatDur() const494 SMILTime SVGSMILElement::repeatDur() const
495 {
496     if (m_cachedRepeatDur != invalidCachedTime)
497         return m_cachedRepeatDur;
498     const AtomicString& value = getAttribute(SVGNames::repeatDurAttr);
499     SMILTime clockValue = parseClockValue(value);
500     return m_cachedRepeatDur = clockValue < 0 ? SMILTime::unresolved() : clockValue;
501 }
502 
503 // So a count is not really a time but let just all pretend we did not notice.
repeatCount() const504 SMILTime SVGSMILElement::repeatCount() const
505 {
506     if (m_cachedRepeatCount != invalidCachedTime)
507         return m_cachedRepeatCount;
508     const AtomicString& value = getAttribute(SVGNames::repeatCountAttr);
509     if (value.isNull())
510         return SMILTime::unresolved();
511 
512     DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite"));
513     if (value == indefiniteValue)
514         return SMILTime::indefinite();
515     bool ok;
516     double result = value.string().toDouble(&ok);
517     return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved();
518 }
519 
maxValue() const520 SMILTime SVGSMILElement::maxValue() const
521 {
522     if (m_cachedMax != invalidCachedTime)
523         return m_cachedMax;
524     const AtomicString& value = getAttribute(SVGNames::maxAttr);
525     SMILTime result = parseClockValue(value);
526     return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result;
527 }
528 
minValue() const529 SMILTime SVGSMILElement::minValue() const
530 {
531     if (m_cachedMin != invalidCachedTime)
532         return m_cachedMin;
533     const AtomicString& value = getAttribute(SVGNames::minAttr);
534     SMILTime result = parseClockValue(value);
535     return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
536 }
537 
simpleDuration() const538 SMILTime SVGSMILElement::simpleDuration() const
539 {
540     return min(dur(), SMILTime::indefinite());
541 }
542 
addBeginTime(SMILTime time)543 void SVGSMILElement::addBeginTime(SMILTime time)
544 {
545     m_beginTimes.append(time);
546     sortTimeList(m_beginTimes);
547     beginListChanged();
548 }
549 
addEndTime(SMILTime time)550 void SVGSMILElement::addEndTime(SMILTime time)
551 {
552     m_endTimes.append(time);
553     sortTimeList(m_endTimes);
554     endListChanged();
555 }
556 
findInstanceTime(BeginOrEnd beginOrEnd,SMILTime minimumTime,bool equalsMinimumOK) const557 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
558 {
559     // FIXME: This searches from the beginning which is inefficient. The list is usually not long
560     // (one entry in common cases) but you can construct a case where it does grow.
561     const Vector<SMILTime>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
562     for (unsigned n = 0; n < list.size(); ++n) {
563         SMILTime time = list[n];
564         ASSERT(!time.isUnresolved());
565         if (time.isIndefinite() && beginOrEnd == Begin) {
566             // "The special value "indefinite" does not yield an instance time in the begin list."
567             continue;
568         }
569         if (equalsMinimumOK) {
570             if (time >= minimumTime)
571                 return time;
572         } else if (time > minimumTime)
573             return time;
574     }
575     return SMILTime::unresolved();
576 }
577 
repeatingDuration() const578 SMILTime SVGSMILElement::repeatingDuration() const
579 {
580     // Computing the active duration
581     // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
582     SMILTime repeatCount = this->repeatCount();
583     SMILTime repeatDur = this->repeatDur();
584     SMILTime simpleDuration = this->simpleDuration();
585     if (simpleDuration == 0 || (repeatDur.isUnresolved() && repeatCount.isUnresolved()))
586         return simpleDuration;
587     SMILTime repeatCountDuration = simpleDuration * repeatCount;
588     return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
589 }
590 
resolveActiveEnd(SMILTime resolvedBegin,SMILTime resolvedEnd) const591 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
592 {
593     // Computing the active duration
594     // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
595     SMILTime preliminaryActiveDuration;
596     if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
597         preliminaryActiveDuration = resolvedEnd - resolvedBegin;
598     else if (!resolvedEnd.isFinite())
599         preliminaryActiveDuration = repeatingDuration();
600     else
601         preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
602 
603     SMILTime minValue = this->minValue();
604     SMILTime maxValue = this->maxValue();
605     if (minValue > maxValue) {
606         // Ignore both.
607         // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
608         minValue = 0;
609         maxValue = SMILTime::indefinite();
610     }
611     return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
612 }
613 
resolveInterval(bool first,SMILTime & beginResult,SMILTime & endResult) const614 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const
615 {
616     // See the pseudocode in
617     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#Timing-BeginEnd-LifeCycle
618     SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
619     SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
620     while (true) {
621         SMILTime tempBegin = findInstanceTime(Begin, beginAfter, true);
622         if (tempBegin.isUnresolved())
623             break;
624         SMILTime tempEnd;
625         if (m_endTimes.isEmpty())
626             tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
627         else {
628             tempEnd = findInstanceTime(End, tempBegin, true);
629             if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd))
630                 tempEnd = findInstanceTime(End, tempBegin, false);
631             if (tempEnd.isUnresolved()) {
632                 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions)
633                     break;
634             }
635             tempEnd = resolveActiveEnd(tempBegin, tempEnd);
636         }
637         if (tempEnd > 0 || !first) {
638             beginResult = tempBegin;
639             endResult = tempEnd;
640             return;
641         } else if (restart() == RestartNever)
642             break;
643         else
644             beginAfter = tempEnd;
645         lastIntervalTempEnd = tempEnd;
646     }
647     beginResult = SMILTime::unresolved();
648     endResult = SMILTime::unresolved();
649 }
650 
resolveFirstInterval()651 void SVGSMILElement::resolveFirstInterval()
652 {
653     SMILTime begin;
654     SMILTime end;
655     resolveInterval(true, begin, end);
656     ASSERT(!begin.isIndefinite());
657 
658     if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {
659         bool wasUnresolved = m_intervalBegin.isUnresolved();
660         m_intervalBegin = begin;
661         m_intervalEnd = end;
662         notifyDependentsIntervalChanged(wasUnresolved ? NewInterval : ExistingInterval);
663         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
664         reschedule();
665     }
666 }
667 
resolveNextInterval()668 void SVGSMILElement::resolveNextInterval()
669 {
670     SMILTime begin;
671     SMILTime end;
672     resolveInterval(false, begin, end);
673     ASSERT(!begin.isIndefinite());
674 
675     if (!begin.isUnresolved() && begin != m_intervalBegin) {
676         m_intervalBegin = begin;
677         m_intervalEnd = end;
678         notifyDependentsIntervalChanged(NewInterval);
679         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
680     }
681 }
682 
nextProgressTime() const683 SMILTime SVGSMILElement::nextProgressTime() const
684 {
685     return m_nextProgressTime;
686 }
687 
beginListChanged()688 void SVGSMILElement::beginListChanged()
689 {
690     SMILTime elapsed = this->elapsed();
691     if (m_isWaitingForFirstInterval)
692         resolveFirstInterval();
693     else if (elapsed < m_intervalBegin) {
694         SMILTime newBegin = findInstanceTime(Begin, elapsed, false);
695         if (newBegin < m_intervalBegin) {
696             // Begin time changed, re-resolve the interval.
697             SMILTime oldBegin = m_intervalBegin;
698             m_intervalBegin = elapsed;
699             resolveInterval(false, m_intervalBegin, m_intervalEnd);
700             ASSERT(!m_intervalBegin.isUnresolved());
701             if (m_intervalBegin != oldBegin)
702                 notifyDependentsIntervalChanged(ExistingInterval);
703         }
704     }
705     m_nextProgressTime = elapsed;
706     reschedule();
707 }
708 
endListChanged()709 void SVGSMILElement::endListChanged()
710 {
711     SMILTime elapsed = this->elapsed();
712     if (m_isWaitingForFirstInterval)
713         resolveFirstInterval();
714     else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
715         SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
716         if (newEnd < m_intervalEnd) {
717             newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
718             if (newEnd != m_intervalEnd) {
719                 m_intervalEnd = newEnd;
720                 notifyDependentsIntervalChanged(ExistingInterval);
721             }
722         }
723     }
724     m_nextProgressTime = elapsed;
725     reschedule();
726 }
727 
checkRestart(SMILTime elapsed)728 void SVGSMILElement::checkRestart(SMILTime elapsed)
729 {
730     ASSERT(!m_isWaitingForFirstInterval);
731     ASSERT(elapsed >= m_intervalBegin);
732 
733     Restart restart = this->restart();
734     if (restart == RestartNever)
735         return;
736 
737     if (elapsed < m_intervalEnd) {
738         if (restart != RestartAlways)
739             return;
740         SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
741         if (nextBegin < m_intervalEnd) {
742             m_intervalEnd = nextBegin;
743             notifyDependentsIntervalChanged(ExistingInterval);
744         }
745     }
746     if (elapsed >= m_intervalEnd)
747         resolveNextInterval();
748 }
749 
calculateAnimationPercentAndRepeat(SMILTime elapsed,unsigned & repeat) const750 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const
751 {
752     SMILTime simpleDuration = this->simpleDuration();
753     repeat = 0;
754     if (simpleDuration.isIndefinite()) {
755         repeat = 0;
756         return 0.f;
757     }
758     if (simpleDuration == 0) {
759         repeat = 0;
760         return 1.f;
761     }
762     ASSERT(m_intervalBegin.isFinite());
763     ASSERT(simpleDuration.isFinite());
764     SMILTime activeTime = elapsed - m_intervalBegin;
765     SMILTime repeatingDuration = this->repeatingDuration();
766     if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
767         repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value());
768         if (fmod(repeatingDuration.value(), simpleDuration.value() == 0.))
769             repeat--;
770         return 1.f;
771     }
772     repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
773     SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
774     return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
775 }
776 
calculateNextProgressTime(SMILTime elapsed) const777 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
778 {
779     if (m_activeState == Active) {
780         // If duration is indefinite the value does not actually change over time. Same is true for <set>.
781         SMILTime simpleDuration = this->simpleDuration();
782         if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) {
783             SMILTime repeatCount = this->repeatCount();
784             SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
785             // We are supposed to do freeze semantics when repeating ends, even if the element is still active.
786             // Take care that we get a timer callback at that point.
787             if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite())
788                 return repeatingDurationEnd;
789             return m_intervalEnd;
790         }
791         return elapsed + 0.025;
792     }
793     return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
794 }
795 
determineActiveState(SMILTime elapsed) const796 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
797 {
798     if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd)
799         return Active;
800 
801     if (m_activeState == Active)
802         return fill() == FillFreeze ? Frozen : Inactive;
803 
804     return m_activeState;
805 }
806 
isContributing(SMILTime elapsed) const807 bool SVGSMILElement::isContributing(SMILTime elapsed) const
808 {
809     // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
810     return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
811 }
812 
progress(SMILTime elapsed,SVGSMILElement * resultElement)813 void SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement)
814 {
815     ASSERT(m_timeContainer);
816     ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
817 
818     if (!m_conditionsConnected)
819         connectConditions();
820 
821     if (!m_intervalBegin.isFinite()) {
822         ASSERT(m_activeState == Inactive);
823         m_nextProgressTime = SMILTime::unresolved();
824         return;
825     }
826 
827     if (elapsed < m_intervalBegin) {
828         ASSERT(m_activeState != Active);
829         if (m_activeState == Frozen && resultElement)
830             updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
831         m_nextProgressTime = m_intervalBegin;
832         return;
833     }
834 
835     m_previousIntervalBegin = m_intervalBegin;
836 
837     if (m_activeState == Inactive) {
838         m_isWaitingForFirstInterval = false;
839         m_activeState = Active;
840         startedActiveInterval();
841     }
842 
843     unsigned repeat;
844     float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
845 
846     checkRestart(elapsed);
847 
848     ActiveState oldActiveState = m_activeState;
849     m_activeState = determineActiveState(elapsed);
850 
851     if (isContributing(elapsed)) {
852         if (resultElement)
853             updateAnimation(percent, repeat, resultElement);
854         m_lastPercent = percent;
855         m_lastRepeat = repeat;
856     }
857 
858     if (oldActiveState == Active && m_activeState != Active)
859         endedActiveInterval();
860 
861     m_nextProgressTime = calculateNextProgressTime(elapsed);
862 }
863 
notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)864 void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)
865 {
866     ASSERT(m_intervalBegin.isFinite());
867     DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ());
868     if (loopBreaker.contains(this))
869         return;
870     loopBreaker.add(this);
871 
872     TimeDependentSet::iterator end = m_timeDependents.end();
873     for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) {
874         SVGSMILElement* dependent = *it;
875         dependent->createInstanceTimesFromSyncbase(this, newOrExisting);
876     }
877 
878     loopBreaker.remove(this);
879 }
880 
createInstanceTimesFromSyncbase(SVGSMILElement * syncbase,NewOrExistingInterval)881 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval)
882 {
883     // FIXME: To be really correct, this should handle updating exising interval by changing
884     // the associated times instead of creating new ones.
885     for (unsigned n = 0; n < m_conditions.size(); ++n) {
886         Condition& condition = m_conditions[n];
887         if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
888             ASSERT(condition.m_name == "begin" || condition.m_name == "end");
889             // No nested time containers in SVG, no need for crazy time space conversions. Phew!
890             SMILTime time = 0;
891             if (condition.m_name == "begin")
892                 time = syncbase->m_intervalBegin + condition.m_offset;
893             else
894                 time = syncbase->m_intervalEnd + condition.m_offset;
895             ASSERT(time.isFinite());
896             if (condition.m_beginOrEnd == Begin)
897                 addBeginTime(time);
898             else
899                 addEndTime(time);
900         }
901     }
902 }
903 
addTimeDependent(SVGSMILElement * animation)904 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
905 {
906     m_timeDependents.add(animation);
907     if (m_intervalBegin.isFinite())
908         animation->createInstanceTimesFromSyncbase(this, NewInterval);
909 }
910 
removeTimeDependent(SVGSMILElement * animation)911 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
912 {
913     m_timeDependents.remove(animation);
914 }
915 
handleConditionEvent(Event *,Condition * condition)916 void SVGSMILElement::handleConditionEvent(Event*, Condition* condition)
917 {
918     if (condition->m_beginOrEnd == Begin)
919         addBeginTime(elapsed() + condition->m_offset);
920     else
921         addEndTime(elapsed() + condition->m_offset);
922 }
923 
beginByLinkActivation()924 void SVGSMILElement::beginByLinkActivation()
925 {
926     addBeginTime(elapsed());
927 }
928 
929 }
930 
931 #endif
932 
933