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.
29 #include "SVGSMILElement.h"
31 #include "Attribute.h"
32 #include "CSSPropertyNames.h"
35 #include "EventListener.h"
36 #include "FloatConversion.h"
37 #include "FrameView.h"
38 #include "HTMLNames.h"
39 #include "SMILTimeContainer.h"
40 #include "SVGDocumentExtensions.h"
42 #include "SVGParserUtilities.h"
43 #include "SVGSVGElement.h"
44 #include "SVGURIReference.h"
45 #include "XLinkNames.h"
46 #include <wtf/MathExtras.h>
47 #include <wtf/StdLibExtras.h>
48 #include <wtf/Vector.h>
54 // This is used for duration type time values that can't be negative.
55 static const double invalidCachedTime = -1.;
57 class ConditionEventListener : public EventListener {
59 static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
61 return adoptRef(new ConditionEventListener(animation, condition));
64 static const ConditionEventListener* cast(const EventListener* listener)
66 return listener->type() == ConditionEventListenerType
67 ? static_cast<const ConditionEventListener*>(listener)
71 virtual bool operator==(const EventListener& other);
73 void disconnectAnimation()
79 ConditionEventListener(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
80 : EventListener(ConditionEventListenerType)
81 , m_animation(animation)
82 , m_condition(condition)
86 virtual void handleEvent(ScriptExecutionContext*, Event*);
88 SVGSMILElement* m_animation;
89 SVGSMILElement::Condition* m_condition;
92 bool ConditionEventListener::operator==(const EventListener& listener)
94 if (const ConditionEventListener* conditionEventListener = ConditionEventListener::cast(&listener))
95 return m_animation == conditionEventListener->m_animation && m_condition == conditionEventListener->m_condition;
99 void ConditionEventListener::handleEvent(ScriptExecutionContext*, Event* event)
103 m_animation->handleConditionEvent(event, m_condition);
106 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeats)
108 , m_beginOrEnd(beginOrEnd)
116 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document* doc)
117 : SVGElement(tagName, doc)
118 , m_attributeName(anyQName())
120 , m_conditionsConnected(false)
121 , m_hasEndEventConditions(false)
122 , m_isWaitingForFirstInterval(true)
123 , m_intervalBegin(SMILTime::unresolved())
124 , m_intervalEnd(SMILTime::unresolved())
125 , m_previousIntervalBegin(SMILTime::unresolved())
126 , m_activeState(Inactive)
129 , m_nextProgressTime(0)
130 , m_documentOrderIndex(0)
131 , m_cachedDur(invalidCachedTime)
132 , m_cachedRepeatDur(invalidCachedTime)
133 , m_cachedRepeatCount(invalidCachedTime)
134 , m_cachedMin(invalidCachedTime)
135 , m_cachedMax(invalidCachedTime)
137 resolveFirstInterval();
140 SVGSMILElement::~SVGSMILElement()
142 clearResourceReferences();
143 disconnectConditions();
144 if (m_timeContainer && m_targetElement && hasValidAttributeName())
145 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
148 void SVGSMILElement::clearResourceReferences()
151 document()->accessSVGExtensions()->removeAllTargetReferencesForElement(this);
154 void SVGSMILElement::buildPendingResource()
156 clearResourceReferences();
161 String href = getAttribute(XLinkNames::hrefAttr);
164 target = parentNode() && parentNode()->isElementNode() ? static_cast<Element*>(parentNode()) : 0;
166 target = SVGURIReference::targetElementFromIRIString(href, document(), &id);
167 SVGElement* svgTarget = target && target->isSVGElement() ? static_cast<SVGElement*>(target) : 0;
169 if (svgTarget != targetElement())
170 setTargetElement(svgTarget);
173 // Do not register as pending if we are already pending this resource.
174 if (document()->accessSVGExtensions()->isElementPendingResource(this, id))
178 document()->accessSVGExtensions()->addPendingResource(id, this);
179 ASSERT(hasPendingResources());
182 // Register us with the target in the dependencies map. Any change of hrefElement
183 // that leads to relayout/repainting now informs us, so we can react to it.
184 document()->accessSVGExtensions()->addElementReferencingTarget(this, svgTarget);
188 static inline QualifiedName constructQualifiedName(const SVGElement* svgElement, const String& attributeName)
191 if (attributeName.isEmpty())
193 if (!attributeName.contains(':'))
194 return QualifiedName(nullAtom, attributeName, nullAtom);
198 ExceptionCode ec = 0;
199 if (!Document::parseQualifiedName(attributeName, prefix, localName, ec))
203 String namespaceURI = svgElement->lookupNamespaceURI(prefix);
204 if (namespaceURI.isEmpty())
207 return QualifiedName(nullAtom, localName, namespaceURI);
210 static inline void clearTimesWithDynamicOrigins(Vector<SMILTimeWithOrigin>& timeList)
212 for (int i = timeList.size() - 1; i >= 0; --i) {
213 if (timeList[i].originIsScript())
218 void SVGSMILElement::reset()
220 clearAnimatedType(m_targetElement);
222 m_activeState = Inactive;
223 m_isWaitingForFirstInterval = true;
224 m_intervalBegin = SMILTime::unresolved();
225 m_intervalEnd = SMILTime::unresolved();
226 m_previousIntervalBegin = SMILTime::unresolved();
229 m_nextProgressTime = 0;
230 resolveFirstInterval();
233 Node::InsertionNotificationRequest SVGSMILElement::insertedInto(ContainerNode* rootParent)
235 SVGElement::insertedInto(rootParent);
236 if (!rootParent->inDocument())
237 return InsertionDone;
239 // Verify we are not in <use> instance tree.
240 ASSERT(!isInShadowTree());
242 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
243 SVGSVGElement* owner = ownerSVGElement();
245 return InsertionDone;
247 m_timeContainer = owner->timeContainer();
248 ASSERT(m_timeContainer);
249 m_timeContainer->setDocumentOrderIndexesDirty();
251 // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
252 if (!fastHasAttribute(SVGNames::beginAttr))
253 m_beginTimes.append(SMILTimeWithOrigin());
255 if (m_isWaitingForFirstInterval)
256 resolveFirstInterval();
259 m_timeContainer->notifyIntervalsChanged();
261 buildPendingResource();
263 return InsertionDone;
266 void SVGSMILElement::removedFrom(ContainerNode* rootParent)
268 if (rootParent->inDocument()) {
269 if (m_timeContainer) {
270 if (m_targetElement && hasValidAttributeName())
271 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
274 // Calling disconnectConditions() may kill us if there are syncbase conditions.
275 // OK, but we don't want to die inside the call.
276 RefPtr<SVGSMILElement> keepAlive(this);
277 disconnectConditions();
279 clearResourceReferences();
281 setAttributeName(anyQName());
282 animationAttributeChanged();
285 SVGElement::removedFrom(rootParent);
288 bool SVGSMILElement::hasValidAttributeName()
290 return attributeName() != anyQName();
293 SMILTime SVGSMILElement::parseOffsetValue(const String& data)
297 String parse = data.stripWhiteSpace();
298 if (parse.endsWith('h'))
299 result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
300 else if (parse.endsWith("min"))
301 result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
302 else if (parse.endsWith("ms"))
303 result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
304 else if (parse.endsWith('s'))
305 result = parse.left(parse.length() - 1).toDouble(&ok);
307 result = parse.toDouble(&ok);
309 return SMILTime::unresolved();
313 SMILTime SVGSMILElement::parseClockValue(const String& data)
316 return SMILTime::unresolved();
318 String parse = data.stripWhiteSpace();
320 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
321 if (parse == indefiniteValue)
322 return SMILTime::indefinite();
326 size_t doublePointOne = parse.find(':');
327 size_t doublePointTwo = parse.find(':', doublePointOne + 1);
328 if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) {
329 result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
331 return SMILTime::unresolved();
332 result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
334 return SMILTime::unresolved();
335 result += parse.substring(6).toDouble(&ok);
336 } else if (doublePointOne == 2 && doublePointTwo == notFound && parse.length() >= 5) {
337 result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
339 return SMILTime::unresolved();
340 result += parse.substring(3).toDouble(&ok);
342 return parseOffsetValue(parse);
345 return SMILTime::unresolved();
349 static void sortTimeList(Vector<SMILTimeWithOrigin>& timeList)
351 std::sort(timeList.begin(), timeList.end());
354 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd)
356 String parseString = value.stripWhiteSpace();
360 size_t pos = parseString.find('+');
361 if (pos == notFound) {
362 pos = parseString.find('-');
366 String conditionString;
369 conditionString = parseString;
371 conditionString = parseString.left(pos).stripWhiteSpace();
372 String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
373 offset = parseOffsetValue(offsetString);
374 if (offset.isUnresolved())
376 offset = offset * sign;
378 if (conditionString.isEmpty())
380 pos = conditionString.find('.');
385 nameString = conditionString;
387 baseID = conditionString.left(pos);
388 nameString = conditionString.substring(pos + 1);
390 if (nameString.isEmpty())
393 Condition::Type type;
395 if (nameString.startsWith("repeat(") && nameString.endsWith(')')) {
396 // FIXME: For repeat events we just need to add the data carrying TimeEvent class and
397 // fire the events at appropiate times.
398 repeats = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
401 nameString = "repeat";
402 type = Condition::EventBase;
403 } else if (nameString == "begin" || nameString == "end") {
404 if (baseID.isEmpty())
406 type = Condition::Syncbase;
407 } else if (nameString.startsWith("accesskey(")) {
408 // FIXME: accesskey() support.
409 type = Condition::AccessKey;
411 type = Condition::EventBase;
413 m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeats));
415 if (type == Condition::EventBase && beginOrEnd == End)
416 m_hasEndEventConditions = true;
421 bool SVGSMILElement::isSMILElement(Node* node)
425 return node->hasTagName(SVGNames::setTag) || node->hasTagName(SVGNames::animateTag) || node->hasTagName(SVGNames::animateMotionTag)
426 || node->hasTagName(SVGNames::animateTransformTag) || node->hasTagName(SVGNames::animateColorTag);
429 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd)
431 Vector<SMILTimeWithOrigin>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
432 if (beginOrEnd == End)
433 m_hasEndEventConditions = false;
434 HashSet<double> existing;
435 for (unsigned n = 0; n < timeList.size(); ++n)
436 existing.add(timeList[n].time().value());
437 Vector<String> splitString;
438 parseString.split(';', splitString);
439 for (unsigned n = 0; n < splitString.size(); ++n) {
440 SMILTime value = parseClockValue(splitString[n]);
441 if (value.isUnresolved())
442 parseCondition(splitString[n], beginOrEnd);
443 else if (!existing.contains(value.value()))
444 timeList.append(SMILTimeWithOrigin(value, SMILTimeWithOrigin::ParserOrigin));
446 sortTimeList(timeList);
449 bool SVGSMILElement::isSupportedAttribute(const QualifiedName& attrName)
451 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
452 if (supportedAttributes.isEmpty()) {
453 supportedAttributes.add(SVGNames::beginAttr);
454 supportedAttributes.add(SVGNames::endAttr);
455 supportedAttributes.add(SVGNames::durAttr);
456 supportedAttributes.add(SVGNames::repeatDurAttr);
457 supportedAttributes.add(SVGNames::repeatCountAttr);
458 supportedAttributes.add(SVGNames::minAttr);
459 supportedAttributes.add(SVGNames::maxAttr);
460 supportedAttributes.add(SVGNames::attributeNameAttr);
461 supportedAttributes.add(XLinkNames::hrefAttr);
463 return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
466 void SVGSMILElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
468 if (name == SVGNames::beginAttr) {
469 if (!m_conditions.isEmpty()) {
470 disconnectConditions();
471 m_conditions.clear();
472 parseBeginOrEnd(fastGetAttribute(SVGNames::endAttr), End);
474 parseBeginOrEnd(value.string(), Begin);
477 } else if (name == SVGNames::endAttr) {
478 if (!m_conditions.isEmpty()) {
479 disconnectConditions();
480 m_conditions.clear();
481 parseBeginOrEnd(fastGetAttribute(SVGNames::beginAttr), Begin);
483 parseBeginOrEnd(value.string(), End);
487 SVGElement::parseAttribute(name, value);
490 void SVGSMILElement::svgAttributeChanged(const QualifiedName& attrName)
492 if (!isSupportedAttribute(attrName)) {
493 SVGElement::svgAttributeChanged(attrName);
497 if (attrName == SVGNames::durAttr)
498 m_cachedDur = invalidCachedTime;
499 else if (attrName == SVGNames::repeatDurAttr)
500 m_cachedRepeatDur = invalidCachedTime;
501 else if (attrName == SVGNames::repeatCountAttr)
502 m_cachedRepeatCount = invalidCachedTime;
503 else if (attrName == SVGNames::minAttr)
504 m_cachedMin = invalidCachedTime;
505 else if (attrName == SVGNames::maxAttr)
506 m_cachedMax = invalidCachedTime;
507 else if (inDocument()) {
508 if (attrName == SVGNames::beginAttr)
509 beginListChanged(elapsed());
510 else if (attrName == SVGNames::endAttr)
511 endListChanged(elapsed());
512 else if (attrName == SVGNames::attributeNameAttr)
513 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
514 else if (attrName.matches(XLinkNames::hrefAttr)) {
515 SVGElementInstance::InvalidationGuard invalidationGuard(this);
516 buildPendingResource();
520 animationAttributeChanged();
523 inline Element* SVGSMILElement::eventBaseFor(const Condition& condition)
525 return condition.m_baseID.isEmpty() ? targetElement() : treeScope()->getElementById(condition.m_baseID);
528 void SVGSMILElement::connectConditions()
530 if (m_conditionsConnected)
531 disconnectConditions();
532 m_conditionsConnected = true;
533 for (unsigned n = 0; n < m_conditions.size(); ++n) {
534 Condition& condition = m_conditions[n];
535 if (condition.m_type == Condition::EventBase) {
536 ASSERT(!condition.m_syncbase);
537 Element* eventBase = eventBaseFor(condition);
540 ASSERT(!condition.m_eventListener);
541 condition.m_eventListener = ConditionEventListener::create(this, &condition);
542 eventBase->addEventListener(condition.m_name, condition.m_eventListener, false);
543 } else if (condition.m_type == Condition::Syncbase) {
544 ASSERT(!condition.m_baseID.isEmpty());
545 condition.m_syncbase = treeScope()->getElementById(condition.m_baseID);
546 if (!isSMILElement(condition.m_syncbase.get())) {
547 condition.m_syncbase = 0;
550 SVGSMILElement* syncbase = static_cast<SVGSMILElement*>(condition.m_syncbase.get());
551 syncbase->addTimeDependent(this);
556 void SVGSMILElement::disconnectConditions()
558 if (!m_conditionsConnected)
560 m_conditionsConnected = false;
561 for (unsigned n = 0; n < m_conditions.size(); ++n) {
562 Condition& condition = m_conditions[n];
563 if (condition.m_type == Condition::EventBase) {
564 ASSERT(!condition.m_syncbase);
565 if (!condition.m_eventListener)
567 // Note: It's a memory optimization to try to remove our condition
568 // event listener, but it's not guaranteed to work, since we have
569 // no guarantee that eventBaseFor() will be able to find our condition's
570 // original eventBase. So, we also have to disconnect ourselves from
571 // our condition event listener, in case it later fires.
572 Element* eventBase = eventBaseFor(condition);
574 eventBase->removeEventListener(condition.m_name, condition.m_eventListener.get(), false);
575 condition.m_eventListener->disconnectAnimation();
576 condition.m_eventListener = 0;
577 } else if (condition.m_type == Condition::Syncbase) {
578 if (condition.m_syncbase) {
579 ASSERT(isSMILElement(condition.m_syncbase.get()));
580 static_cast<SVGSMILElement*>(condition.m_syncbase.get())->removeTimeDependent(this);
583 condition.m_syncbase = 0;
587 void SVGSMILElement::setAttributeName(const QualifiedName& attributeName)
589 if (m_timeContainer && m_targetElement && m_attributeName != attributeName) {
590 if (hasValidAttributeName())
591 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
592 m_attributeName = attributeName;
593 if (hasValidAttributeName())
594 m_timeContainer->schedule(this, m_targetElement, m_attributeName);
596 m_attributeName = attributeName;
598 // Only clear the animated type, if we had a target before.
600 clearAnimatedType(m_targetElement);
603 void SVGSMILElement::setTargetElement(SVGElement* target)
605 if (m_timeContainer && hasValidAttributeName()) {
607 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
609 m_timeContainer->schedule(this, target, m_attributeName);
612 // Only clear the animated type, if we had a target before.
614 clearAnimatedType(m_targetElement);
616 // If the animation state is not Inactive, always reset to a clear state before leaving the old target element.
617 if (m_activeState != Inactive)
618 endedActiveInterval();
620 m_targetElement = target;
623 SMILTime SVGSMILElement::elapsed() const
625 return m_timeContainer ? m_timeContainer->elapsed() : 0;
628 bool SVGSMILElement::isInactive() const
630 return m_activeState == Inactive;
633 bool SVGSMILElement::isFrozen() const
635 return m_activeState == Frozen;
638 SVGSMILElement::Restart SVGSMILElement::restart() const
640 DEFINE_STATIC_LOCAL(const AtomicString, never, ("never", AtomicString::ConstructFromLiteral));
641 DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive", AtomicString::ConstructFromLiteral));
642 const AtomicString& value = fastGetAttribute(SVGNames::restartAttr);
645 if (value == whenNotActive)
646 return RestartWhenNotActive;
647 return RestartAlways;
650 SVGSMILElement::FillMode SVGSMILElement::fill() const
652 DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze", AtomicString::ConstructFromLiteral));
653 const AtomicString& value = fastGetAttribute(SVGNames::fillAttr);
654 return value == freeze ? FillFreeze : FillRemove;
657 SMILTime SVGSMILElement::dur() const
659 if (m_cachedDur != invalidCachedTime)
661 const AtomicString& value = fastGetAttribute(SVGNames::durAttr);
662 SMILTime clockValue = parseClockValue(value);
663 return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
666 SMILTime SVGSMILElement::repeatDur() const
668 if (m_cachedRepeatDur != invalidCachedTime)
669 return m_cachedRepeatDur;
670 const AtomicString& value = fastGetAttribute(SVGNames::repeatDurAttr);
671 SMILTime clockValue = parseClockValue(value);
672 m_cachedRepeatDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
673 return m_cachedRepeatDur;
676 // So a count is not really a time but let just all pretend we did not notice.
677 SMILTime SVGSMILElement::repeatCount() const
679 if (m_cachedRepeatCount != invalidCachedTime)
680 return m_cachedRepeatCount;
681 const AtomicString& value = fastGetAttribute(SVGNames::repeatCountAttr);
683 return SMILTime::unresolved();
685 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
686 if (value == indefiniteValue)
687 return SMILTime::indefinite();
689 double result = value.string().toDouble(&ok);
690 return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved();
693 SMILTime SVGSMILElement::maxValue() const
695 if (m_cachedMax != invalidCachedTime)
697 const AtomicString& value = fastGetAttribute(SVGNames::maxAttr);
698 SMILTime result = parseClockValue(value);
699 return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result;
702 SMILTime SVGSMILElement::minValue() const
704 if (m_cachedMin != invalidCachedTime)
706 const AtomicString& value = fastGetAttribute(SVGNames::minAttr);
707 SMILTime result = parseClockValue(value);
708 return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
711 SMILTime SVGSMILElement::simpleDuration() const
713 return min(dur(), SMILTime::indefinite());
716 void SVGSMILElement::addBeginTime(SMILTime eventTime, SMILTime beginTime, SMILTimeWithOrigin::Origin origin)
718 ASSERT(!isnan(beginTime.value()));
719 m_beginTimes.append(SMILTimeWithOrigin(beginTime, origin));
720 sortTimeList(m_beginTimes);
721 beginListChanged(eventTime);
724 void SVGSMILElement::addEndTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin origin)
726 ASSERT(!isnan(endTime.value()));
727 m_endTimes.append(SMILTimeWithOrigin(endTime, origin));
728 sortTimeList(m_endTimes);
729 endListChanged(eventTime);
732 inline SMILTime extractTimeFromVector(const SMILTimeWithOrigin* position)
734 return position->time();
737 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
739 const Vector<SMILTimeWithOrigin>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
740 int sizeOfList = list.size();
743 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
745 const SMILTimeWithOrigin* result = binarySearch<const SMILTimeWithOrigin, SMILTime, extractTimeFromVector>(list.begin(), sizeOfList, minimumTime, WTF::KeyMustNotBePresentInArray);
746 int indexOfResult = result - list.begin();
747 ASSERT(indexOfResult < sizeOfList);
748 const SMILTime& currentTime = list[indexOfResult].time();
750 // The special value "indefinite" does not yield an instance time in the begin list.
751 if (currentTime.isIndefinite() && beginOrEnd == Begin)
752 return SMILTime::unresolved();
754 if (currentTime < minimumTime)
755 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
756 if (currentTime > minimumTime)
759 ASSERT(currentTime == minimumTime);
763 // If the equals is not accepted, return the next bigger item in the list.
764 SMILTime nextTime = currentTime;
765 while (indexOfResult < sizeOfList - 1) {
766 nextTime = list[indexOfResult + 1].time();
767 if (nextTime > minimumTime)
772 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
775 SMILTime SVGSMILElement::repeatingDuration() const
777 // Computing the active duration
778 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
779 SMILTime repeatCount = this->repeatCount();
780 SMILTime repeatDur = this->repeatDur();
781 SMILTime simpleDuration = this->simpleDuration();
782 if (!simpleDuration || (repeatDur.isUnresolved() && repeatCount.isUnresolved()))
783 return simpleDuration;
784 SMILTime repeatCountDuration = simpleDuration * repeatCount;
785 return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
788 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
790 // Computing the active duration
791 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
792 SMILTime preliminaryActiveDuration;
793 if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
794 preliminaryActiveDuration = resolvedEnd - resolvedBegin;
795 else if (!resolvedEnd.isFinite())
796 preliminaryActiveDuration = repeatingDuration();
798 preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
800 SMILTime minValue = this->minValue();
801 SMILTime maxValue = this->maxValue();
802 if (minValue > maxValue) {
804 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
806 maxValue = SMILTime::indefinite();
808 return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
811 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const
813 // See the pseudocode in http://www.w3.org/TR/SMIL3/smil-timing.html#q90.
814 SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
815 SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
817 bool equalsMinimumOK = !first || m_intervalEnd > m_intervalBegin;
818 SMILTime tempBegin = findInstanceTime(Begin, beginAfter, equalsMinimumOK);
819 if (tempBegin.isUnresolved())
822 if (m_endTimes.isEmpty())
823 tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
825 tempEnd = findInstanceTime(End, tempBegin, true);
826 if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd))
827 tempEnd = findInstanceTime(End, tempBegin, false);
828 if (tempEnd.isUnresolved()) {
829 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions)
832 tempEnd = resolveActiveEnd(tempBegin, tempEnd);
834 if (!first || (tempEnd > 0 || (!tempBegin.value() && !tempEnd.value()))) {
835 beginResult = tempBegin;
840 beginAfter = tempEnd;
841 lastIntervalTempEnd = tempEnd;
843 beginResult = SMILTime::unresolved();
844 endResult = SMILTime::unresolved();
847 void SVGSMILElement::resolveFirstInterval()
851 resolveInterval(true, begin, end);
852 ASSERT(!begin.isIndefinite());
854 if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {
855 bool wasUnresolved = m_intervalBegin.isUnresolved();
856 m_intervalBegin = begin;
858 notifyDependentsIntervalChanged(wasUnresolved ? NewInterval : ExistingInterval);
859 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
862 m_timeContainer->notifyIntervalsChanged();
866 void SVGSMILElement::resolveNextInterval(bool notifyDependents)
870 resolveInterval(false, begin, end);
871 ASSERT(!begin.isIndefinite());
873 if (!begin.isUnresolved() && begin != m_intervalBegin) {
874 m_intervalBegin = begin;
876 if (notifyDependents)
877 notifyDependentsIntervalChanged(NewInterval);
878 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
882 SMILTime SVGSMILElement::nextProgressTime() const
884 return m_nextProgressTime;
887 void SVGSMILElement::beginListChanged(SMILTime eventTime)
889 if (m_isWaitingForFirstInterval)
890 resolveFirstInterval();
892 SMILTime newBegin = findInstanceTime(Begin, eventTime, true);
893 if (newBegin.isFinite() && (m_intervalEnd <= eventTime || newBegin < m_intervalBegin)) {
894 // Begin time changed, re-resolve the interval.
895 SMILTime oldBegin = m_intervalBegin;
896 m_intervalEnd = eventTime;
897 resolveInterval(false, m_intervalBegin, m_intervalEnd);
898 ASSERT(!m_intervalBegin.isUnresolved());
899 if (m_intervalBegin != oldBegin) {
900 if (m_activeState == Active && m_intervalBegin > eventTime) {
901 m_activeState = determineActiveState(eventTime);
902 if (m_activeState != Active)
903 endedActiveInterval();
905 notifyDependentsIntervalChanged(ExistingInterval);
909 m_nextProgressTime = elapsed();
912 m_timeContainer->notifyIntervalsChanged();
915 void SVGSMILElement::endListChanged(SMILTime)
917 SMILTime elapsed = this->elapsed();
918 if (m_isWaitingForFirstInterval)
919 resolveFirstInterval();
920 else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
921 SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
922 if (newEnd < m_intervalEnd) {
923 newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
924 if (newEnd != m_intervalEnd) {
925 m_intervalEnd = newEnd;
926 notifyDependentsIntervalChanged(ExistingInterval);
930 m_nextProgressTime = elapsed;
933 m_timeContainer->notifyIntervalsChanged();
936 void SVGSMILElement::checkRestart(SMILTime elapsed)
938 ASSERT(!m_isWaitingForFirstInterval);
939 ASSERT(elapsed >= m_intervalBegin);
941 Restart restart = this->restart();
942 if (restart == RestartNever)
945 if (elapsed < m_intervalEnd) {
946 if (restart != RestartAlways)
948 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
949 if (nextBegin < m_intervalEnd) {
950 m_intervalEnd = nextBegin;
951 notifyDependentsIntervalChanged(ExistingInterval);
955 if (elapsed >= m_intervalEnd)
956 resolveNextInterval(true);
959 void SVGSMILElement::seekToIntervalCorrespondingToTime(SMILTime elapsed)
961 ASSERT(!m_isWaitingForFirstInterval);
962 ASSERT(elapsed >= m_intervalBegin);
964 // Manually seek from interval to interval, just as if the animation would run regulary.
966 // Figure out the next value in the begin time list after the current interval begin.
967 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
969 // If the 'nextBegin' time is unresolved (eg. just one defined interval), we're done seeking.
970 if (nextBegin.isUnresolved())
973 // If the 'nextBegin' time is larger than or equal to the current interval end time, we're done seeking.
974 // If the 'elapsed' time is smaller than the next begin interval time, we're done seeking.
975 if (nextBegin < m_intervalEnd && elapsed >= nextBegin) {
976 // End current interval, and start a new interval from the 'nextBegin' time.
977 m_intervalEnd = nextBegin;
978 resolveNextInterval(false);
982 // If the desired 'elapsed' time is past the current interval, advance to the next.
983 if (elapsed >= m_intervalEnd) {
984 resolveNextInterval(false);
992 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const
994 SMILTime simpleDuration = this->simpleDuration();
996 if (simpleDuration.isIndefinite()) {
1000 if (!simpleDuration) {
1004 ASSERT(m_intervalBegin.isFinite());
1005 ASSERT(simpleDuration.isFinite());
1006 SMILTime activeTime = elapsed - m_intervalBegin;
1007 SMILTime repeatingDuration = this->repeatingDuration();
1008 if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
1009 repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value()) - 1;
1011 double percent = (m_intervalEnd.value() - m_intervalBegin.value()) / simpleDuration.value();
1012 percent = percent - floor(percent);
1013 if (percent < numeric_limits<float>::epsilon() || 1 - percent < numeric_limits<float>::epsilon())
1015 return narrowPrecisionToFloat(percent);
1017 repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
1018 SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
1019 return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
1022 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
1024 if (m_activeState == Active) {
1025 // If duration is indefinite the value does not actually change over time. Same is true for <set>.
1026 SMILTime simpleDuration = this->simpleDuration();
1027 if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) {
1028 SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
1029 // We are supposed to do freeze semantics when repeating ends, even if the element is still active.
1030 // Take care that we get a timer callback at that point.
1031 if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite())
1032 return repeatingDurationEnd;
1033 return m_intervalEnd;
1035 return elapsed + 0.025;
1037 return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
1040 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
1042 if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd)
1045 return fill() == FillFreeze ? Frozen : Inactive;
1048 bool SVGSMILElement::isContributing(SMILTime elapsed) const
1050 // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
1051 return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
1054 bool SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement, bool seekToTime)
1056 ASSERT(resultElement);
1057 ASSERT(m_timeContainer);
1058 ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
1060 if (!m_conditionsConnected)
1061 connectConditions();
1063 if (!m_intervalBegin.isFinite()) {
1064 ASSERT(m_activeState == Inactive);
1065 m_nextProgressTime = SMILTime::unresolved();
1069 if (elapsed < m_intervalBegin) {
1070 ASSERT(m_activeState != Active);
1071 if (m_activeState == Frozen)
1072 updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
1073 m_nextProgressTime = m_intervalBegin;
1077 m_previousIntervalBegin = m_intervalBegin;
1079 if (m_isWaitingForFirstInterval) {
1080 m_isWaitingForFirstInterval = false;
1081 resolveFirstInterval();
1084 // This call may obtain a new interval -- never call calculateAnimationPercentAndRepeat() before!
1086 seekToIntervalCorrespondingToTime(elapsed);
1087 if (elapsed < m_intervalBegin) {
1088 // elapsed is not within an interval.
1089 m_nextProgressTime = m_intervalBegin;
1094 unsigned repeat = 0;
1095 float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
1096 checkRestart(elapsed);
1098 ActiveState oldActiveState = m_activeState;
1099 m_activeState = determineActiveState(elapsed);
1100 bool animationIsContributing = isContributing(elapsed);
1102 // 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.
1103 if (this == resultElement && animationIsContributing)
1104 resetAnimatedType();
1106 if (animationIsContributing) {
1107 if (oldActiveState == Inactive)
1108 startedActiveInterval();
1110 updateAnimation(percent, repeat, resultElement);
1111 m_lastPercent = percent;
1112 m_lastRepeat = repeat;
1115 if (oldActiveState == Active && m_activeState != Active) {
1116 endedActiveInterval();
1117 if (m_activeState != Frozen)
1118 clearAnimatedType(m_targetElement);
1121 m_nextProgressTime = calculateNextProgressTime(elapsed);
1122 return animationIsContributing;
1125 void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)
1127 ASSERT(m_intervalBegin.isFinite());
1128 DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ());
1129 if (loopBreaker.contains(this))
1131 loopBreaker.add(this);
1133 TimeDependentSet::iterator end = m_timeDependents.end();
1134 for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) {
1135 SVGSMILElement* dependent = *it;
1136 dependent->createInstanceTimesFromSyncbase(this, newOrExisting);
1139 loopBreaker.remove(this);
1142 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval)
1144 // FIXME: To be really correct, this should handle updating exising interval by changing
1145 // the associated times instead of creating new ones.
1146 for (unsigned n = 0; n < m_conditions.size(); ++n) {
1147 Condition& condition = m_conditions[n];
1148 if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
1149 ASSERT(condition.m_name == "begin" || condition.m_name == "end");
1150 // No nested time containers in SVG, no need for crazy time space conversions. Phew!
1152 if (condition.m_name == "begin")
1153 time = syncbase->m_intervalBegin + condition.m_offset;
1155 time = syncbase->m_intervalEnd + condition.m_offset;
1156 ASSERT(time.isFinite());
1157 if (condition.m_beginOrEnd == Begin)
1158 addBeginTime(elapsed(), time);
1160 addEndTime(elapsed(), time);
1165 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
1167 m_timeDependents.add(animation);
1168 if (m_intervalBegin.isFinite())
1169 animation->createInstanceTimesFromSyncbase(this, NewInterval);
1172 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
1174 m_timeDependents.remove(animation);
1177 void SVGSMILElement::handleConditionEvent(Event*, Condition* condition)
1179 SMILTime elapsed = this->elapsed();
1180 if (condition->m_beginOrEnd == Begin)
1181 addBeginTime(elapsed, elapsed + condition->m_offset);
1183 addEndTime(elapsed, elapsed + condition->m_offset);
1186 void SVGSMILElement::beginByLinkActivation()
1188 SMILTime elapsed = this->elapsed();
1189 addBeginTime(elapsed, elapsed);
1192 void SVGSMILElement::endedActiveInterval()
1194 clearTimesWithDynamicOrigins(m_beginTimes);
1195 clearTimesWithDynamicOrigins(m_endTimes);