2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #if ENABLE(SVG_ANIMATION)
28 #include "SVGSMILElement.h"
30 #include "CSSPropertyNames.h"
33 #include "EventListener.h"
34 #include "FloatConversion.h"
35 #include "FrameView.h"
36 #include "HTMLNames.h"
38 #include "SVGParserUtilities.h"
39 #include "SVGSVGElement.h"
40 #include "SVGURIReference.h"
41 #include "SMILTimeContainer.h"
42 #include "XLinkNames.h"
44 #include <wtf/MathExtras.h>
45 #include <wtf/Vector.h>
51 // This is used for duration type time values that can't be negative.
52 static const double invalidCachedTime
= -1.;
54 class ConditionEventListener
: public EventListener
{
56 ConditionEventListener(SVGSMILElement
* animation
, Element
* eventBase
, SVGSMILElement::Condition
* condition
)
57 : m_animation(animation
)
58 , m_condition(condition
)
59 , m_eventBase(eventBase
)
61 m_eventBase
->addEventListener(m_condition
->m_name
, this, false);
66 // If this has only one ref then the event base is dead already and we don't need to remove ourself.
68 m_eventBase
->removeEventListener(m_condition
->m_name
, this, false);
71 virtual void handleEvent(Event
* event
, bool isWindowEvent
)
73 m_animation
->handleConditionEvent(event
, m_condition
);
76 SVGSMILElement
* m_animation
;
77 SVGSMILElement::Condition
* m_condition
;
81 SVGSMILElement::Condition::Condition(Type type
, BeginOrEnd beginOrEnd
, const String
& baseID
, const String
& name
, SMILTime offset
, int repeats
)
83 , m_beginOrEnd(beginOrEnd
)
91 SVGSMILElement::SVGSMILElement(const QualifiedName
& tagName
, Document
* doc
)
92 : SVGElement(tagName
, doc
)
93 , m_conditionsConnected(false)
94 , m_hasEndEventConditions(false)
95 , m_intervalBegin(SMILTime::unresolved())
96 , m_intervalEnd(SMILTime::unresolved())
97 , m_previousIntervalBegin(SMILTime::unresolved())
98 , m_isWaitingForFirstInterval(true)
99 , m_activeState(Inactive
)
102 , m_nextProgressTime(0)
103 , m_documentOrderIndex(0)
104 , m_cachedDur(invalidCachedTime
)
105 , m_cachedRepeatDur(invalidCachedTime
)
106 , m_cachedRepeatCount(invalidCachedTime
)
107 , m_cachedMin(invalidCachedTime
)
108 , m_cachedMax(invalidCachedTime
)
112 SVGSMILElement::~SVGSMILElement()
114 disconnectConditions();
116 m_timeContainer
->unschedule(this);
119 void SVGSMILElement::insertedIntoDocument()
121 SVGElement::insertedIntoDocument();
123 // Verify we are not in <use> instance tree.
124 for (Node
* n
= this; n
; n
= n
->parent())
125 ASSERT(!n
->isShadowNode());
127 SVGSVGElement
* owner
= ownerSVGElement();
130 m_timeContainer
= owner
->timeContainer();
131 ASSERT(m_timeContainer
);
132 m_timeContainer
->setDocumentOrderIndexesDirty();
136 void SVGSMILElement::removedFromDocument()
138 if (m_timeContainer
) {
139 m_timeContainer
->unschedule(this);
142 // Calling disconnectConditions() may kill us if there are syncbase conditions.
143 // OK, but we don't want to die inside the call.
144 RefPtr
<SVGSMILElement
> keepAlive(this);
145 disconnectConditions();
146 SVGElement::removedFromDocument();
149 void SVGSMILElement::finishParsingChildren()
151 SVGElement::finishParsingChildren();
153 // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
154 if (!hasAttribute(SVGNames::beginAttr
))
155 m_beginTimes
.append(0);
157 if (m_isWaitingForFirstInterval
) {
158 resolveFirstInterval();
163 SMILTime
SVGSMILElement::parseOffsetValue(const String
& data
)
167 String parse
= data
.stripWhiteSpace();
168 if (parse
.endsWith("h"))
169 result
= parse
.left(parse
.length() - 1).toDouble(&ok
) * 60 * 60;
170 else if (parse
.endsWith("min"))
171 result
= parse
.left(parse
.length() - 3).toDouble(&ok
) * 60;
172 else if (parse
.endsWith("ms"))
173 result
= parse
.left(parse
.length() - 2).toDouble(&ok
) / 1000;
174 else if (parse
.endsWith("s"))
175 result
= parse
.left(parse
.length() - 1).toDouble(&ok
);
177 result
= parse
.toDouble(&ok
);
179 return SMILTime::unresolved();
183 SMILTime
SVGSMILElement::parseClockValue(const String
& data
)
186 return SMILTime::unresolved();
188 String parse
= data
.stripWhiteSpace();
190 static const AtomicString
indefiniteValue("indefinite");
191 if (parse
== indefiniteValue
)
192 return SMILTime::indefinite();
196 int doublePointOne
= parse
.find(':');
197 int doublePointTwo
= parse
.find(':', doublePointOne
+ 1);
198 if (doublePointOne
== 2 && doublePointTwo
== 5 && parse
.length() >= 8) {
199 result
+= parse
.substring(0, 2).toUIntStrict(&ok
) * 60 * 60;
201 return SMILTime::unresolved();
202 result
+= parse
.substring(3, 2).toUIntStrict(&ok
) * 60;
204 return SMILTime::unresolved();
205 result
+= parse
.substring(6).toDouble(&ok
);
206 } else if (doublePointOne
== 2 && doublePointTwo
== -1 && parse
.length() >= 5) {
207 result
+= parse
.substring(0, 2).toUIntStrict(&ok
) * 60;
209 return SMILTime::unresolved();
210 result
+= parse
.substring(3).toDouble(&ok
);
212 return parseOffsetValue(parse
);
215 return SMILTime::unresolved();
219 static void sortTimeList(Vector
<SMILTime
>& timeList
)
221 std::sort(timeList
.begin(), timeList
.end());
224 bool SVGSMILElement::parseCondition(const String
& value
, BeginOrEnd beginOrEnd
)
226 String parseString
= value
.stripWhiteSpace();
230 int pos
= parseString
.find('+');
232 pos
= parseString
.find('-');
236 String conditionString
;
239 conditionString
= parseString
;
241 conditionString
= parseString
.left(pos
).stripWhiteSpace();
242 String offsetString
= parseString
.substring(pos
+ 1).stripWhiteSpace();
243 offset
= parseOffsetValue(offsetString
);
244 if (offset
.isUnresolved())
246 offset
= offset
* sign
;
248 if (conditionString
.isEmpty())
250 pos
= conditionString
.find('.');
255 nameString
= conditionString
;
257 baseID
= conditionString
.left(pos
);
258 nameString
= conditionString
.substring(pos
+ 1);
260 if (nameString
.isEmpty())
263 Condition::Type type
;
265 if (nameString
.startsWith("repeat(") && nameString
.endsWith(")")) {
266 // FIXME: For repeat events we just need to add the data carrying TimeEvent class and
267 // fire the events at appropriate times.
268 repeats
= nameString
.substring(7, nameString
.length() - 8).toUIntStrict(&ok
);
271 nameString
= "repeat";
272 type
= Condition::EventBase
;
273 } else if (nameString
== "begin" || nameString
== "end") {
274 if (baseID
.isEmpty())
276 type
= Condition::Syncbase
;
277 } else if (nameString
.startsWith("accesskey(")) {
278 // FIXME: accesskey() support.
279 type
= Condition::AccessKey
;
281 type
= Condition::EventBase
;
283 m_conditions
.append(Condition(type
, beginOrEnd
, baseID
, nameString
, offset
, repeats
));
285 if (type
== Condition::EventBase
&& beginOrEnd
== End
)
286 m_hasEndEventConditions
= true;
291 bool SVGSMILElement::isSMILElement(Node
* node
)
295 return node
->hasTagName(SVGNames::setTag
) || node
->hasTagName(SVGNames::animateTag
) || node
->hasTagName(SVGNames::animateMotionTag
)
296 || node
->hasTagName(SVGNames::animateTransformTag
) || node
->hasTagName(SVGNames::animateColorTag
);
299 void SVGSMILElement::parseBeginOrEnd(const String
& parseString
, BeginOrEnd beginOrEnd
)
301 Vector
<SMILTime
>& timeList
= beginOrEnd
== Begin
? m_beginTimes
: m_endTimes
;
302 if (beginOrEnd
== End
)
303 m_hasEndEventConditions
= false;
304 HashSet
<double> existing
;
305 for (unsigned n
= 0; n
< timeList
.size(); ++n
)
306 existing
.add(timeList
[n
].value());
307 Vector
<String
> splitString
;
308 parseString
.split(';', splitString
);
309 for (unsigned n
= 0; n
< splitString
.size(); ++n
) {
310 SMILTime value
= parseClockValue(splitString
[n
]);
311 if (value
.isUnresolved())
312 parseCondition(splitString
[n
], beginOrEnd
);
313 else if (!existing
.contains(value
.value()))
314 timeList
.append(value
);
316 sortTimeList(timeList
);
319 void SVGSMILElement::parseMappedAttribute(MappedAttribute
* attr
)
321 if (attr
->name() == SVGNames::beginAttr
) {
322 if (!m_conditions
.isEmpty()) {
323 disconnectConditions();
324 m_conditions
.clear();
325 parseBeginOrEnd(getAttribute(SVGNames::endAttr
), End
);
327 parseBeginOrEnd(attr
->value().string(), Begin
);
330 } else if (attr
->name() == SVGNames::endAttr
) {
331 if (!m_conditions
.isEmpty()) {
332 disconnectConditions();
333 m_conditions
.clear();
334 parseBeginOrEnd(getAttribute(SVGNames::beginAttr
), Begin
);
336 parseBeginOrEnd(attr
->value().string(), End
);
340 SVGElement::parseMappedAttribute(attr
);
343 void SVGSMILElement::attributeChanged(Attribute
* attr
, bool preserveDecls
)
345 SVGElement::attributeChanged(attr
, preserveDecls
);
347 const QualifiedName
& attrName
= attr
->name();
348 if (attrName
== SVGNames::durAttr
)
349 m_cachedDur
= invalidCachedTime
;
350 else if (attrName
== SVGNames::repeatDurAttr
)
351 m_cachedRepeatDur
= invalidCachedTime
;
352 else if (attrName
== SVGNames::repeatCountAttr
)
353 m_cachedRepeatCount
= invalidCachedTime
;
354 else if (attrName
== SVGNames::minAttr
)
355 m_cachedMin
= invalidCachedTime
;
356 else if (attrName
== SVGNames::maxAttr
)
357 m_cachedMax
= invalidCachedTime
;
360 if (attrName
== SVGNames::beginAttr
)
362 else if (attrName
== SVGNames::endAttr
)
367 void SVGSMILElement::connectConditions()
369 if (m_conditionsConnected
)
370 disconnectConditions();
371 m_conditionsConnected
= true;
372 for (unsigned n
= 0; n
< m_conditions
.size(); ++n
) {
373 Condition
& condition
= m_conditions
[n
];
374 if (condition
.m_type
== Condition::EventBase
) {
375 ASSERT(!condition
.m_syncbase
);
376 Element
* eventBase
= condition
.m_baseID
.isEmpty() ? targetElement() : document()->getElementById(condition
.m_baseID
);
379 ASSERT(!condition
.m_eventListener
);
380 condition
.m_eventListener
= new ConditionEventListener(this, eventBase
, &condition
);
381 } else if (condition
.m_type
== Condition::Syncbase
) {
382 ASSERT(!condition
.m_baseID
.isEmpty());
383 condition
.m_syncbase
= document()->getElementById(condition
.m_baseID
);
384 if (!isSMILElement(condition
.m_syncbase
.get())) {
385 condition
.m_syncbase
= 0;
388 SVGSMILElement
* syncbase
= static_cast<SVGSMILElement
*>(condition
.m_syncbase
.get());
389 syncbase
->addTimeDependent(this);
394 void SVGSMILElement::disconnectConditions()
396 if (!m_conditionsConnected
)
398 m_conditionsConnected
= false;
399 for (unsigned n
= 0; n
< m_conditions
.size(); ++n
) {
400 Condition
& condition
= m_conditions
[n
];
401 if (condition
.m_type
== Condition::EventBase
) {
402 ASSERT(!condition
.m_syncbase
);
403 if (condition
.m_eventListener
) {
404 condition
.m_eventListener
->unregister();
405 condition
.m_eventListener
= 0;
407 } else if (condition
.m_type
== Condition::Syncbase
) {
408 if (condition
.m_syncbase
) {
409 ASSERT(isSMILElement(condition
.m_syncbase
.get()));
410 static_cast<SVGSMILElement
*>(condition
.m_syncbase
.get())->removeTimeDependent(this);
413 condition
.m_syncbase
= 0;
417 void SVGSMILElement::reschedule()
420 m_timeContainer
->schedule(this);
423 SVGElement
* SVGSMILElement::targetElement() const
425 String href
= xlinkHref();
426 Node
* target
= href
.isEmpty() ? parentNode() : document()->getElementById(SVGURIReference::getTarget(href
));
427 if (target
&& target
->isSVGElement())
428 return static_cast<SVGElement
*>(target
);
432 String
SVGSMILElement::attributeName() const
434 return getAttribute(SVGNames::attributeNameAttr
).string().stripWhiteSpace();
437 SMILTime
SVGSMILElement::elapsed() const
439 return m_timeContainer
? m_timeContainer
->elapsed() : 0;
442 bool SVGSMILElement::isInactive() const
444 return m_activeState
== Inactive
;
447 bool SVGSMILElement::isFrozen() const
449 return m_activeState
== Frozen
;
452 SVGSMILElement::Restart
SVGSMILElement::restart() const
454 static const AtomicString
never("never");
455 static const AtomicString
whenNotActive("whenNotActive");
456 const AtomicString
& value
= getAttribute(SVGNames::restartAttr
);
459 if (value
== whenNotActive
)
460 return RestartWhenNotActive
;
461 return RestartAlways
;
464 SVGSMILElement::FillMode
SVGSMILElement::fill() const
466 static const AtomicString
freeze("freeze");
467 const AtomicString
& value
= getAttribute(SVGNames::fillAttr
);
468 return value
== freeze
? FillFreeze
: FillRemove
;
471 String
SVGSMILElement::xlinkHref() const
473 return getAttribute(XLinkNames::hrefAttr
);
476 SMILTime
SVGSMILElement::dur() const
478 if (m_cachedDur
!= invalidCachedTime
)
480 const AtomicString
& value
= getAttribute(SVGNames::durAttr
);
481 SMILTime clockValue
= parseClockValue(value
);
482 return m_cachedDur
= clockValue
<= 0 ? SMILTime::unresolved() : clockValue
;
485 SMILTime
SVGSMILElement::repeatDur() const
487 if (m_cachedRepeatDur
!= invalidCachedTime
)
488 return m_cachedRepeatDur
;
489 const AtomicString
& value
= getAttribute(SVGNames::repeatDurAttr
);
490 SMILTime clockValue
= parseClockValue(value
);
491 return m_cachedRepeatDur
= clockValue
< 0 ? SMILTime::unresolved() : clockValue
;
494 // So a count is not really a time but let just all pretend we did not notice.
495 SMILTime
SVGSMILElement::repeatCount() const
497 if (m_cachedRepeatCount
!= invalidCachedTime
)
498 return m_cachedRepeatCount
;
499 const AtomicString
& value
= getAttribute(SVGNames::repeatCountAttr
);
501 return SMILTime::unresolved();
503 static const AtomicString
indefiniteValue("indefinite");
504 if (value
== indefiniteValue
)
505 return SMILTime::indefinite();
507 double result
= value
.string().toDouble(&ok
);
508 return m_cachedRepeatCount
= ok
&& result
> 0 ? result
: SMILTime::unresolved();
511 SMILTime
SVGSMILElement::maxValue() const
513 if (m_cachedMax
!= invalidCachedTime
)
515 const AtomicString
& value
= getAttribute(SVGNames::maxAttr
);
516 SMILTime result
= parseClockValue(value
);
517 return m_cachedMax
= (result
.isUnresolved() || result
< 0) ? SMILTime::indefinite() : result
;
520 SMILTime
SVGSMILElement::minValue() const
522 if (m_cachedMin
!= invalidCachedTime
)
524 const AtomicString
& value
= getAttribute(SVGNames::minAttr
);
525 SMILTime result
= parseClockValue(value
);
526 return m_cachedMin
= (result
.isUnresolved() || result
< 0) ? 0 : result
;
529 SMILTime
SVGSMILElement::simpleDuration() const
531 return min(dur(), SMILTime::indefinite());
534 void SVGSMILElement::addBeginTime(SMILTime time
)
536 m_beginTimes
.append(time
);
537 sortTimeList(m_beginTimes
);
541 void SVGSMILElement::addEndTime(SMILTime time
)
543 m_endTimes
.append(time
);
544 sortTimeList(m_endTimes
);
548 SMILTime
SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd
, SMILTime minimumTime
, bool equalsMinimumOK
) const
550 // FIXME: This searches from the beginning which is inefficient. The list is usually not long
551 // (one entry in common cases) but you can construct a case where it does grow.
552 const Vector
<SMILTime
>& list
= beginOrEnd
== Begin
? m_beginTimes
: m_endTimes
;
553 for (unsigned n
= 0; n
< list
.size(); ++n
) {
554 SMILTime time
= list
[n
];
555 ASSERT(!time
.isUnresolved());
556 if (time
.isIndefinite() && beginOrEnd
== Begin
) {
557 // "The special value "indefinite" does not yield an instance time in the begin list."
560 if (equalsMinimumOK
) {
561 if (time
>= minimumTime
)
563 } else if (time
> minimumTime
)
566 return SMILTime::unresolved();
569 SMILTime
SVGSMILElement::repeatingDuration() const
571 // Computing the active duration
572 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
573 SMILTime repeatCount
= this->repeatCount();
574 SMILTime repeatDur
= this->repeatDur();
575 SMILTime simpleDuration
= this->simpleDuration();
576 if (simpleDuration
== 0 || (repeatDur
.isUnresolved() && repeatCount
.isUnresolved()))
577 return simpleDuration
;
578 SMILTime repeatCountDuration
= simpleDuration
* repeatCount
;
579 return min(repeatCountDuration
, min(repeatDur
, SMILTime::indefinite()));
582 SMILTime
SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin
, SMILTime resolvedEnd
) const
584 // Computing the active duration
585 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
586 SMILTime preliminaryActiveDuration
;
587 if (!resolvedEnd
.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
588 preliminaryActiveDuration
= resolvedEnd
- resolvedBegin
;
589 else if (!resolvedEnd
.isFinite())
590 preliminaryActiveDuration
= repeatingDuration();
592 preliminaryActiveDuration
= min(repeatingDuration(), resolvedEnd
- resolvedBegin
);
594 SMILTime minValue
= this->minValue();
595 SMILTime maxValue
= this->maxValue();
596 if (minValue
> maxValue
) {
598 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
600 maxValue
= SMILTime::indefinite();
602 return resolvedBegin
+ min(maxValue
, max(minValue
, preliminaryActiveDuration
));
605 void SVGSMILElement::resolveInterval(bool first
, SMILTime
& beginResult
, SMILTime
& endResult
) const
607 // See the pseudocode in
608 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#Timing-BeginEnd-LifeCycle
609 SMILTime beginAfter
= first
? -numeric_limits
<double>::infinity() : m_intervalEnd
;
610 SMILTime lastIntervalTempEnd
= numeric_limits
<double>::infinity();
612 SMILTime tempBegin
= findInstanceTime(Begin
, beginAfter
, true);
613 if (tempBegin
.isUnresolved())
616 if (m_endTimes
.isEmpty())
617 tempEnd
= resolveActiveEnd(tempBegin
, SMILTime::indefinite());
619 tempEnd
= findInstanceTime(End
, tempBegin
, true);
620 if ((first
&& tempBegin
== tempEnd
&& tempEnd
== lastIntervalTempEnd
) || (!first
&& tempEnd
== m_intervalEnd
))
621 tempEnd
= findInstanceTime(End
, tempBegin
, false);
622 if (tempEnd
.isUnresolved()) {
623 if (!m_endTimes
.isEmpty() && !m_hasEndEventConditions
)
626 tempEnd
= resolveActiveEnd(tempBegin
, tempEnd
);
628 if (tempEnd
> 0 || !first
) {
629 beginResult
= tempBegin
;
632 } else if (restart() == RestartNever
)
635 beginAfter
= tempEnd
;
636 lastIntervalTempEnd
= tempEnd
;
638 beginResult
= SMILTime::unresolved();
639 endResult
= SMILTime::unresolved();
642 void SVGSMILElement::resolveFirstInterval()
646 resolveInterval(true, begin
, end
);
647 ASSERT(!begin
.isIndefinite());
649 if (!begin
.isUnresolved() && (begin
!= m_intervalBegin
|| end
!= m_intervalEnd
)) {
650 bool wasUnresolved
= m_intervalBegin
.isUnresolved();
651 m_intervalBegin
= begin
;
653 notifyDependentsIntervalChanged(wasUnresolved
? NewInterval
: ExistingInterval
);
654 m_nextProgressTime
= min(m_nextProgressTime
, m_intervalBegin
);
659 void SVGSMILElement::resolveNextInterval()
663 resolveInterval(false, begin
, end
);
664 ASSERT(!begin
.isIndefinite());
666 if (!begin
.isUnresolved() && begin
!= m_intervalBegin
) {
667 m_intervalBegin
= begin
;
669 notifyDependentsIntervalChanged(NewInterval
);
670 m_nextProgressTime
= min(m_nextProgressTime
, m_intervalBegin
);
674 SMILTime
SVGSMILElement::nextProgressTime() const
676 return m_nextProgressTime
;
679 void SVGSMILElement::beginListChanged()
681 SMILTime elapsed
= this->elapsed();
682 if (m_isWaitingForFirstInterval
)
683 resolveFirstInterval();
684 else if (elapsed
< m_intervalBegin
) {
685 SMILTime newBegin
= findInstanceTime(Begin
, elapsed
, false);
686 if (newBegin
< m_intervalBegin
) {
687 // Begin time changed, re-resolve the interval.
688 SMILTime oldBegin
= m_intervalBegin
;
689 m_intervalBegin
= elapsed
;
690 resolveInterval(false, m_intervalBegin
, m_intervalEnd
);
691 ASSERT(!m_intervalBegin
.isUnresolved());
692 if (m_intervalBegin
!= oldBegin
)
693 notifyDependentsIntervalChanged(ExistingInterval
);
696 m_nextProgressTime
= elapsed
;
700 void SVGSMILElement::endListChanged()
702 SMILTime elapsed
= this->elapsed();
703 if (m_isWaitingForFirstInterval
)
704 resolveFirstInterval();
705 else if (elapsed
< m_intervalEnd
&& m_intervalBegin
.isFinite()) {
706 SMILTime newEnd
= findInstanceTime(End
, m_intervalBegin
, false);
707 if (newEnd
< m_intervalEnd
) {
708 newEnd
= resolveActiveEnd(m_intervalBegin
, newEnd
);
709 if (newEnd
!= m_intervalEnd
) {
710 m_intervalEnd
= newEnd
;
711 notifyDependentsIntervalChanged(ExistingInterval
);
715 m_nextProgressTime
= elapsed
;
719 void SVGSMILElement::checkRestart(SMILTime elapsed
)
721 ASSERT(!m_isWaitingForFirstInterval
);
722 ASSERT(elapsed
>= m_intervalBegin
);
724 Restart restart
= this->restart();
725 if (restart
== RestartNever
)
728 if (elapsed
< m_intervalEnd
) {
729 if (restart
!= RestartAlways
)
731 SMILTime nextBegin
= findInstanceTime(Begin
, m_intervalBegin
, false);
732 if (nextBegin
< m_intervalEnd
) {
733 m_intervalEnd
= nextBegin
;
734 notifyDependentsIntervalChanged(ExistingInterval
);
737 if (elapsed
>= m_intervalEnd
)
738 resolveNextInterval();
741 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed
, unsigned& repeat
) const
743 SMILTime simpleDuration
= this->simpleDuration();
745 if (simpleDuration
.isIndefinite()) {
749 if (simpleDuration
== 0) {
753 ASSERT(m_intervalBegin
.isFinite());
754 ASSERT(simpleDuration
.isFinite());
755 SMILTime activeTime
= elapsed
- m_intervalBegin
;
756 SMILTime repeatingDuration
= this->repeatingDuration();
757 if (elapsed
>= m_intervalEnd
|| activeTime
> repeatingDuration
) {
758 repeat
= static_cast<unsigned>(repeatingDuration
.value() / simpleDuration
.value());
759 if (fmod(repeatingDuration
.value(), simpleDuration
.value() == 0.))
763 repeat
= static_cast<unsigned>(activeTime
.value() / simpleDuration
.value());
764 SMILTime simpleTime
= fmod(activeTime
.value(), simpleDuration
.value());
765 return narrowPrecisionToFloat(simpleTime
.value() / simpleDuration
.value());
768 SMILTime
SVGSMILElement::calculateNextProgressTime(SMILTime elapsed
) const
770 if (m_activeState
== Active
) {
771 // If duration is indefinite the value does not actually change over time. Same is true for <set>.
772 SMILTime simpleDuration
= this->simpleDuration();
773 if (simpleDuration
.isIndefinite() || hasTagName(SVGNames::setTag
)) {
774 SMILTime repeatCount
= this->repeatCount();
775 SMILTime repeatingDurationEnd
= m_intervalBegin
+ repeatingDuration();
776 // We are supposed to do freeze semantics when repeating ends, even if the element is still active.
777 // Take care that we get a timer callback at that point.
778 if (elapsed
< repeatingDurationEnd
&& repeatingDurationEnd
< m_intervalEnd
&& repeatingDurationEnd
.isFinite())
779 return repeatingDurationEnd
;
780 return m_intervalEnd
;
782 return elapsed
+ 0.025;
784 return m_intervalBegin
>= elapsed
? m_intervalBegin
: SMILTime::unresolved();
787 SVGSMILElement::ActiveState
SVGSMILElement::determineActiveState(SMILTime elapsed
) const
789 if (elapsed
>= m_intervalBegin
&& elapsed
< m_intervalEnd
)
792 if (m_activeState
== Active
)
793 return fill() == FillFreeze
? Frozen
: Inactive
;
795 return m_activeState
;
798 bool SVGSMILElement::isContributing(SMILTime elapsed
) const
800 // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
801 return (m_activeState
== Active
&& (fill() == FillFreeze
|| elapsed
<= m_intervalBegin
+ repeatingDuration())) || m_activeState
== Frozen
;
804 void SVGSMILElement::progress(SMILTime elapsed
, SVGSMILElement
* resultElement
)
806 ASSERT(m_timeContainer
);
807 ASSERT(m_isWaitingForFirstInterval
|| m_intervalBegin
.isFinite());
809 if (!m_conditionsConnected
)
812 if (!m_intervalBegin
.isFinite()) {
813 ASSERT(m_activeState
== Inactive
);
814 m_nextProgressTime
= SMILTime::unresolved();
818 if (elapsed
< m_intervalBegin
) {
819 ASSERT(m_activeState
!= Active
);
820 if (m_activeState
== Frozen
&& resultElement
)
821 updateAnimation(m_lastPercent
, m_lastRepeat
, resultElement
);
822 m_nextProgressTime
= m_intervalBegin
;
826 m_previousIntervalBegin
= m_intervalBegin
;
828 if (m_activeState
== Inactive
) {
829 m_isWaitingForFirstInterval
= false;
830 m_activeState
= Active
;
831 startedActiveInterval();
835 float percent
= calculateAnimationPercentAndRepeat(elapsed
, repeat
);
837 checkRestart(elapsed
);
839 ActiveState oldActiveState
= m_activeState
;
840 m_activeState
= determineActiveState(elapsed
);
842 if (isContributing(elapsed
)) {
844 updateAnimation(percent
, repeat
, resultElement
);
845 m_lastPercent
= percent
;
846 m_lastRepeat
= repeat
;
849 if (oldActiveState
== Active
&& m_activeState
!= Active
)
850 endedActiveInterval();
852 m_nextProgressTime
= calculateNextProgressTime(elapsed
);
855 void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting
)
857 ASSERT(m_intervalBegin
.isFinite());
858 static HashSet
<SVGSMILElement
*> loopBreaker
;
859 if (loopBreaker
.contains(this))
861 loopBreaker
.add(this);
863 TimeDependentSet::iterator end
= m_timeDependents
.end();
864 for (TimeDependentSet::iterator it
= m_timeDependents
.begin(); it
!= end
; ++it
) {
865 SVGSMILElement
* dependent
= *it
;
866 dependent
->createInstanceTimesFromSyncbase(this, newOrExisting
);
869 loopBreaker
.remove(this);
872 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement
* syncbase
, NewOrExistingInterval newOrExisting
)
874 // FIXME: To be really correct, this should handle updating exising interval by changing
875 // the associated times instead of creating new ones.
876 for (unsigned n
= 0; n
< m_conditions
.size(); ++n
) {
877 Condition
& condition
= m_conditions
[n
];
878 if (condition
.m_type
== Condition::Syncbase
&& condition
.m_syncbase
== syncbase
) {
879 ASSERT(condition
.m_name
== "begin" || condition
.m_name
== "end");
880 // No nested time containers in SVG, no need for crazy time space conversions. Phew!
882 if (condition
.m_name
== "begin")
883 time
= syncbase
->m_intervalBegin
+ condition
.m_offset
;
885 time
= syncbase
->m_intervalEnd
+ condition
.m_offset
;
886 ASSERT(time
.isFinite());
887 if (condition
.m_beginOrEnd
== Begin
)
895 void SVGSMILElement::addTimeDependent(SVGSMILElement
* animation
)
897 m_timeDependents
.add(animation
);
898 if (m_intervalBegin
.isFinite())
899 animation
->createInstanceTimesFromSyncbase(this, NewInterval
);
902 void SVGSMILElement::removeTimeDependent(SVGSMILElement
* animation
)
904 m_timeDependents
.remove(animation
);
907 void SVGSMILElement::handleConditionEvent(Event
* event
, Condition
* condition
)
909 if (condition
->m_beginOrEnd
== Begin
)
910 addBeginTime(elapsed() + condition
->m_offset
);
912 addEndTime(elapsed() + condition
->m_offset
);
915 void SVGSMILElement::beginByLinkActivation()
917 addBeginTime(elapsed());