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/SVGElementInstance.h"
38 #include "core/svg/SVGSVGElement.h"
39 #include "core/svg/SVGURIReference.h"
40 #include "core/svg/animation/SMILTimeContainer.h"
41 #include "platform/FloatConversion.h"
42 #include "wtf/MathExtras.h"
43 #include "wtf/StdLibExtras.h"
44 #include "wtf/Vector.h"
50 class RepeatEvent FINAL : public Event {
52 static PassRefPtrWillBeRawPtr<RepeatEvent> create(const AtomicString& type, int repeat)
54 return adoptRefWillBeNoop(new RepeatEvent(type, false, false, repeat));
57 virtual ~RepeatEvent() { }
59 int repeat() const { return m_repeat; }
61 virtual void trace(Visitor* visitor) OVERRIDE
63 Event::trace(visitor);
67 RepeatEvent(const AtomicString& type, bool canBubble, bool cancelable, int repeat = -1)
68 : Event(type, canBubble, cancelable)
77 inline RepeatEvent* toRepeatEvent(Event* event)
79 ASSERT_WITH_SECURITY_IMPLICATION(!event || event->type() == "repeatn");
80 return static_cast<RepeatEvent*>(event);
83 static SMILEventSender& smilEndEventSender()
85 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("endEvent"));
89 static SMILEventSender& smilBeginEventSender()
91 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("beginEvent"));
95 static SMILEventSender& smilRepeatEventSender()
97 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("repeatEvent"));
101 static SMILEventSender& smilRepeatNEventSender()
103 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("repeatn"));
107 // This is used for duration type time values that can't be negative.
108 static const double invalidCachedTime = -1.;
110 class ConditionEventListener FINAL : public EventListener {
112 static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
114 return adoptRef(new ConditionEventListener(animation, condition));
117 static const ConditionEventListener* cast(const EventListener* listener)
119 return listener->type() == ConditionEventListenerType
120 ? static_cast<const ConditionEventListener*>(listener)
124 virtual bool operator==(const EventListener& other) OVERRIDE;
126 void disconnectAnimation()
132 ConditionEventListener(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
133 : EventListener(ConditionEventListenerType)
134 , m_animation(animation)
135 , m_condition(condition)
139 virtual void handleEvent(ExecutionContext*, Event*) OVERRIDE;
141 SVGSMILElement* m_animation;
142 SVGSMILElement::Condition* m_condition;
145 bool ConditionEventListener::operator==(const EventListener& listener)
147 if (const ConditionEventListener* conditionEventListener = ConditionEventListener::cast(&listener))
148 return m_animation == conditionEventListener->m_animation && m_condition == conditionEventListener->m_condition;
152 void ConditionEventListener::handleEvent(ExecutionContext*, Event* event)
156 m_animation->handleConditionEvent(event, m_condition);
159 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeat)
161 , m_beginOrEnd(beginOrEnd)
169 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document& doc)
170 : SVGElement(tagName, doc)
171 , m_attributeName(anyQName())
172 , m_targetElement(nullptr)
173 , m_syncBaseConditionsConnected(false)
174 , m_hasEndEventConditions(false)
175 , m_isWaitingForFirstInterval(true)
176 , m_intervalBegin(SMILTime::unresolved())
177 , m_intervalEnd(SMILTime::unresolved())
178 , m_previousIntervalBegin(SMILTime::unresolved())
179 , m_activeState(Inactive)
182 , m_nextProgressTime(0)
183 , m_documentOrderIndex(0)
184 , m_cachedDur(invalidCachedTime)
185 , m_cachedRepeatDur(invalidCachedTime)
186 , m_cachedRepeatCount(invalidCachedTime)
187 , m_cachedMin(invalidCachedTime)
188 , m_cachedMax(invalidCachedTime)
190 resolveFirstInterval();
193 SVGSMILElement::~SVGSMILElement()
196 clearResourceAndEventBaseReferences();
198 smilEndEventSender().cancelEvent(this);
199 smilBeginEventSender().cancelEvent(this);
200 smilRepeatEventSender().cancelEvent(this);
201 smilRepeatNEventSender().cancelEvent(this);
204 if (m_timeContainer && m_targetElement && hasValidAttributeName())
205 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
209 void SVGSMILElement::clearResourceAndEventBaseReferences()
211 document().accessSVGExtensions().removeAllTargetReferencesForElement(this);
214 void SVGSMILElement::clearConditions()
216 disconnectSyncBaseConditions();
217 disconnectEventBaseConditions();
218 m_conditions.clear();
221 void SVGSMILElement::buildPendingResource()
223 clearResourceAndEventBaseReferences();
226 // Reset the target element if we are no longer in the document.
232 AtomicString href = getAttribute(XLinkNames::hrefAttr);
235 target = parentNode() && parentNode()->isElementNode() ? toElement(parentNode()) : 0;
237 target = SVGURIReference::targetElementFromIRIString(href, treeScope(), &id);
238 SVGElement* svgTarget = target && target->isSVGElement() ? toSVGElement(target) : 0;
240 if (svgTarget && !svgTarget->inDocument())
243 if (svgTarget != targetElement())
244 setTargetElement(svgTarget);
247 // Do not register as pending if we are already pending this resource.
248 if (document().accessSVGExtensions().isElementPendingResource(this, id))
252 document().accessSVGExtensions().addPendingResource(id, this);
253 ASSERT(hasPendingResources());
256 // Register us with the target in the dependencies map. Any change of hrefElement
257 // that leads to relayout/repainting now informs us, so we can react to it.
258 document().accessSVGExtensions().addElementReferencingTarget(this, svgTarget);
260 connectEventBaseConditions();
263 static inline QualifiedName constructQualifiedName(const SVGElement* svgElement, const AtomicString& attributeName)
266 if (attributeName.isEmpty())
268 if (!attributeName.contains(':'))
269 return QualifiedName(nullAtom, attributeName, nullAtom);
272 AtomicString localName;
273 if (!Document::parseQualifiedName(attributeName, prefix, localName, IGNORE_EXCEPTION))
276 const AtomicString& namespaceURI = svgElement->lookupNamespaceURI(prefix);
277 if (namespaceURI.isEmpty())
280 return QualifiedName(nullAtom, localName, namespaceURI);
283 static inline void clearTimesWithDynamicOrigins(Vector<SMILTimeWithOrigin>& timeList)
285 for (int i = timeList.size() - 1; i >= 0; --i) {
286 if (timeList[i].originIsScript())
291 void SVGSMILElement::reset()
293 clearAnimatedType(m_targetElement);
295 m_activeState = Inactive;
296 m_isWaitingForFirstInterval = true;
297 m_intervalBegin = SMILTime::unresolved();
298 m_intervalEnd = SMILTime::unresolved();
299 m_previousIntervalBegin = SMILTime::unresolved();
302 m_nextProgressTime = 0;
303 resolveFirstInterval();
306 Node::InsertionNotificationRequest SVGSMILElement::insertedInto(ContainerNode* rootParent)
308 SVGElement::insertedInto(rootParent);
310 if (!rootParent->inDocument())
311 return InsertionDone;
313 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
314 SVGSVGElement* owner = ownerSVGElement();
316 return InsertionDone;
318 m_timeContainer = owner->timeContainer();
319 ASSERT(m_timeContainer);
320 m_timeContainer->setDocumentOrderIndexesDirty();
322 // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
323 if (!fastHasAttribute(SVGNames::beginAttr))
324 m_beginTimes.append(SMILTimeWithOrigin());
326 if (m_isWaitingForFirstInterval)
327 resolveFirstInterval();
330 m_timeContainer->notifyIntervalsChanged();
332 buildPendingResource();
334 return InsertionDone;
337 void SVGSMILElement::removedFrom(ContainerNode* rootParent)
339 if (rootParent->inDocument()) {
340 clearResourceAndEventBaseReferences();
343 setAttributeName(anyQName());
344 animationAttributeChanged();
345 m_timeContainer = nullptr;
348 SVGElement::removedFrom(rootParent);
351 bool SVGSMILElement::hasValidAttributeName()
353 return attributeName() != anyQName();
356 SMILTime SVGSMILElement::parseOffsetValue(const String& data)
360 String parse = data.stripWhiteSpace();
361 if (parse.endsWith('h'))
362 result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
363 else if (parse.endsWith("min"))
364 result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
365 else if (parse.endsWith("ms"))
366 result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
367 else if (parse.endsWith('s'))
368 result = parse.left(parse.length() - 1).toDouble(&ok);
370 result = parse.toDouble(&ok);
372 return SMILTime::unresolved();
376 SMILTime SVGSMILElement::parseClockValue(const String& data)
379 return SMILTime::unresolved();
381 String parse = data.stripWhiteSpace();
383 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
384 if (parse == indefiniteValue)
385 return SMILTime::indefinite();
389 size_t doublePointOne = parse.find(':');
390 size_t doublePointTwo = parse.find(':', doublePointOne + 1);
391 if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) {
392 result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
394 return SMILTime::unresolved();
395 result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
397 return SMILTime::unresolved();
398 result += parse.substring(6).toDouble(&ok);
399 } else if (doublePointOne == 2 && doublePointTwo == kNotFound && parse.length() >= 5) {
400 result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
402 return SMILTime::unresolved();
403 result += parse.substring(3).toDouble(&ok);
405 return parseOffsetValue(parse);
408 return SMILTime::unresolved();
412 static void sortTimeList(Vector<SMILTimeWithOrigin>& timeList)
414 std::sort(timeList.begin(), timeList.end());
417 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd)
419 String parseString = value.stripWhiteSpace();
423 size_t pos = parseString.find('+');
424 if (pos == kNotFound) {
425 pos = parseString.find('-');
426 if (pos != kNotFound)
429 String conditionString;
431 if (pos == kNotFound)
432 conditionString = parseString;
434 conditionString = parseString.left(pos).stripWhiteSpace();
435 String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
436 offset = parseOffsetValue(offsetString);
437 if (offset.isUnresolved())
439 offset = offset * sign;
441 if (conditionString.isEmpty())
443 pos = conditionString.find('.');
447 if (pos == kNotFound)
448 nameString = conditionString;
450 baseID = conditionString.left(pos);
451 nameString = conditionString.substring(pos + 1);
453 if (nameString.isEmpty())
456 Condition::Type type;
458 if (nameString.startsWith("repeat(") && nameString.endsWith(')')) {
459 repeat = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
462 nameString = "repeatn";
463 type = Condition::EventBase;
464 } else if (nameString == "begin" || nameString == "end") {
465 if (baseID.isEmpty())
467 type = Condition::Syncbase;
468 } else if (nameString.startsWith("accesskey(")) {
469 // FIXME: accesskey() support.
470 type = Condition::AccessKey;
472 type = Condition::EventBase;
474 m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeat));
476 if (type == Condition::EventBase && beginOrEnd == End)
477 m_hasEndEventConditions = true;
482 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd)
484 Vector<SMILTimeWithOrigin>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
485 if (beginOrEnd == End)
486 m_hasEndEventConditions = false;
487 HashSet<double> existing;
488 for (unsigned n = 0; n < timeList.size(); ++n)
489 existing.add(timeList[n].time().value());
490 Vector<String> splitString;
491 parseString.split(';', splitString);
492 for (unsigned n = 0; n < splitString.size(); ++n) {
493 SMILTime value = parseClockValue(splitString[n]);
494 if (value.isUnresolved())
495 parseCondition(splitString[n], beginOrEnd);
496 else if (!existing.contains(value.value()))
497 timeList.append(SMILTimeWithOrigin(value, SMILTimeWithOrigin::ParserOrigin));
499 sortTimeList(timeList);
502 bool SVGSMILElement::isSupportedAttribute(const QualifiedName& attrName)
504 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
505 if (supportedAttributes.isEmpty()) {
506 supportedAttributes.add(SVGNames::beginAttr);
507 supportedAttributes.add(SVGNames::endAttr);
508 supportedAttributes.add(SVGNames::durAttr);
509 supportedAttributes.add(SVGNames::repeatDurAttr);
510 supportedAttributes.add(SVGNames::repeatCountAttr);
511 supportedAttributes.add(SVGNames::minAttr);
512 supportedAttributes.add(SVGNames::maxAttr);
513 supportedAttributes.add(SVGNames::attributeNameAttr);
514 supportedAttributes.add(XLinkNames::hrefAttr);
516 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
519 void SVGSMILElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
521 if (name == SVGNames::beginAttr) {
522 if (!m_conditions.isEmpty()) {
524 parseBeginOrEnd(fastGetAttribute(SVGNames::endAttr), End);
526 parseBeginOrEnd(value.string(), Begin);
528 connectSyncBaseConditions();
529 } else if (name == SVGNames::endAttr) {
530 if (!m_conditions.isEmpty()) {
532 parseBeginOrEnd(fastGetAttribute(SVGNames::beginAttr), Begin);
534 parseBeginOrEnd(value.string(), End);
536 connectSyncBaseConditions();
537 } else if (name == SVGNames::onbeginAttr) {
538 setAttributeEventListener(EventTypeNames::beginEvent, createAttributeEventListener(this, name, value));
539 } else if (name == SVGNames::onendAttr) {
540 setAttributeEventListener(EventTypeNames::endEvent, createAttributeEventListener(this, name, value));
541 } else if (name == SVGNames::onrepeatAttr) {
542 setAttributeEventListener(EventTypeNames::repeatEvent, createAttributeEventListener(this, name, value));
544 SVGElement::parseAttribute(name, value);
547 void SVGSMILElement::svgAttributeChanged(const QualifiedName& attrName)
549 if (!isSupportedAttribute(attrName)) {
550 SVGElement::svgAttributeChanged(attrName);
554 if (attrName == SVGNames::durAttr)
555 m_cachedDur = invalidCachedTime;
556 else if (attrName == SVGNames::repeatDurAttr)
557 m_cachedRepeatDur = invalidCachedTime;
558 else if (attrName == SVGNames::repeatCountAttr)
559 m_cachedRepeatCount = invalidCachedTime;
560 else if (attrName == SVGNames::minAttr)
561 m_cachedMin = invalidCachedTime;
562 else if (attrName == SVGNames::maxAttr)
563 m_cachedMax = invalidCachedTime;
564 else if (attrName == SVGNames::attributeNameAttr)
565 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
566 else if (attrName.matches(XLinkNames::hrefAttr)) {
567 SVGElement::InvalidationGuard invalidationGuard(this);
568 buildPendingResource();
570 clearAnimatedType(m_targetElement);
571 } else if (inDocument()) {
572 if (attrName == SVGNames::beginAttr)
573 beginListChanged(elapsed());
574 else if (attrName == SVGNames::endAttr)
575 endListChanged(elapsed());
578 animationAttributeChanged();
581 inline SVGElement* SVGSMILElement::eventBaseFor(const Condition& condition)
583 Element* eventBase = condition.m_baseID.isEmpty() ? targetElement() : treeScope().getElementById(AtomicString(condition.m_baseID));
584 if (eventBase && eventBase->isSVGElement())
585 return toSVGElement(eventBase);
589 void SVGSMILElement::connectSyncBaseConditions()
591 if (m_syncBaseConditionsConnected)
592 disconnectSyncBaseConditions();
593 m_syncBaseConditionsConnected = true;
594 for (unsigned n = 0; n < m_conditions.size(); ++n) {
595 Condition& condition = m_conditions[n];
596 if (condition.m_type == Condition::Syncbase) {
597 ASSERT(!condition.m_baseID.isEmpty());
598 condition.m_syncbase = treeScope().getElementById(AtomicString(condition.m_baseID));
599 if (!condition.m_syncbase || !isSVGSMILElement(*condition.m_syncbase)) {
600 condition.m_syncbase = nullptr;
603 toSVGSMILElement(condition.m_syncbase.get())->addSyncBaseDependent(this);
608 void SVGSMILElement::disconnectSyncBaseConditions()
610 if (!m_syncBaseConditionsConnected)
612 m_syncBaseConditionsConnected = false;
613 for (unsigned n = 0; n < m_conditions.size(); ++n) {
614 Condition& condition = m_conditions[n];
615 if (condition.m_type == Condition::Syncbase) {
616 if (condition.m_syncbase)
617 toSVGSMILElement(condition.m_syncbase.get())->removeSyncBaseDependent(this);
618 condition.m_syncbase = nullptr;
623 void SVGSMILElement::connectEventBaseConditions()
625 disconnectEventBaseConditions();
626 for (unsigned n = 0; n < m_conditions.size(); ++n) {
627 Condition& condition = m_conditions[n];
628 if (condition.m_type == Condition::EventBase) {
629 ASSERT(!condition.m_syncbase);
630 SVGElement* eventBase = eventBaseFor(condition);
632 if (!condition.m_baseID.isEmpty() && !document().accessSVGExtensions().isElementPendingResource(this, AtomicString(condition.m_baseID)))
633 document().accessSVGExtensions().addPendingResource(AtomicString(condition.m_baseID), this);
636 ASSERT(!condition.m_eventListener);
637 condition.m_eventListener = ConditionEventListener::create(this, &condition);
638 eventBase->addEventListener(AtomicString(condition.m_name), condition.m_eventListener, false);
639 document().accessSVGExtensions().addElementReferencingTarget(this, eventBase);
644 void SVGSMILElement::disconnectEventBaseConditions()
646 for (unsigned n = 0; n < m_conditions.size(); ++n) {
647 Condition& condition = m_conditions[n];
648 if (condition.m_type == Condition::EventBase) {
649 ASSERT(!condition.m_syncbase);
650 if (!condition.m_eventListener)
652 // Note: It's a memory optimization to try to remove our condition
653 // event listener, but it's not guaranteed to work, since we have
654 // no guarantee that eventBaseFor() will be able to find our condition's
655 // original eventBase. So, we also have to disconnect ourselves from
656 // our condition event listener, in case it later fires.
657 SVGElement* eventBase = eventBaseFor(condition);
659 eventBase->removeEventListener(AtomicString(condition.m_name), condition.m_eventListener.get(), false);
660 condition.m_eventListener->disconnectAnimation();
661 condition.m_eventListener = nullptr;
666 void SVGSMILElement::setAttributeName(const QualifiedName& attributeName)
668 if (m_timeContainer && m_targetElement && m_attributeName != attributeName) {
669 if (hasValidAttributeName())
670 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
671 m_attributeName = attributeName;
672 if (hasValidAttributeName())
673 m_timeContainer->schedule(this, m_targetElement, m_attributeName);
675 m_attributeName = attributeName;
677 // Only clear the animated type, if we had a target before.
679 clearAnimatedType(m_targetElement);
682 void SVGSMILElement::setTargetElement(SVGElement* target)
684 if (m_timeContainer && hasValidAttributeName()) {
686 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
688 m_timeContainer->schedule(this, target, m_attributeName);
691 if (m_targetElement) {
692 // Clear values that may depend on the previous target.
693 clearAnimatedType(m_targetElement);
694 disconnectSyncBaseConditions();
697 // If the animation state is not Inactive, always reset to a clear state before leaving the old target element.
698 if (m_activeState != Inactive)
699 endedActiveInterval();
701 m_targetElement = target;
704 SMILTime SVGSMILElement::elapsed() const
706 return m_timeContainer ? m_timeContainer->elapsed() : 0;
709 bool SVGSMILElement::isFrozen() const
711 return m_activeState == Frozen;
714 SVGSMILElement::Restart SVGSMILElement::restart() const
716 DEFINE_STATIC_LOCAL(const AtomicString, never, ("never", AtomicString::ConstructFromLiteral));
717 DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive", AtomicString::ConstructFromLiteral));
718 const AtomicString& value = fastGetAttribute(SVGNames::restartAttr);
721 if (value == whenNotActive)
722 return RestartWhenNotActive;
723 return RestartAlways;
726 SVGSMILElement::FillMode SVGSMILElement::fill() const
728 DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze", AtomicString::ConstructFromLiteral));
729 const AtomicString& value = fastGetAttribute(SVGNames::fillAttr);
730 return value == freeze ? FillFreeze : FillRemove;
733 SMILTime SVGSMILElement::dur() const
735 if (m_cachedDur != invalidCachedTime)
737 const AtomicString& value = fastGetAttribute(SVGNames::durAttr);
738 SMILTime clockValue = parseClockValue(value);
739 return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
742 SMILTime SVGSMILElement::repeatDur() const
744 if (m_cachedRepeatDur != invalidCachedTime)
745 return m_cachedRepeatDur;
746 const AtomicString& value = fastGetAttribute(SVGNames::repeatDurAttr);
747 SMILTime clockValue = parseClockValue(value);
748 m_cachedRepeatDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
749 return m_cachedRepeatDur;
752 // So a count is not really a time but let just all pretend we did not notice.
753 SMILTime SVGSMILElement::repeatCount() const
755 if (m_cachedRepeatCount != invalidCachedTime)
756 return m_cachedRepeatCount;
757 SMILTime computedRepeatCount = SMILTime::unresolved();
758 const AtomicString& value = fastGetAttribute(SVGNames::repeatCountAttr);
759 if (!value.isNull()) {
760 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
761 if (value == indefiniteValue) {
762 computedRepeatCount = SMILTime::indefinite();
765 double result = value.string().toDouble(&ok);
766 if (ok && result > 0)
767 computedRepeatCount = result;
770 m_cachedRepeatCount = computedRepeatCount;
771 return m_cachedRepeatCount;
774 SMILTime SVGSMILElement::maxValue() const
776 if (m_cachedMax != invalidCachedTime)
778 const AtomicString& value = fastGetAttribute(SVGNames::maxAttr);
779 SMILTime result = parseClockValue(value);
780 return m_cachedMax = (result.isUnresolved() || result <= 0) ? SMILTime::indefinite() : result;
783 SMILTime SVGSMILElement::minValue() const
785 if (m_cachedMin != invalidCachedTime)
787 const AtomicString& value = fastGetAttribute(SVGNames::minAttr);
788 SMILTime result = parseClockValue(value);
789 return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
792 SMILTime SVGSMILElement::simpleDuration() const
794 return min(dur(), SMILTime::indefinite());
797 void SVGSMILElement::addBeginTime(SMILTime eventTime, SMILTime beginTime, SMILTimeWithOrigin::Origin origin)
799 ASSERT(!std::isnan(beginTime.value()));
800 m_beginTimes.append(SMILTimeWithOrigin(beginTime, origin));
801 sortTimeList(m_beginTimes);
802 beginListChanged(eventTime);
805 void SVGSMILElement::addEndTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin origin)
807 ASSERT(!std::isnan(endTime.value()));
808 m_endTimes.append(SMILTimeWithOrigin(endTime, origin));
809 sortTimeList(m_endTimes);
810 endListChanged(eventTime);
813 inline bool compareTimes(const SMILTimeWithOrigin& left, const SMILTimeWithOrigin& right)
815 return left.time() < right.time();
818 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
820 const Vector<SMILTimeWithOrigin>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
821 int sizeOfList = list.size();
824 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
826 const SMILTimeWithOrigin dummyTimeWithOrigin(minimumTime, SMILTimeWithOrigin::ParserOrigin);
827 const SMILTimeWithOrigin* result = std::lower_bound(list.begin(), list.end(), dummyTimeWithOrigin, compareTimes);
828 int indexOfResult = result - list.begin();
829 if (indexOfResult == sizeOfList)
830 return SMILTime::unresolved();
831 const SMILTime& currentTime = list[indexOfResult].time();
833 // The special value "indefinite" does not yield an instance time in the begin list.
834 if (currentTime.isIndefinite() && beginOrEnd == Begin)
835 return SMILTime::unresolved();
837 if (currentTime > minimumTime)
840 ASSERT(currentTime == minimumTime);
844 // If the equals is not accepted, return the next bigger item in the list.
845 SMILTime nextTime = currentTime;
846 while (indexOfResult < sizeOfList - 1) {
847 nextTime = list[indexOfResult + 1].time();
848 if (nextTime > minimumTime)
853 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
856 SMILTime SVGSMILElement::repeatingDuration() const
858 // Computing the active duration
859 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
860 SMILTime repeatCount = this->repeatCount();
861 SMILTime repeatDur = this->repeatDur();
862 SMILTime simpleDuration = this->simpleDuration();
863 if (!simpleDuration || (repeatDur.isUnresolved() && repeatCount.isUnresolved()))
864 return simpleDuration;
865 SMILTime repeatCountDuration = simpleDuration * repeatCount;
866 return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
869 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
871 // Computing the active duration
872 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
873 SMILTime preliminaryActiveDuration;
874 if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
875 preliminaryActiveDuration = resolvedEnd - resolvedBegin;
876 else if (!resolvedEnd.isFinite())
877 preliminaryActiveDuration = repeatingDuration();
879 preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
881 SMILTime minValue = this->minValue();
882 SMILTime maxValue = this->maxValue();
883 if (minValue > maxValue) {
885 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
887 maxValue = SMILTime::indefinite();
889 return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
892 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const
894 // See the pseudocode in http://www.w3.org/TR/SMIL3/smil-timing.html#q90.
895 SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
896 SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
898 bool equalsMinimumOK = !first || m_intervalEnd > m_intervalBegin;
899 SMILTime tempBegin = findInstanceTime(Begin, beginAfter, equalsMinimumOK);
900 if (tempBegin.isUnresolved())
903 if (m_endTimes.isEmpty())
904 tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
906 tempEnd = findInstanceTime(End, tempBegin, true);
907 if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd))
908 tempEnd = findInstanceTime(End, tempBegin, false);
909 if (tempEnd.isUnresolved()) {
910 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions)
913 tempEnd = resolveActiveEnd(tempBegin, tempEnd);
915 if (!first || (tempEnd > 0 || (!tempBegin.value() && !tempEnd.value()))) {
916 beginResult = tempBegin;
921 beginAfter = tempEnd;
922 lastIntervalTempEnd = tempEnd;
924 beginResult = SMILTime::unresolved();
925 endResult = SMILTime::unresolved();
928 void SVGSMILElement::resolveFirstInterval()
932 resolveInterval(true, begin, end);
933 ASSERT(!begin.isIndefinite());
935 if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {
936 m_intervalBegin = begin;
938 notifyDependentsIntervalChanged();
939 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
942 m_timeContainer->notifyIntervalsChanged();
946 bool SVGSMILElement::resolveNextInterval()
950 resolveInterval(false, begin, end);
951 ASSERT(!begin.isIndefinite());
953 if (!begin.isUnresolved() && begin != m_intervalBegin) {
954 m_intervalBegin = begin;
956 notifyDependentsIntervalChanged();
957 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
964 SMILTime SVGSMILElement::nextProgressTime() const
966 return m_nextProgressTime;
969 void SVGSMILElement::beginListChanged(SMILTime eventTime)
971 if (m_isWaitingForFirstInterval)
972 resolveFirstInterval();
974 SMILTime newBegin = findInstanceTime(Begin, eventTime, true);
975 if (newBegin.isFinite() && (m_intervalEnd <= eventTime || newBegin < m_intervalBegin)) {
976 // Begin time changed, re-resolve the interval.
977 SMILTime oldBegin = m_intervalBegin;
978 m_intervalEnd = eventTime;
979 resolveInterval(false, m_intervalBegin, m_intervalEnd);
980 ASSERT(!m_intervalBegin.isUnresolved());
981 if (m_intervalBegin != oldBegin) {
982 if (m_activeState == Active && m_intervalBegin > eventTime) {
983 m_activeState = determineActiveState(eventTime);
984 if (m_activeState != Active)
985 endedActiveInterval();
987 notifyDependentsIntervalChanged();
991 m_nextProgressTime = elapsed();
994 m_timeContainer->notifyIntervalsChanged();
997 void SVGSMILElement::endListChanged(SMILTime)
999 SMILTime elapsed = this->elapsed();
1000 if (m_isWaitingForFirstInterval)
1001 resolveFirstInterval();
1002 else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
1003 SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
1004 if (newEnd < m_intervalEnd) {
1005 newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
1006 if (newEnd != m_intervalEnd) {
1007 m_intervalEnd = newEnd;
1008 notifyDependentsIntervalChanged();
1012 m_nextProgressTime = elapsed;
1014 if (m_timeContainer)
1015 m_timeContainer->notifyIntervalsChanged();
1018 SVGSMILElement::RestartedInterval SVGSMILElement::maybeRestartInterval(SMILTime elapsed)
1020 ASSERT(!m_isWaitingForFirstInterval);
1021 ASSERT(elapsed >= m_intervalBegin);
1023 Restart restart = this->restart();
1024 if (restart == RestartNever)
1025 return DidNotRestartInterval;
1027 if (elapsed < m_intervalEnd) {
1028 if (restart != RestartAlways)
1029 return DidNotRestartInterval;
1030 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
1031 if (nextBegin < m_intervalEnd) {
1032 m_intervalEnd = nextBegin;
1033 notifyDependentsIntervalChanged();
1037 if (elapsed >= m_intervalEnd) {
1038 if (resolveNextInterval() && elapsed >= m_intervalBegin)
1039 return DidRestartInterval;
1041 return DidNotRestartInterval;
1044 void SVGSMILElement::seekToIntervalCorrespondingToTime(SMILTime elapsed)
1046 ASSERT(!m_isWaitingForFirstInterval);
1047 ASSERT(elapsed >= m_intervalBegin);
1049 // Manually seek from interval to interval, just as if the animation would run regulary.
1051 // Figure out the next value in the begin time list after the current interval begin.
1052 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
1054 // If the 'nextBegin' time is unresolved (eg. just one defined interval), we're done seeking.
1055 if (nextBegin.isUnresolved())
1058 // If the 'nextBegin' time is larger than or equal to the current interval end time, we're done seeking.
1059 // If the 'elapsed' time is smaller than the next begin interval time, we're done seeking.
1060 if (nextBegin < m_intervalEnd && elapsed >= nextBegin) {
1061 // End current interval, and start a new interval from the 'nextBegin' time.
1062 m_intervalEnd = nextBegin;
1063 if (!resolveNextInterval())
1068 // If the desired 'elapsed' time is past the current interval, advance to the next.
1069 if (elapsed >= m_intervalEnd) {
1070 if (!resolveNextInterval())
1079 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const
1081 SMILTime simpleDuration = this->simpleDuration();
1083 if (simpleDuration.isIndefinite()) {
1087 if (!simpleDuration) {
1091 ASSERT(m_intervalBegin.isFinite());
1092 ASSERT(simpleDuration.isFinite());
1093 SMILTime activeTime = elapsed - m_intervalBegin;
1094 SMILTime repeatingDuration = this->repeatingDuration();
1095 if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
1096 repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value());
1097 if (!fmod(repeatingDuration.value(), simpleDuration.value()))
1100 double percent = (m_intervalEnd.value() - m_intervalBegin.value()) / simpleDuration.value();
1101 percent = percent - floor(percent);
1102 if (percent < numeric_limits<float>::epsilon() || 1 - percent < numeric_limits<float>::epsilon())
1104 return narrowPrecisionToFloat(percent);
1106 repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
1107 SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
1108 return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
1111 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
1113 if (m_activeState == Active) {
1114 // If duration is indefinite the value does not actually change over time. Same is true for <set>.
1115 SMILTime simpleDuration = this->simpleDuration();
1116 if (simpleDuration.isIndefinite() || isSVGSetElement(*this)) {
1117 SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
1118 // We are supposed to do freeze semantics when repeating ends, even if the element is still active.
1119 // Take care that we get a timer callback at that point.
1120 if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite())
1121 return repeatingDurationEnd;
1122 return m_intervalEnd;
1124 return elapsed + 0.025;
1126 return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
1129 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
1131 if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd)
1134 return fill() == FillFreeze ? Frozen : Inactive;
1137 bool SVGSMILElement::isContributing(SMILTime elapsed) const
1139 // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
1140 return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
1143 bool SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement, bool seekToTime)
1145 ASSERT(resultElement);
1146 ASSERT(m_timeContainer);
1147 ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
1149 if (!m_syncBaseConditionsConnected)
1150 connectSyncBaseConditions();
1152 if (!m_intervalBegin.isFinite()) {
1153 ASSERT(m_activeState == Inactive);
1154 m_nextProgressTime = SMILTime::unresolved();
1158 if (elapsed < m_intervalBegin) {
1159 ASSERT(m_activeState != Active);
1160 if (m_activeState == Frozen) {
1161 if (this == resultElement)
1162 resetAnimatedType();
1163 updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
1165 m_nextProgressTime = m_intervalBegin;
1169 m_previousIntervalBegin = m_intervalBegin;
1171 if (m_isWaitingForFirstInterval) {
1172 m_isWaitingForFirstInterval = false;
1173 resolveFirstInterval();
1176 // This call may obtain a new interval -- never call calculateAnimationPercentAndRepeat() before!
1178 seekToIntervalCorrespondingToTime(elapsed);
1179 if (elapsed < m_intervalBegin) {
1180 // elapsed is not within an interval.
1181 m_nextProgressTime = m_intervalBegin;
1186 unsigned repeat = 0;
1187 float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
1188 RestartedInterval restartedInterval = maybeRestartInterval(elapsed);
1190 ActiveState oldActiveState = m_activeState;
1191 m_activeState = determineActiveState(elapsed);
1192 bool animationIsContributing = isContributing(elapsed);
1194 // 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.
1195 if (this == resultElement && animationIsContributing)
1196 resetAnimatedType();
1198 if (animationIsContributing) {
1199 if (oldActiveState == Inactive || restartedInterval == DidRestartInterval) {
1200 smilBeginEventSender().dispatchEventSoon(this);
1201 startedActiveInterval();
1204 if (repeat && repeat != m_lastRepeat)
1205 dispatchRepeatEvents(repeat);
1207 updateAnimation(percent, repeat, resultElement);
1208 m_lastPercent = percent;
1209 m_lastRepeat = repeat;
1212 if ((oldActiveState == Active && m_activeState != Active) || restartedInterval == DidRestartInterval) {
1213 smilEndEventSender().dispatchEventSoon(this);
1214 endedActiveInterval();
1215 if (!animationIsContributing && this == resultElement)
1216 clearAnimatedType(m_targetElement);
1219 // Triggering all the pending events if the animation timeline is changed.
1221 if (m_activeState == Inactive)
1222 smilBeginEventSender().dispatchEventSoon(this);
1225 for (unsigned repeatEventCount = 1; repeatEventCount < repeat; repeatEventCount++)
1226 dispatchRepeatEvents(repeatEventCount);
1227 if (m_activeState == Inactive)
1228 dispatchRepeatEvents(repeat);
1231 if (m_activeState == Inactive || m_activeState == Frozen)
1232 smilEndEventSender().dispatchEventSoon(this);
1235 m_nextProgressTime = calculateNextProgressTime(elapsed);
1236 return animationIsContributing;
1239 void SVGSMILElement::notifyDependentsIntervalChanged()
1241 ASSERT(m_intervalBegin.isFinite());
1242 DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ());
1243 if (!loopBreaker.add(this).isNewEntry)
1246 TimeDependentSet::iterator end = m_syncBaseDependents.end();
1247 for (TimeDependentSet::iterator it = m_syncBaseDependents.begin(); it != end; ++it) {
1248 SVGSMILElement* dependent = *it;
1249 dependent->createInstanceTimesFromSyncbase(this);
1252 loopBreaker.remove(this);
1255 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase)
1257 // FIXME: To be really correct, this should handle updating exising interval by changing
1258 // the associated times instead of creating new ones.
1259 for (unsigned n = 0; n < m_conditions.size(); ++n) {
1260 Condition& condition = m_conditions[n];
1261 if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
1262 ASSERT(condition.m_name == "begin" || condition.m_name == "end");
1263 // No nested time containers in SVG, no need for crazy time space conversions. Phew!
1265 if (condition.m_name == "begin")
1266 time = syncbase->m_intervalBegin + condition.m_offset;
1268 time = syncbase->m_intervalEnd + condition.m_offset;
1269 if (!time.isFinite())
1271 if (condition.m_beginOrEnd == Begin)
1272 addBeginTime(elapsed(), time);
1274 addEndTime(elapsed(), time);
1279 void SVGSMILElement::addSyncBaseDependent(SVGSMILElement* animation)
1281 m_syncBaseDependents.add(animation);
1282 if (m_intervalBegin.isFinite())
1283 animation->createInstanceTimesFromSyncbase(this);
1286 void SVGSMILElement::removeSyncBaseDependent(SVGSMILElement* animation)
1288 m_syncBaseDependents.remove(animation);
1291 void SVGSMILElement::handleConditionEvent(Event* event, Condition* condition)
1293 if (event->type() == "repeatn" && toRepeatEvent(event)->repeat() != condition->m_repeat)
1296 SMILTime elapsed = this->elapsed();
1297 if (condition->m_beginOrEnd == Begin)
1298 addBeginTime(elapsed, elapsed + condition->m_offset);
1300 addEndTime(elapsed, elapsed + condition->m_offset);
1303 void SVGSMILElement::beginByLinkActivation()
1305 SMILTime elapsed = this->elapsed();
1306 addBeginTime(elapsed, elapsed);
1309 void SVGSMILElement::endedActiveInterval()
1311 clearTimesWithDynamicOrigins(m_beginTimes);
1312 clearTimesWithDynamicOrigins(m_endTimes);
1315 void SVGSMILElement::dispatchRepeatEvents(unsigned count)
1317 m_repeatEventCountList.append(count);
1318 smilRepeatEventSender().dispatchEventSoon(this);
1319 smilRepeatNEventSender().dispatchEventSoon(this);
1322 void SVGSMILElement::dispatchPendingEvent(SMILEventSender* eventSender)
1324 ASSERT(eventSender == &smilEndEventSender() || eventSender == &smilBeginEventSender() || eventSender == &smilRepeatEventSender() || eventSender == &smilRepeatNEventSender());
1325 const AtomicString& eventType = eventSender->eventType();
1326 if (eventType == "repeatn") {
1327 unsigned repeatEventCount = m_repeatEventCountList.first();
1328 m_repeatEventCountList.remove(0);
1329 dispatchEvent(RepeatEvent::create(eventType, repeatEventCount));
1331 dispatchEvent(Event::create(eventType));
1335 void SVGSMILElement::trace(Visitor* visitor)
1337 visitor->trace(m_targetElement);
1338 SVGElement::trace(visitor);