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 #include "core/svg/animation/SVGSMILElement.h"
30 #include "XLinkNames.h"
31 #include "bindings/v8/ExceptionStatePlaceholder.h"
32 #include "bindings/v8/ScriptEventListener.h"
33 #include "core/dom/Document.h"
34 #include "core/events/EventListener.h"
35 #include "core/events/EventSender.h"
36 #include "core/svg/SVGDocumentExtensions.h"
37 #include "core/svg/SVGSVGElement.h"
38 #include "core/svg/SVGURIReference.h"
39 #include "core/svg/animation/SMILTimeContainer.h"
40 #include "platform/FloatConversion.h"
41 #include "wtf/MathExtras.h"
42 #include "wtf/StdLibExtras.h"
43 #include "wtf/Vector.h"
49 class RepeatEvent FINAL : public Event {
51 static PassRefPtr<RepeatEvent> create(const AtomicString& type, int repeat)
53 return adoptRef(new RepeatEvent(type, false, false, repeat));
56 virtual ~RepeatEvent() { }
58 int repeat() const { return m_repeat; }
60 RepeatEvent(const AtomicString& type, bool canBubble, bool cancelable, int repeat = -1)
61 : Event(type, canBubble, cancelable)
69 inline RepeatEvent* toRepeatEvent(Event* event)
71 ASSERT_WITH_SECURITY_IMPLICATION(!event || event->type() == "repeatn");
72 return static_cast<RepeatEvent*>(event);
75 static SMILEventSender& smilEndEventSender()
77 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("endEvent"));
81 static SMILEventSender& smilBeginEventSender()
83 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("beginEvent"));
87 static SMILEventSender& smilRepeatEventSender()
89 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("repeatEvent"));
93 static SMILEventSender& smilRepeatNEventSender()
95 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("repeatn"));
99 // This is used for duration type time values that can't be negative.
100 static const double invalidCachedTime = -1.;
102 class ConditionEventListener FINAL : public EventListener {
104 static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
106 return adoptRef(new ConditionEventListener(animation, condition));
109 static const ConditionEventListener* cast(const EventListener* listener)
111 return listener->type() == ConditionEventListenerType
112 ? static_cast<const ConditionEventListener*>(listener)
116 virtual bool operator==(const EventListener& other) OVERRIDE;
118 void disconnectAnimation()
124 ConditionEventListener(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
125 : EventListener(ConditionEventListenerType)
126 , m_animation(animation)
127 , m_condition(condition)
131 virtual void handleEvent(ExecutionContext*, Event*) OVERRIDE;
133 SVGSMILElement* m_animation;
134 SVGSMILElement::Condition* m_condition;
137 bool ConditionEventListener::operator==(const EventListener& listener)
139 if (const ConditionEventListener* conditionEventListener = ConditionEventListener::cast(&listener))
140 return m_animation == conditionEventListener->m_animation && m_condition == conditionEventListener->m_condition;
144 void ConditionEventListener::handleEvent(ExecutionContext*, Event* event)
148 m_animation->handleConditionEvent(event, m_condition);
151 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeat)
153 , m_beginOrEnd(beginOrEnd)
161 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document& doc)
162 : SVGElement(tagName, doc)
163 , m_attributeName(anyQName())
165 , m_syncBaseConditionsConnected(false)
166 , m_hasEndEventConditions(false)
167 , m_isWaitingForFirstInterval(true)
168 , m_intervalBegin(SMILTime::unresolved())
169 , m_intervalEnd(SMILTime::unresolved())
170 , m_previousIntervalBegin(SMILTime::unresolved())
171 , m_activeState(Inactive)
174 , m_nextProgressTime(0)
175 , m_documentOrderIndex(0)
176 , m_cachedDur(invalidCachedTime)
177 , m_cachedRepeatDur(invalidCachedTime)
178 , m_cachedRepeatCount(invalidCachedTime)
179 , m_cachedMin(invalidCachedTime)
180 , m_cachedMax(invalidCachedTime)
182 resolveFirstInterval();
185 SVGSMILElement::~SVGSMILElement()
187 clearResourceAndEventBaseReferences();
188 smilEndEventSender().cancelEvent(this);
189 smilBeginEventSender().cancelEvent(this);
190 smilRepeatEventSender().cancelEvent(this);
191 smilRepeatNEventSender().cancelEvent(this);
193 if (m_timeContainer && m_targetElement && hasValidAttributeName())
194 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
197 void SVGSMILElement::clearResourceAndEventBaseReferences()
199 document().accessSVGExtensions()->removeAllTargetReferencesForElement(this);
202 void SVGSMILElement::clearConditions()
204 disconnectSyncBaseConditions();
205 disconnectEventBaseConditions();
206 m_conditions.clear();
209 void SVGSMILElement::buildPendingResource()
211 clearResourceAndEventBaseReferences();
214 // Reset the target element if we are no longer in the document.
220 AtomicString href = getAttribute(XLinkNames::hrefAttr);
223 target = parentNode() && parentNode()->isElementNode() ? toElement(parentNode()) : 0;
225 target = SVGURIReference::targetElementFromIRIString(href, document(), &id);
226 SVGElement* svgTarget = target && target->isSVGElement() ? toSVGElement(target) : 0;
228 if (svgTarget && !svgTarget->inDocument())
231 if (svgTarget != targetElement())
232 setTargetElement(svgTarget);
235 // Do not register as pending if we are already pending this resource.
236 if (document().accessSVGExtensions()->isElementPendingResource(this, id))
240 document().accessSVGExtensions()->addPendingResource(id, this);
241 ASSERT(hasPendingResources());
244 // Register us with the target in the dependencies map. Any change of hrefElement
245 // that leads to relayout/repainting now informs us, so we can react to it.
246 document().accessSVGExtensions()->addElementReferencingTarget(this, svgTarget);
248 connectEventBaseConditions();
251 static inline QualifiedName constructQualifiedName(const SVGElement* svgElement, const AtomicString& attributeName)
254 if (attributeName.isEmpty())
256 if (!attributeName.contains(':'))
257 return QualifiedName(nullAtom, attributeName, nullAtom);
260 AtomicString localName;
261 if (!Document::parseQualifiedName(attributeName, prefix, localName, ASSERT_NO_EXCEPTION))
264 const AtomicString& namespaceURI = svgElement->lookupNamespaceURI(prefix);
265 if (namespaceURI.isEmpty())
268 return QualifiedName(nullAtom, localName, namespaceURI);
271 static inline void clearTimesWithDynamicOrigins(Vector<SMILTimeWithOrigin>& timeList)
273 for (int i = timeList.size() - 1; i >= 0; --i) {
274 if (timeList[i].originIsScript())
279 void SVGSMILElement::reset()
281 clearAnimatedType(m_targetElement);
283 m_activeState = Inactive;
284 m_isWaitingForFirstInterval = true;
285 m_intervalBegin = SMILTime::unresolved();
286 m_intervalEnd = SMILTime::unresolved();
287 m_previousIntervalBegin = SMILTime::unresolved();
290 m_nextProgressTime = 0;
291 resolveFirstInterval();
294 Node::InsertionNotificationRequest SVGSMILElement::insertedInto(ContainerNode* rootParent)
296 SVGElement::insertedInto(rootParent);
297 if (!rootParent->inDocument())
298 return InsertionDone;
300 // Verify we are not in <use> instance tree.
301 ASSERT(!isInShadowTree() || !parentOrShadowHostElement() || !parentOrShadowHostElement()->isSVGElement());
303 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
304 SVGSVGElement* owner = ownerSVGElement();
306 return InsertionDone;
308 m_timeContainer = owner->timeContainer();
309 ASSERT(m_timeContainer);
310 m_timeContainer->setDocumentOrderIndexesDirty();
312 // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
313 if (!fastHasAttribute(SVGNames::beginAttr))
314 m_beginTimes.append(SMILTimeWithOrigin());
316 if (m_isWaitingForFirstInterval)
317 resolveFirstInterval();
320 m_timeContainer->notifyIntervalsChanged();
322 buildPendingResource();
324 return InsertionDone;
327 void SVGSMILElement::removedFrom(ContainerNode* rootParent)
329 if (rootParent->inDocument()) {
330 clearResourceAndEventBaseReferences();
333 setAttributeName(anyQName());
334 animationAttributeChanged();
338 SVGElement::removedFrom(rootParent);
341 bool SVGSMILElement::hasValidAttributeName()
343 return attributeName() != anyQName();
346 SMILTime SVGSMILElement::parseOffsetValue(const String& data)
350 String parse = data.stripWhiteSpace();
351 if (parse.endsWith('h'))
352 result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
353 else if (parse.endsWith("min"))
354 result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
355 else if (parse.endsWith("ms"))
356 result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
357 else if (parse.endsWith('s'))
358 result = parse.left(parse.length() - 1).toDouble(&ok);
360 result = parse.toDouble(&ok);
362 return SMILTime::unresolved();
366 SMILTime SVGSMILElement::parseClockValue(const String& data)
369 return SMILTime::unresolved();
371 String parse = data.stripWhiteSpace();
373 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
374 if (parse == indefiniteValue)
375 return SMILTime::indefinite();
379 size_t doublePointOne = parse.find(':');
380 size_t doublePointTwo = parse.find(':', doublePointOne + 1);
381 if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) {
382 result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
384 return SMILTime::unresolved();
385 result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
387 return SMILTime::unresolved();
388 result += parse.substring(6).toDouble(&ok);
389 } else if (doublePointOne == 2 && doublePointTwo == kNotFound && parse.length() >= 5) {
390 result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
392 return SMILTime::unresolved();
393 result += parse.substring(3).toDouble(&ok);
395 return parseOffsetValue(parse);
398 return SMILTime::unresolved();
402 static void sortTimeList(Vector<SMILTimeWithOrigin>& timeList)
404 std::sort(timeList.begin(), timeList.end());
407 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd)
409 String parseString = value.stripWhiteSpace();
413 size_t pos = parseString.find('+');
414 if (pos == kNotFound) {
415 pos = parseString.find('-');
416 if (pos != kNotFound)
419 String conditionString;
421 if (pos == kNotFound)
422 conditionString = parseString;
424 conditionString = parseString.left(pos).stripWhiteSpace();
425 String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
426 offset = parseOffsetValue(offsetString);
427 if (offset.isUnresolved())
429 offset = offset * sign;
431 if (conditionString.isEmpty())
433 pos = conditionString.find('.');
437 if (pos == kNotFound)
438 nameString = conditionString;
440 baseID = conditionString.left(pos);
441 nameString = conditionString.substring(pos + 1);
443 if (nameString.isEmpty())
446 Condition::Type type;
448 if (nameString.startsWith("repeat(") && nameString.endsWith(')')) {
449 repeat = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
452 nameString = "repeatn";
453 type = Condition::EventBase;
454 } else if (nameString == "begin" || nameString == "end") {
455 if (baseID.isEmpty())
457 type = Condition::Syncbase;
458 } else if (nameString.startsWith("accesskey(")) {
459 // FIXME: accesskey() support.
460 type = Condition::AccessKey;
462 type = Condition::EventBase;
464 m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeat));
466 if (type == Condition::EventBase && beginOrEnd == End)
467 m_hasEndEventConditions = true;
472 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd)
474 Vector<SMILTimeWithOrigin>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
475 if (beginOrEnd == End)
476 m_hasEndEventConditions = false;
477 HashSet<double> existing;
478 for (unsigned n = 0; n < timeList.size(); ++n)
479 existing.add(timeList[n].time().value());
480 Vector<String> splitString;
481 parseString.split(';', splitString);
482 for (unsigned n = 0; n < splitString.size(); ++n) {
483 SMILTime value = parseClockValue(splitString[n]);
484 if (value.isUnresolved())
485 parseCondition(splitString[n], beginOrEnd);
486 else if (!existing.contains(value.value()))
487 timeList.append(SMILTimeWithOrigin(value, SMILTimeWithOrigin::ParserOrigin));
489 sortTimeList(timeList);
492 bool SVGSMILElement::isSupportedAttribute(const QualifiedName& attrName)
494 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
495 if (supportedAttributes.isEmpty()) {
496 supportedAttributes.add(SVGNames::beginAttr);
497 supportedAttributes.add(SVGNames::endAttr);
498 supportedAttributes.add(SVGNames::durAttr);
499 supportedAttributes.add(SVGNames::repeatDurAttr);
500 supportedAttributes.add(SVGNames::repeatCountAttr);
501 supportedAttributes.add(SVGNames::minAttr);
502 supportedAttributes.add(SVGNames::maxAttr);
503 supportedAttributes.add(SVGNames::attributeNameAttr);
504 supportedAttributes.add(XLinkNames::hrefAttr);
506 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
509 void SVGSMILElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
511 if (name == SVGNames::beginAttr) {
512 if (!m_conditions.isEmpty()) {
514 parseBeginOrEnd(fastGetAttribute(SVGNames::endAttr), End);
516 parseBeginOrEnd(value.string(), Begin);
518 connectSyncBaseConditions();
519 } else if (name == SVGNames::endAttr) {
520 if (!m_conditions.isEmpty()) {
522 parseBeginOrEnd(fastGetAttribute(SVGNames::beginAttr), Begin);
524 parseBeginOrEnd(value.string(), End);
526 connectSyncBaseConditions();
527 } else if (name == SVGNames::onbeginAttr) {
528 setAttributeEventListener(EventTypeNames::beginEvent, createAttributeEventListener(this, name, value));
529 } else if (name == SVGNames::onendAttr) {
530 setAttributeEventListener(EventTypeNames::endEvent, createAttributeEventListener(this, name, value));
531 } else if (name == SVGNames::onrepeatAttr) {
532 setAttributeEventListener(EventTypeNames::repeatEvent, createAttributeEventListener(this, name, value));
534 SVGElement::parseAttribute(name, value);
537 void SVGSMILElement::svgAttributeChanged(const QualifiedName& attrName)
539 if (!isSupportedAttribute(attrName)) {
540 SVGElement::svgAttributeChanged(attrName);
544 if (attrName == SVGNames::durAttr)
545 m_cachedDur = invalidCachedTime;
546 else if (attrName == SVGNames::repeatDurAttr)
547 m_cachedRepeatDur = invalidCachedTime;
548 else if (attrName == SVGNames::repeatCountAttr)
549 m_cachedRepeatCount = invalidCachedTime;
550 else if (attrName == SVGNames::minAttr)
551 m_cachedMin = invalidCachedTime;
552 else if (attrName == SVGNames::maxAttr)
553 m_cachedMax = invalidCachedTime;
554 else if (attrName == SVGNames::attributeNameAttr)
555 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
556 else if (attrName.matches(XLinkNames::hrefAttr)) {
557 SVGElementInstance::InvalidationGuard invalidationGuard(this);
558 buildPendingResource();
560 clearAnimatedType(m_targetElement);
561 } else if (inDocument()) {
562 if (attrName == SVGNames::beginAttr)
563 beginListChanged(elapsed());
564 else if (attrName == SVGNames::endAttr)
565 endListChanged(elapsed());
568 animationAttributeChanged();
571 inline SVGElement* SVGSMILElement::eventBaseFor(const Condition& condition)
573 Element* eventBase = condition.m_baseID.isEmpty() ? targetElement() : treeScope().getElementById(AtomicString(condition.m_baseID));
574 if (eventBase && eventBase->isSVGElement())
575 return toSVGElement(eventBase);
579 void SVGSMILElement::connectSyncBaseConditions()
581 if (m_syncBaseConditionsConnected)
582 disconnectSyncBaseConditions();
583 m_syncBaseConditionsConnected = true;
584 for (unsigned n = 0; n < m_conditions.size(); ++n) {
585 Condition& condition = m_conditions[n];
586 if (condition.m_type == Condition::Syncbase) {
587 ASSERT(!condition.m_baseID.isEmpty());
588 condition.m_syncbase = treeScope().getElementById(AtomicString(condition.m_baseID));
589 if (!condition.m_syncbase || !isSVGSMILElement(*condition.m_syncbase)) {
590 condition.m_syncbase = 0;
593 toSVGSMILElement(condition.m_syncbase.get())->addSyncBaseDependent(this);
598 void SVGSMILElement::disconnectSyncBaseConditions()
600 if (!m_syncBaseConditionsConnected)
602 m_syncBaseConditionsConnected = false;
603 for (unsigned n = 0; n < m_conditions.size(); ++n) {
604 Condition& condition = m_conditions[n];
605 if (condition.m_type == Condition::Syncbase) {
606 if (condition.m_syncbase)
607 toSVGSMILElement(condition.m_syncbase.get())->removeSyncBaseDependent(this);
608 condition.m_syncbase = 0;
613 void SVGSMILElement::connectEventBaseConditions()
615 disconnectEventBaseConditions();
616 for (unsigned n = 0; n < m_conditions.size(); ++n) {
617 Condition& condition = m_conditions[n];
618 if (condition.m_type == Condition::EventBase) {
619 ASSERT(!condition.m_syncbase);
620 SVGElement* eventBase = eventBaseFor(condition);
622 if (!condition.m_baseID.isEmpty() && !document().accessSVGExtensions()->isElementPendingResource(this, AtomicString(condition.m_baseID)))
623 document().accessSVGExtensions()->addPendingResource(AtomicString(condition.m_baseID), this);
626 ASSERT(!condition.m_eventListener);
627 condition.m_eventListener = ConditionEventListener::create(this, &condition);
628 eventBase->addEventListener(AtomicString(condition.m_name), condition.m_eventListener, false);
629 document().accessSVGExtensions()->addElementReferencingTarget(this, eventBase);
634 void SVGSMILElement::disconnectEventBaseConditions()
636 for (unsigned n = 0; n < m_conditions.size(); ++n) {
637 Condition& condition = m_conditions[n];
638 if (condition.m_type == Condition::EventBase) {
639 ASSERT(!condition.m_syncbase);
640 if (!condition.m_eventListener)
642 // Note: It's a memory optimization to try to remove our condition
643 // event listener, but it's not guaranteed to work, since we have
644 // no guarantee that eventBaseFor() will be able to find our condition's
645 // original eventBase. So, we also have to disconnect ourselves from
646 // our condition event listener, in case it later fires.
647 SVGElement* eventBase = eventBaseFor(condition);
649 eventBase->removeEventListener(AtomicString(condition.m_name), condition.m_eventListener.get(), false);
650 condition.m_eventListener->disconnectAnimation();
651 condition.m_eventListener = 0;
656 void SVGSMILElement::setAttributeName(const QualifiedName& attributeName)
658 if (m_timeContainer && m_targetElement && m_attributeName != attributeName) {
659 if (hasValidAttributeName())
660 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
661 m_attributeName = attributeName;
662 if (hasValidAttributeName())
663 m_timeContainer->schedule(this, m_targetElement, m_attributeName);
665 m_attributeName = attributeName;
667 // Only clear the animated type, if we had a target before.
669 clearAnimatedType(m_targetElement);
672 void SVGSMILElement::setTargetElement(SVGElement* target)
674 if (m_timeContainer && hasValidAttributeName()) {
676 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
678 m_timeContainer->schedule(this, target, m_attributeName);
681 if (m_targetElement) {
682 // Clear values that may depend on the previous target.
683 clearAnimatedType(m_targetElement);
684 disconnectSyncBaseConditions();
687 // If the animation state is not Inactive, always reset to a clear state before leaving the old target element.
688 if (m_activeState != Inactive)
689 endedActiveInterval();
691 m_targetElement = target;
694 SMILTime SVGSMILElement::elapsed() const
696 return m_timeContainer ? m_timeContainer->elapsed() : 0;
699 bool SVGSMILElement::isFrozen() const
701 return m_activeState == Frozen;
704 SVGSMILElement::Restart SVGSMILElement::restart() const
706 DEFINE_STATIC_LOCAL(const AtomicString, never, ("never", AtomicString::ConstructFromLiteral));
707 DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive", AtomicString::ConstructFromLiteral));
708 const AtomicString& value = fastGetAttribute(SVGNames::restartAttr);
711 if (value == whenNotActive)
712 return RestartWhenNotActive;
713 return RestartAlways;
716 SVGSMILElement::FillMode SVGSMILElement::fill() const
718 DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze", AtomicString::ConstructFromLiteral));
719 const AtomicString& value = fastGetAttribute(SVGNames::fillAttr);
720 return value == freeze ? FillFreeze : FillRemove;
723 SMILTime SVGSMILElement::dur() const
725 if (m_cachedDur != invalidCachedTime)
727 const AtomicString& value = fastGetAttribute(SVGNames::durAttr);
728 SMILTime clockValue = parseClockValue(value);
729 return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
732 SMILTime SVGSMILElement::repeatDur() const
734 if (m_cachedRepeatDur != invalidCachedTime)
735 return m_cachedRepeatDur;
736 const AtomicString& value = fastGetAttribute(SVGNames::repeatDurAttr);
737 SMILTime clockValue = parseClockValue(value);
738 m_cachedRepeatDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
739 return m_cachedRepeatDur;
742 // So a count is not really a time but let just all pretend we did not notice.
743 SMILTime SVGSMILElement::repeatCount() const
745 if (m_cachedRepeatCount != invalidCachedTime)
746 return m_cachedRepeatCount;
747 const AtomicString& value = fastGetAttribute(SVGNames::repeatCountAttr);
749 return SMILTime::unresolved();
751 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
752 if (value == indefiniteValue)
753 return SMILTime::indefinite();
755 double result = value.string().toDouble(&ok);
756 return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved();
759 SMILTime SVGSMILElement::maxValue() const
761 if (m_cachedMax != invalidCachedTime)
763 const AtomicString& value = fastGetAttribute(SVGNames::maxAttr);
764 SMILTime result = parseClockValue(value);
765 return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result;
768 SMILTime SVGSMILElement::minValue() const
770 if (m_cachedMin != invalidCachedTime)
772 const AtomicString& value = fastGetAttribute(SVGNames::minAttr);
773 SMILTime result = parseClockValue(value);
774 return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
777 SMILTime SVGSMILElement::simpleDuration() const
779 return min(dur(), SMILTime::indefinite());
782 void SVGSMILElement::addBeginTime(SMILTime eventTime, SMILTime beginTime, SMILTimeWithOrigin::Origin origin)
784 ASSERT(!std::isnan(beginTime.value()));
785 m_beginTimes.append(SMILTimeWithOrigin(beginTime, origin));
786 sortTimeList(m_beginTimes);
787 beginListChanged(eventTime);
790 void SVGSMILElement::addEndTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin origin)
792 ASSERT(!std::isnan(endTime.value()));
793 m_endTimes.append(SMILTimeWithOrigin(endTime, origin));
794 sortTimeList(m_endTimes);
795 endListChanged(eventTime);
798 inline bool compareTimes(const SMILTimeWithOrigin& left, const SMILTimeWithOrigin& right)
800 return left.time() < right.time();
803 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
805 const Vector<SMILTimeWithOrigin>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
806 int sizeOfList = list.size();
809 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
811 const SMILTimeWithOrigin dummyTimeWithOrigin(minimumTime, SMILTimeWithOrigin::ParserOrigin);
812 const SMILTimeWithOrigin* result = std::lower_bound(list.begin(), list.end(), dummyTimeWithOrigin, compareTimes);
813 int indexOfResult = result - list.begin();
814 if (indexOfResult == sizeOfList)
815 return SMILTime::unresolved();
816 const SMILTime& currentTime = list[indexOfResult].time();
818 // The special value "indefinite" does not yield an instance time in the begin list.
819 if (currentTime.isIndefinite() && beginOrEnd == Begin)
820 return SMILTime::unresolved();
822 if (currentTime > minimumTime)
825 ASSERT(currentTime == minimumTime);
829 // If the equals is not accepted, return the next bigger item in the list.
830 SMILTime nextTime = currentTime;
831 while (indexOfResult < sizeOfList - 1) {
832 nextTime = list[indexOfResult + 1].time();
833 if (nextTime > minimumTime)
838 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
841 SMILTime SVGSMILElement::repeatingDuration() const
843 // Computing the active duration
844 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
845 SMILTime repeatCount = this->repeatCount();
846 SMILTime repeatDur = this->repeatDur();
847 SMILTime simpleDuration = this->simpleDuration();
848 if (!simpleDuration || (repeatDur.isUnresolved() && repeatCount.isUnresolved()))
849 return simpleDuration;
850 SMILTime repeatCountDuration = simpleDuration * repeatCount;
851 return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
854 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
856 // Computing the active duration
857 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
858 SMILTime preliminaryActiveDuration;
859 if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
860 preliminaryActiveDuration = resolvedEnd - resolvedBegin;
861 else if (!resolvedEnd.isFinite())
862 preliminaryActiveDuration = repeatingDuration();
864 preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
866 SMILTime minValue = this->minValue();
867 SMILTime maxValue = this->maxValue();
868 if (minValue > maxValue) {
870 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
872 maxValue = SMILTime::indefinite();
874 return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
877 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const
879 // See the pseudocode in http://www.w3.org/TR/SMIL3/smil-timing.html#q90.
880 SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
881 SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
883 bool equalsMinimumOK = !first || m_intervalEnd > m_intervalBegin;
884 SMILTime tempBegin = findInstanceTime(Begin, beginAfter, equalsMinimumOK);
885 if (tempBegin.isUnresolved())
888 if (m_endTimes.isEmpty())
889 tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
891 tempEnd = findInstanceTime(End, tempBegin, true);
892 if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd))
893 tempEnd = findInstanceTime(End, tempBegin, false);
894 if (tempEnd.isUnresolved()) {
895 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions)
898 tempEnd = resolveActiveEnd(tempBegin, tempEnd);
900 if (!first || (tempEnd > 0 || (!tempBegin.value() && !tempEnd.value()))) {
901 beginResult = tempBegin;
906 beginAfter = tempEnd;
907 lastIntervalTempEnd = tempEnd;
909 beginResult = SMILTime::unresolved();
910 endResult = SMILTime::unresolved();
913 void SVGSMILElement::resolveFirstInterval()
917 resolveInterval(true, begin, end);
918 ASSERT(!begin.isIndefinite());
920 if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {
921 m_intervalBegin = begin;
923 notifyDependentsIntervalChanged();
924 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
927 m_timeContainer->notifyIntervalsChanged();
931 bool SVGSMILElement::resolveNextInterval()
935 resolveInterval(false, begin, end);
936 ASSERT(!begin.isIndefinite());
938 if (!begin.isUnresolved() && begin != m_intervalBegin) {
939 m_intervalBegin = begin;
941 notifyDependentsIntervalChanged();
942 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
949 SMILTime SVGSMILElement::nextProgressTime() const
951 return m_nextProgressTime;
954 void SVGSMILElement::beginListChanged(SMILTime eventTime)
956 if (m_isWaitingForFirstInterval)
957 resolveFirstInterval();
959 SMILTime newBegin = findInstanceTime(Begin, eventTime, true);
960 if (newBegin.isFinite() && (m_intervalEnd <= eventTime || newBegin < m_intervalBegin)) {
961 // Begin time changed, re-resolve the interval.
962 SMILTime oldBegin = m_intervalBegin;
963 m_intervalEnd = eventTime;
964 resolveInterval(false, m_intervalBegin, m_intervalEnd);
965 ASSERT(!m_intervalBegin.isUnresolved());
966 if (m_intervalBegin != oldBegin) {
967 if (m_activeState == Active && m_intervalBegin > eventTime) {
968 m_activeState = determineActiveState(eventTime);
969 if (m_activeState != Active)
970 endedActiveInterval();
972 notifyDependentsIntervalChanged();
976 m_nextProgressTime = elapsed();
979 m_timeContainer->notifyIntervalsChanged();
982 void SVGSMILElement::endListChanged(SMILTime)
984 SMILTime elapsed = this->elapsed();
985 if (m_isWaitingForFirstInterval)
986 resolveFirstInterval();
987 else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
988 SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
989 if (newEnd < m_intervalEnd) {
990 newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
991 if (newEnd != m_intervalEnd) {
992 m_intervalEnd = newEnd;
993 notifyDependentsIntervalChanged();
997 m_nextProgressTime = elapsed;
1000 m_timeContainer->notifyIntervalsChanged();
1003 SVGSMILElement::RestartedInterval SVGSMILElement::maybeRestartInterval(SMILTime elapsed)
1005 ASSERT(!m_isWaitingForFirstInterval);
1006 ASSERT(elapsed >= m_intervalBegin);
1008 Restart restart = this->restart();
1009 if (restart == RestartNever)
1010 return DidNotRestartInterval;
1012 if (elapsed < m_intervalEnd) {
1013 if (restart != RestartAlways)
1014 return DidNotRestartInterval;
1015 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
1016 if (nextBegin < m_intervalEnd) {
1017 m_intervalEnd = nextBegin;
1018 notifyDependentsIntervalChanged();
1022 if (elapsed >= m_intervalEnd) {
1023 if (resolveNextInterval())
1024 return DidRestartInterval;
1026 return DidNotRestartInterval;
1029 void SVGSMILElement::seekToIntervalCorrespondingToTime(SMILTime elapsed)
1031 ASSERT(!m_isWaitingForFirstInterval);
1032 ASSERT(elapsed >= m_intervalBegin);
1034 // Manually seek from interval to interval, just as if the animation would run regulary.
1036 // Figure out the next value in the begin time list after the current interval begin.
1037 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
1039 // If the 'nextBegin' time is unresolved (eg. just one defined interval), we're done seeking.
1040 if (nextBegin.isUnresolved())
1043 // If the 'nextBegin' time is larger than or equal to the current interval end time, we're done seeking.
1044 // If the 'elapsed' time is smaller than the next begin interval time, we're done seeking.
1045 if (nextBegin < m_intervalEnd && elapsed >= nextBegin) {
1046 // End current interval, and start a new interval from the 'nextBegin' time.
1047 m_intervalEnd = nextBegin;
1048 if (!resolveNextInterval())
1053 // If the desired 'elapsed' time is past the current interval, advance to the next.
1054 if (elapsed >= m_intervalEnd) {
1055 if (!resolveNextInterval())
1064 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const
1066 SMILTime simpleDuration = this->simpleDuration();
1068 if (simpleDuration.isIndefinite()) {
1072 if (!simpleDuration) {
1076 ASSERT(m_intervalBegin.isFinite());
1077 ASSERT(simpleDuration.isFinite());
1078 SMILTime activeTime = elapsed - m_intervalBegin;
1079 SMILTime repeatingDuration = this->repeatingDuration();
1080 if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
1081 repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value());
1082 if (!fmod(repeatingDuration.value(), simpleDuration.value()))
1085 double percent = (m_intervalEnd.value() - m_intervalBegin.value()) / simpleDuration.value();
1086 percent = percent - floor(percent);
1087 if (percent < numeric_limits<float>::epsilon() || 1 - percent < numeric_limits<float>::epsilon())
1089 return narrowPrecisionToFloat(percent);
1091 repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
1092 SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
1093 return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
1096 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
1098 if (m_activeState == Active) {
1099 // If duration is indefinite the value does not actually change over time. Same is true for <set>.
1100 SMILTime simpleDuration = this->simpleDuration();
1101 if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) {
1102 SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
1103 // We are supposed to do freeze semantics when repeating ends, even if the element is still active.
1104 // Take care that we get a timer callback at that point.
1105 if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite())
1106 return repeatingDurationEnd;
1107 return m_intervalEnd;
1109 return elapsed + 0.025;
1111 return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
1114 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
1116 if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd)
1119 return fill() == FillFreeze ? Frozen : Inactive;
1122 bool SVGSMILElement::isContributing(SMILTime elapsed) const
1124 // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
1125 return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
1128 bool SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement, bool seekToTime)
1130 ASSERT(resultElement);
1131 ASSERT(m_timeContainer);
1132 ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
1134 if (!m_syncBaseConditionsConnected)
1135 connectSyncBaseConditions();
1137 if (!m_intervalBegin.isFinite()) {
1138 ASSERT(m_activeState == Inactive);
1139 m_nextProgressTime = SMILTime::unresolved();
1143 if (elapsed < m_intervalBegin) {
1144 ASSERT(m_activeState != Active);
1145 if (m_activeState == Frozen) {
1146 if (this == resultElement)
1147 resetAnimatedType();
1148 updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
1150 m_nextProgressTime = m_intervalBegin;
1154 m_previousIntervalBegin = m_intervalBegin;
1156 if (m_isWaitingForFirstInterval) {
1157 m_isWaitingForFirstInterval = false;
1158 resolveFirstInterval();
1161 // This call may obtain a new interval -- never call calculateAnimationPercentAndRepeat() before!
1163 seekToIntervalCorrespondingToTime(elapsed);
1164 if (elapsed < m_intervalBegin) {
1165 // elapsed is not within an interval.
1166 m_nextProgressTime = m_intervalBegin;
1171 unsigned repeat = 0;
1172 float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
1173 RestartedInterval restartedInterval = maybeRestartInterval(elapsed);
1175 ActiveState oldActiveState = m_activeState;
1176 m_activeState = determineActiveState(elapsed);
1177 bool animationIsContributing = isContributing(elapsed);
1179 // 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.
1180 if (this == resultElement && animationIsContributing)
1181 resetAnimatedType();
1183 if (animationIsContributing) {
1184 if (oldActiveState == Inactive || restartedInterval == DidRestartInterval) {
1185 smilBeginEventSender().dispatchEventSoon(this);
1186 startedActiveInterval();
1189 if (repeat && repeat != m_lastRepeat)
1190 dispatchRepeatEvents(repeat);
1192 updateAnimation(percent, repeat, resultElement);
1193 m_lastPercent = percent;
1194 m_lastRepeat = repeat;
1197 if ((oldActiveState == Active && m_activeState != Active) || restartedInterval == DidRestartInterval) {
1198 smilEndEventSender().dispatchEventSoon(this);
1199 endedActiveInterval();
1200 if (restartedInterval == DidNotRestartInterval && m_activeState != Frozen && this == resultElement)
1201 clearAnimatedType(m_targetElement);
1204 // Triggering all the pending events if the animation timeline is changed.
1206 if (m_activeState == Inactive)
1207 smilBeginEventSender().dispatchEventSoon(this);
1210 for (unsigned repeatEventCount = 1; repeatEventCount < repeat; repeatEventCount++)
1211 dispatchRepeatEvents(repeatEventCount);
1212 if (m_activeState == Inactive)
1213 dispatchRepeatEvents(repeat);
1216 if (m_activeState == Inactive || m_activeState == Frozen)
1217 smilEndEventSender().dispatchEventSoon(this);
1220 m_nextProgressTime = calculateNextProgressTime(elapsed);
1221 return animationIsContributing;
1224 void SVGSMILElement::notifyDependentsIntervalChanged()
1226 ASSERT(m_intervalBegin.isFinite());
1227 DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ());
1228 if (!loopBreaker.add(this).isNewEntry)
1231 TimeDependentSet::iterator end = m_syncBaseDependents.end();
1232 for (TimeDependentSet::iterator it = m_syncBaseDependents.begin(); it != end; ++it) {
1233 SVGSMILElement* dependent = *it;
1234 dependent->createInstanceTimesFromSyncbase(this);
1237 loopBreaker.remove(this);
1240 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase)
1242 // FIXME: To be really correct, this should handle updating exising interval by changing
1243 // the associated times instead of creating new ones.
1244 for (unsigned n = 0; n < m_conditions.size(); ++n) {
1245 Condition& condition = m_conditions[n];
1246 if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
1247 ASSERT(condition.m_name == "begin" || condition.m_name == "end");
1248 // No nested time containers in SVG, no need for crazy time space conversions. Phew!
1250 if (condition.m_name == "begin")
1251 time = syncbase->m_intervalBegin + condition.m_offset;
1253 time = syncbase->m_intervalEnd + condition.m_offset;
1254 if (!time.isFinite())
1256 if (condition.m_beginOrEnd == Begin)
1257 addBeginTime(elapsed(), time);
1259 addEndTime(elapsed(), time);
1264 void SVGSMILElement::addSyncBaseDependent(SVGSMILElement* animation)
1266 m_syncBaseDependents.add(animation);
1267 if (m_intervalBegin.isFinite())
1268 animation->createInstanceTimesFromSyncbase(this);
1271 void SVGSMILElement::removeSyncBaseDependent(SVGSMILElement* animation)
1273 m_syncBaseDependents.remove(animation);
1276 void SVGSMILElement::handleConditionEvent(Event* event, Condition* condition)
1278 if (event->type() == "repeatn" && toRepeatEvent(event)->repeat() != condition->m_repeat)
1281 SMILTime elapsed = this->elapsed();
1282 if (condition->m_beginOrEnd == Begin)
1283 addBeginTime(elapsed, elapsed + condition->m_offset);
1285 addEndTime(elapsed, elapsed + condition->m_offset);
1288 void SVGSMILElement::beginByLinkActivation()
1290 SMILTime elapsed = this->elapsed();
1291 addBeginTime(elapsed, elapsed);
1294 void SVGSMILElement::endedActiveInterval()
1296 clearTimesWithDynamicOrigins(m_beginTimes);
1297 clearTimesWithDynamicOrigins(m_endTimes);
1300 void SVGSMILElement::dispatchRepeatEvents(unsigned count)
1302 m_repeatEventCountList.append(count);
1303 smilRepeatEventSender().dispatchEventSoon(this);
1304 smilRepeatNEventSender().dispatchEventSoon(this);
1307 void SVGSMILElement::dispatchPendingEvent(SMILEventSender* eventSender)
1309 ASSERT(eventSender == &smilEndEventSender() || eventSender == &smilBeginEventSender() || eventSender == &smilRepeatEventSender() || eventSender == &smilRepeatNEventSender());
1310 const AtomicString& eventType = eventSender->eventType();
1311 if (eventType == "repeatn") {
1312 unsigned repeatEventCount = m_repeatEventCountList.first();
1313 m_repeatEventCountList.remove(0);
1314 dispatchEvent(RepeatEvent::create(eventType, repeatEventCount));
1316 dispatchEvent(Event::create(eventType));