1 /*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "core/svg/animation/SVGSMILElement.h"
28
29 #include "SVGNames.h"
30 #include "XLinkNames.h"
31 #include "bindings/v8/ExceptionStatePlaceholder.h"
32 #include "core/dom/Document.h"
33 #include "core/events/EventListener.h"
34 #include "core/events/EventSender.h"
35 #include "core/svg/SVGDocumentExtensions.h"
36 #include "core/svg/SVGSVGElement.h"
37 #include "core/svg/SVGURIReference.h"
38 #include "core/svg/animation/SMILTimeContainer.h"
39 #include "platform/FloatConversion.h"
40 #include "wtf/MathExtras.h"
41 #include "wtf/StdLibExtras.h"
42 #include "wtf/Vector.h"
43
44 using namespace std;
45
46 namespace WebCore {
47
48 class RepeatEvent : public Event {
49 public:
create(const AtomicString & type,int repeat)50 static PassRefPtr<RepeatEvent> create(const AtomicString& type, int repeat)
51 {
52 return adoptRef(new RepeatEvent(type, false, false, repeat));
53 }
54
~RepeatEvent()55 ~RepeatEvent() { }
56
repeat() const57 int repeat() const { return m_repeat; }
58 protected:
RepeatEvent(const AtomicString & type,bool canBubble,bool cancelable,int repeat=-1)59 RepeatEvent(const AtomicString& type, bool canBubble, bool cancelable, int repeat = -1)
60 : Event(type, canBubble, cancelable)
61 , m_repeat(repeat)
62 {
63 }
64 private:
65 int m_repeat;
66 };
67
toRepeatEvent(Event * event)68 inline RepeatEvent* toRepeatEvent(Event* event)
69 {
70 ASSERT_WITH_SECURITY_IMPLICATION(!event || event->type() == "repeatn");
71 return static_cast<RepeatEvent*>(event);
72 }
73
smilEndEventSender()74 static SMILEventSender& smilEndEventSender()
75 {
76 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("endEvent"));
77 return sender;
78 }
79
smilBeginEventSender()80 static SMILEventSender& smilBeginEventSender()
81 {
82 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("beginEvent"));
83 return sender;
84 }
85
smilRepeatEventSender()86 static SMILEventSender& smilRepeatEventSender()
87 {
88 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("repeatEvent"));
89 return sender;
90 }
91
smilRepeatNEventSender()92 static SMILEventSender& smilRepeatNEventSender()
93 {
94 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("repeatn"));
95 return sender;
96 }
97
98 // This is used for duration type time values that can't be negative.
99 static const double invalidCachedTime = -1.;
100
101 class ConditionEventListener : public EventListener {
102 public:
create(SVGSMILElement * animation,SVGSMILElement::Condition * condition)103 static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
104 {
105 return adoptRef(new ConditionEventListener(animation, condition));
106 }
107
cast(const EventListener * listener)108 static const ConditionEventListener* cast(const EventListener* listener)
109 {
110 return listener->type() == ConditionEventListenerType
111 ? static_cast<const ConditionEventListener*>(listener)
112 : 0;
113 }
114
115 virtual bool operator==(const EventListener& other);
116
disconnectAnimation()117 void disconnectAnimation()
118 {
119 m_animation = 0;
120 }
121
122 private:
ConditionEventListener(SVGSMILElement * animation,SVGSMILElement::Condition * condition)123 ConditionEventListener(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
124 : EventListener(ConditionEventListenerType)
125 , m_animation(animation)
126 , m_condition(condition)
127 {
128 }
129
130 virtual void handleEvent(ExecutionContext*, Event*);
131
132 SVGSMILElement* m_animation;
133 SVGSMILElement::Condition* m_condition;
134 };
135
operator ==(const EventListener & listener)136 bool ConditionEventListener::operator==(const EventListener& listener)
137 {
138 if (const ConditionEventListener* conditionEventListener = ConditionEventListener::cast(&listener))
139 return m_animation == conditionEventListener->m_animation && m_condition == conditionEventListener->m_condition;
140 return false;
141 }
142
handleEvent(ExecutionContext *,Event * event)143 void ConditionEventListener::handleEvent(ExecutionContext*, Event* event)
144 {
145 if (!m_animation)
146 return;
147 m_animation->handleConditionEvent(event, m_condition);
148 }
149
Condition(Type type,BeginOrEnd beginOrEnd,const String & baseID,const String & name,SMILTime offset,int repeat)150 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeat)
151 : m_type(type)
152 , m_beginOrEnd(beginOrEnd)
153 , m_baseID(baseID)
154 , m_name(name)
155 , m_offset(offset)
156 , m_repeat(repeat)
157 {
158 }
159
SVGSMILElement(const QualifiedName & tagName,Document & doc)160 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document& doc)
161 : SVGElement(tagName, doc)
162 , m_attributeName(anyQName())
163 , m_targetElement(0)
164 , m_conditionsConnected(false)
165 , m_hasEndEventConditions(false)
166 , m_isWaitingForFirstInterval(true)
167 , m_intervalBegin(SMILTime::unresolved())
168 , m_intervalEnd(SMILTime::unresolved())
169 , m_previousIntervalBegin(SMILTime::unresolved())
170 , m_activeState(Inactive)
171 , m_lastPercent(0)
172 , m_lastRepeat(0)
173 , m_nextProgressTime(0)
174 , m_documentOrderIndex(0)
175 , m_cachedDur(invalidCachedTime)
176 , m_cachedRepeatDur(invalidCachedTime)
177 , m_cachedRepeatCount(invalidCachedTime)
178 , m_cachedMin(invalidCachedTime)
179 , m_cachedMax(invalidCachedTime)
180 {
181 resolveFirstInterval();
182 }
183
~SVGSMILElement()184 SVGSMILElement::~SVGSMILElement()
185 {
186 clearResourceReferences();
187 smilEndEventSender().cancelEvent(this);
188 smilBeginEventSender().cancelEvent(this);
189 smilRepeatEventSender().cancelEvent(this);
190 smilRepeatNEventSender().cancelEvent(this);
191 disconnectConditions();
192 if (m_timeContainer && m_targetElement && hasValidAttributeName())
193 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
194 }
195
clearResourceReferences()196 void SVGSMILElement::clearResourceReferences()
197 {
198 document().accessSVGExtensions()->removeAllTargetReferencesForElement(this);
199 }
200
buildPendingResource()201 void SVGSMILElement::buildPendingResource()
202 {
203 clearResourceReferences();
204
205 if (!inDocument()) {
206 // Reset the target element if we are no longer in the document.
207 setTargetElement(0);
208 return;
209 }
210
211 String id;
212 String href = getAttribute(XLinkNames::hrefAttr);
213 Element* target;
214 if (href.isEmpty())
215 target = parentNode() && parentNode()->isElementNode() ? toElement(parentNode()) : 0;
216 else
217 target = SVGURIReference::targetElementFromIRIString(href, document(), &id);
218 SVGElement* svgTarget = target && target->isSVGElement() ? toSVGElement(target) : 0;
219
220 if (svgTarget && !svgTarget->inDocument())
221 svgTarget = 0;
222
223 if (svgTarget != targetElement())
224 setTargetElement(svgTarget);
225
226 if (!svgTarget) {
227 // Do not register as pending if we are already pending this resource.
228 if (document().accessSVGExtensions()->isElementPendingResource(this, id))
229 return;
230
231 if (!id.isEmpty()) {
232 document().accessSVGExtensions()->addPendingResource(id, this);
233 ASSERT(hasPendingResources());
234 }
235 } else {
236 // Register us with the target in the dependencies map. Any change of hrefElement
237 // that leads to relayout/repainting now informs us, so we can react to it.
238 document().accessSVGExtensions()->addElementReferencingTarget(this, svgTarget);
239 }
240 }
241
constructQualifiedName(const SVGElement * svgElement,const AtomicString & attributeName)242 static inline QualifiedName constructQualifiedName(const SVGElement* svgElement, const AtomicString& attributeName)
243 {
244 ASSERT(svgElement);
245 if (attributeName.isEmpty())
246 return anyQName();
247 if (!attributeName.contains(':'))
248 return QualifiedName(nullAtom, attributeName, nullAtom);
249
250 AtomicString prefix;
251 AtomicString localName;
252 if (!Document::parseQualifiedName(attributeName, prefix, localName, ASSERT_NO_EXCEPTION))
253 return anyQName();
254
255 const AtomicString& namespaceURI = svgElement->lookupNamespaceURI(prefix);
256 if (namespaceURI.isEmpty())
257 return anyQName();
258
259 return QualifiedName(nullAtom, localName, namespaceURI);
260 }
261
clearTimesWithDynamicOrigins(Vector<SMILTimeWithOrigin> & timeList)262 static inline void clearTimesWithDynamicOrigins(Vector<SMILTimeWithOrigin>& timeList)
263 {
264 for (int i = timeList.size() - 1; i >= 0; --i) {
265 if (timeList[i].originIsScript())
266 timeList.remove(i);
267 }
268 }
269
reset()270 void SVGSMILElement::reset()
271 {
272 clearAnimatedType(m_targetElement);
273
274 m_activeState = Inactive;
275 m_isWaitingForFirstInterval = true;
276 m_intervalBegin = SMILTime::unresolved();
277 m_intervalEnd = SMILTime::unresolved();
278 m_previousIntervalBegin = SMILTime::unresolved();
279 m_lastPercent = 0;
280 m_lastRepeat = 0;
281 m_nextProgressTime = 0;
282 resolveFirstInterval();
283 }
284
insertedInto(ContainerNode * rootParent)285 Node::InsertionNotificationRequest SVGSMILElement::insertedInto(ContainerNode* rootParent)
286 {
287 SVGElement::insertedInto(rootParent);
288 if (!rootParent->inDocument())
289 return InsertionDone;
290
291 // Verify we are not in <use> instance tree.
292 ASSERT(!isInShadowTree() || !parentOrShadowHostElement() || !parentOrShadowHostElement()->isSVGElement());
293
294 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
295 SVGSVGElement* owner = ownerSVGElement();
296 if (!owner)
297 return InsertionDone;
298
299 m_timeContainer = owner->timeContainer();
300 ASSERT(m_timeContainer);
301 m_timeContainer->setDocumentOrderIndexesDirty();
302
303 // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
304 if (!fastHasAttribute(SVGNames::beginAttr))
305 m_beginTimes.append(SMILTimeWithOrigin());
306
307 if (m_isWaitingForFirstInterval)
308 resolveFirstInterval();
309
310 if (m_timeContainer)
311 m_timeContainer->notifyIntervalsChanged();
312
313 buildPendingResource();
314
315 return InsertionDone;
316 }
317
removedFrom(ContainerNode * rootParent)318 void SVGSMILElement::removedFrom(ContainerNode* rootParent)
319 {
320 if (rootParent->inDocument()) {
321 clearResourceReferences();
322 disconnectConditions();
323 setTargetElement(0);
324 setAttributeName(anyQName());
325 animationAttributeChanged();
326 m_timeContainer = 0;
327 }
328
329 SVGElement::removedFrom(rootParent);
330 }
331
hasValidAttributeName()332 bool SVGSMILElement::hasValidAttributeName()
333 {
334 return attributeName() != anyQName();
335 }
336
parseOffsetValue(const String & data)337 SMILTime SVGSMILElement::parseOffsetValue(const String& data)
338 {
339 bool ok;
340 double result = 0;
341 String parse = data.stripWhiteSpace();
342 if (parse.endsWith('h'))
343 result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
344 else if (parse.endsWith("min"))
345 result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
346 else if (parse.endsWith("ms"))
347 result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
348 else if (parse.endsWith('s'))
349 result = parse.left(parse.length() - 1).toDouble(&ok);
350 else
351 result = parse.toDouble(&ok);
352 if (!ok)
353 return SMILTime::unresolved();
354 return result;
355 }
356
parseClockValue(const String & data)357 SMILTime SVGSMILElement::parseClockValue(const String& data)
358 {
359 if (data.isNull())
360 return SMILTime::unresolved();
361
362 String parse = data.stripWhiteSpace();
363
364 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
365 if (parse == indefiniteValue)
366 return SMILTime::indefinite();
367
368 double result = 0;
369 bool ok;
370 size_t doublePointOne = parse.find(':');
371 size_t doublePointTwo = parse.find(':', doublePointOne + 1);
372 if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) {
373 result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
374 if (!ok)
375 return SMILTime::unresolved();
376 result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
377 if (!ok)
378 return SMILTime::unresolved();
379 result += parse.substring(6).toDouble(&ok);
380 } else if (doublePointOne == 2 && doublePointTwo == kNotFound && parse.length() >= 5) {
381 result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
382 if (!ok)
383 return SMILTime::unresolved();
384 result += parse.substring(3).toDouble(&ok);
385 } else
386 return parseOffsetValue(parse);
387
388 if (!ok)
389 return SMILTime::unresolved();
390 return result;
391 }
392
sortTimeList(Vector<SMILTimeWithOrigin> & timeList)393 static void sortTimeList(Vector<SMILTimeWithOrigin>& timeList)
394 {
395 std::sort(timeList.begin(), timeList.end());
396 }
397
parseCondition(const String & value,BeginOrEnd beginOrEnd)398 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd)
399 {
400 String parseString = value.stripWhiteSpace();
401
402 double sign = 1.;
403 bool ok;
404 size_t pos = parseString.find('+');
405 if (pos == kNotFound) {
406 pos = parseString.find('-');
407 if (pos != kNotFound)
408 sign = -1.;
409 }
410 String conditionString;
411 SMILTime offset = 0;
412 if (pos == kNotFound)
413 conditionString = parseString;
414 else {
415 conditionString = parseString.left(pos).stripWhiteSpace();
416 String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
417 offset = parseOffsetValue(offsetString);
418 if (offset.isUnresolved())
419 return false;
420 offset = offset * sign;
421 }
422 if (conditionString.isEmpty())
423 return false;
424 pos = conditionString.find('.');
425
426 String baseID;
427 String nameString;
428 if (pos == kNotFound)
429 nameString = conditionString;
430 else {
431 baseID = conditionString.left(pos);
432 nameString = conditionString.substring(pos + 1);
433 }
434 if (nameString.isEmpty())
435 return false;
436
437 Condition::Type type;
438 int repeat = -1;
439 if (nameString.startsWith("repeat(") && nameString.endsWith(')')) {
440 repeat = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
441 if (!ok)
442 return false;
443 nameString = "repeatn";
444 type = Condition::EventBase;
445 } else if (nameString == "begin" || nameString == "end") {
446 if (baseID.isEmpty())
447 return false;
448 type = Condition::Syncbase;
449 } else if (nameString.startsWith("accesskey(")) {
450 // FIXME: accesskey() support.
451 type = Condition::AccessKey;
452 } else
453 type = Condition::EventBase;
454
455 m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeat));
456
457 if (type == Condition::EventBase && beginOrEnd == End)
458 m_hasEndEventConditions = true;
459
460 return true;
461 }
462
parseBeginOrEnd(const String & parseString,BeginOrEnd beginOrEnd)463 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd)
464 {
465 Vector<SMILTimeWithOrigin>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
466 if (beginOrEnd == End)
467 m_hasEndEventConditions = false;
468 HashSet<double> existing;
469 for (unsigned n = 0; n < timeList.size(); ++n)
470 existing.add(timeList[n].time().value());
471 Vector<String> splitString;
472 parseString.split(';', splitString);
473 for (unsigned n = 0; n < splitString.size(); ++n) {
474 SMILTime value = parseClockValue(splitString[n]);
475 if (value.isUnresolved())
476 parseCondition(splitString[n], beginOrEnd);
477 else if (!existing.contains(value.value()))
478 timeList.append(SMILTimeWithOrigin(value, SMILTimeWithOrigin::ParserOrigin));
479 }
480 sortTimeList(timeList);
481 }
482
isSupportedAttribute(const QualifiedName & attrName)483 bool SVGSMILElement::isSupportedAttribute(const QualifiedName& attrName)
484 {
485 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
486 if (supportedAttributes.isEmpty()) {
487 supportedAttributes.add(SVGNames::beginAttr);
488 supportedAttributes.add(SVGNames::endAttr);
489 supportedAttributes.add(SVGNames::durAttr);
490 supportedAttributes.add(SVGNames::repeatDurAttr);
491 supportedAttributes.add(SVGNames::repeatCountAttr);
492 supportedAttributes.add(SVGNames::minAttr);
493 supportedAttributes.add(SVGNames::maxAttr);
494 supportedAttributes.add(SVGNames::attributeNameAttr);
495 supportedAttributes.add(XLinkNames::hrefAttr);
496 }
497 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
498 }
499
parseAttribute(const QualifiedName & name,const AtomicString & value)500 void SVGSMILElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
501 {
502 if (name == SVGNames::beginAttr) {
503 if (!m_conditions.isEmpty()) {
504 disconnectConditions();
505 m_conditions.clear();
506 parseBeginOrEnd(fastGetAttribute(SVGNames::endAttr), End);
507 }
508 parseBeginOrEnd(value.string(), Begin);
509 if (inDocument())
510 connectConditions();
511 } else if (name == SVGNames::endAttr) {
512 if (!m_conditions.isEmpty()) {
513 disconnectConditions();
514 m_conditions.clear();
515 parseBeginOrEnd(fastGetAttribute(SVGNames::beginAttr), Begin);
516 }
517 parseBeginOrEnd(value.string(), End);
518 if (inDocument())
519 connectConditions();
520 } else
521 SVGElement::parseAttribute(name, value);
522 }
523
svgAttributeChanged(const QualifiedName & attrName)524 void SVGSMILElement::svgAttributeChanged(const QualifiedName& attrName)
525 {
526 if (!isSupportedAttribute(attrName)) {
527 SVGElement::svgAttributeChanged(attrName);
528 return;
529 }
530
531 if (attrName == SVGNames::durAttr)
532 m_cachedDur = invalidCachedTime;
533 else if (attrName == SVGNames::repeatDurAttr)
534 m_cachedRepeatDur = invalidCachedTime;
535 else if (attrName == SVGNames::repeatCountAttr)
536 m_cachedRepeatCount = invalidCachedTime;
537 else if (attrName == SVGNames::minAttr)
538 m_cachedMin = invalidCachedTime;
539 else if (attrName == SVGNames::maxAttr)
540 m_cachedMax = invalidCachedTime;
541 else if (attrName == SVGNames::attributeNameAttr)
542 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr)));
543 else if (attrName.matches(XLinkNames::hrefAttr)) {
544 SVGElementInstance::InvalidationGuard invalidationGuard(this);
545 buildPendingResource();
546 if (m_targetElement)
547 clearAnimatedType(m_targetElement);
548 } else if (inDocument()) {
549 if (attrName == SVGNames::beginAttr)
550 beginListChanged(elapsed());
551 else if (attrName == SVGNames::endAttr)
552 endListChanged(elapsed());
553 }
554
555 animationAttributeChanged();
556 }
557
eventBaseFor(const Condition & condition)558 inline Element* SVGSMILElement::eventBaseFor(const Condition& condition)
559 {
560 return condition.m_baseID.isEmpty() ? targetElement() : treeScope().getElementById(condition.m_baseID);
561 }
562
connectConditions()563 void SVGSMILElement::connectConditions()
564 {
565 if (m_conditionsConnected)
566 disconnectConditions();
567 m_conditionsConnected = true;
568 for (unsigned n = 0; n < m_conditions.size(); ++n) {
569 Condition& condition = m_conditions[n];
570 if (condition.m_type == Condition::EventBase) {
571 ASSERT(!condition.m_syncbase);
572 Element* eventBase = eventBaseFor(condition);
573 if (!eventBase)
574 continue;
575 ASSERT(!condition.m_eventListener);
576 condition.m_eventListener = ConditionEventListener::create(this, &condition);
577 eventBase->addEventListener(condition.m_name, condition.m_eventListener, false);
578 } else if (condition.m_type == Condition::Syncbase) {
579 ASSERT(!condition.m_baseID.isEmpty());
580 condition.m_syncbase = treeScope().getElementById(condition.m_baseID);
581 if (!condition.m_syncbase || !isSVGSMILElement(*condition.m_syncbase)) {
582 condition.m_syncbase = 0;
583 continue;
584 }
585 toSVGSMILElement(condition.m_syncbase.get())->addTimeDependent(this);
586 }
587 }
588 }
589
disconnectConditions()590 void SVGSMILElement::disconnectConditions()
591 {
592 if (!m_conditionsConnected)
593 return;
594 m_conditionsConnected = false;
595 for (unsigned n = 0; n < m_conditions.size(); ++n) {
596 Condition& condition = m_conditions[n];
597 if (condition.m_type == Condition::EventBase) {
598 ASSERT(!condition.m_syncbase);
599 if (!condition.m_eventListener)
600 continue;
601 // Note: It's a memory optimization to try to remove our condition
602 // event listener, but it's not guaranteed to work, since we have
603 // no guarantee that eventBaseFor() will be able to find our condition's
604 // original eventBase. So, we also have to disconnect ourselves from
605 // our condition event listener, in case it later fires.
606 Element* eventBase = eventBaseFor(condition);
607 if (eventBase)
608 eventBase->removeEventListener(condition.m_name, condition.m_eventListener.get(), false);
609 condition.m_eventListener->disconnectAnimation();
610 condition.m_eventListener = 0;
611 } else if (condition.m_type == Condition::Syncbase) {
612 if (condition.m_syncbase)
613 toSVGSMILElement(condition.m_syncbase.get())->removeTimeDependent(this);
614 }
615 condition.m_syncbase = 0;
616 }
617 }
618
setAttributeName(const QualifiedName & attributeName)619 void SVGSMILElement::setAttributeName(const QualifiedName& attributeName)
620 {
621 if (m_timeContainer && m_targetElement && m_attributeName != attributeName) {
622 if (hasValidAttributeName())
623 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
624 m_attributeName = attributeName;
625 if (hasValidAttributeName())
626 m_timeContainer->schedule(this, m_targetElement, m_attributeName);
627 } else
628 m_attributeName = attributeName;
629
630 // Only clear the animated type, if we had a target before.
631 if (m_targetElement)
632 clearAnimatedType(m_targetElement);
633 }
634
setTargetElement(SVGElement * target)635 void SVGSMILElement::setTargetElement(SVGElement* target)
636 {
637 if (m_timeContainer && hasValidAttributeName()) {
638 if (m_targetElement)
639 m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
640 if (target)
641 m_timeContainer->schedule(this, target, m_attributeName);
642 }
643
644 if (m_targetElement) {
645 // Clear values that may depend on the previous target.
646 clearAnimatedType(m_targetElement);
647 disconnectConditions();
648 }
649
650 // If the animation state is not Inactive, always reset to a clear state before leaving the old target element.
651 if (m_activeState != Inactive)
652 endedActiveInterval();
653
654 m_targetElement = target;
655 }
656
elapsed() const657 SMILTime SVGSMILElement::elapsed() const
658 {
659 return m_timeContainer ? m_timeContainer->elapsed() : 0;
660 }
661
isInactive() const662 bool SVGSMILElement::isInactive() const
663 {
664 return m_activeState == Inactive;
665 }
666
isFrozen() const667 bool SVGSMILElement::isFrozen() const
668 {
669 return m_activeState == Frozen;
670 }
671
restart() const672 SVGSMILElement::Restart SVGSMILElement::restart() const
673 {
674 DEFINE_STATIC_LOCAL(const AtomicString, never, ("never", AtomicString::ConstructFromLiteral));
675 DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive", AtomicString::ConstructFromLiteral));
676 const AtomicString& value = fastGetAttribute(SVGNames::restartAttr);
677 if (value == never)
678 return RestartNever;
679 if (value == whenNotActive)
680 return RestartWhenNotActive;
681 return RestartAlways;
682 }
683
fill() const684 SVGSMILElement::FillMode SVGSMILElement::fill() const
685 {
686 DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze", AtomicString::ConstructFromLiteral));
687 const AtomicString& value = fastGetAttribute(SVGNames::fillAttr);
688 return value == freeze ? FillFreeze : FillRemove;
689 }
690
dur() const691 SMILTime SVGSMILElement::dur() const
692 {
693 if (m_cachedDur != invalidCachedTime)
694 return m_cachedDur;
695 const AtomicString& value = fastGetAttribute(SVGNames::durAttr);
696 SMILTime clockValue = parseClockValue(value);
697 return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
698 }
699
repeatDur() const700 SMILTime SVGSMILElement::repeatDur() const
701 {
702 if (m_cachedRepeatDur != invalidCachedTime)
703 return m_cachedRepeatDur;
704 const AtomicString& value = fastGetAttribute(SVGNames::repeatDurAttr);
705 SMILTime clockValue = parseClockValue(value);
706 m_cachedRepeatDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
707 return m_cachedRepeatDur;
708 }
709
710 // So a count is not really a time but let just all pretend we did not notice.
repeatCount() const711 SMILTime SVGSMILElement::repeatCount() const
712 {
713 if (m_cachedRepeatCount != invalidCachedTime)
714 return m_cachedRepeatCount;
715 const AtomicString& value = fastGetAttribute(SVGNames::repeatCountAttr);
716 if (value.isNull())
717 return SMILTime::unresolved();
718
719 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral));
720 if (value == indefiniteValue)
721 return SMILTime::indefinite();
722 bool ok;
723 double result = value.string().toDouble(&ok);
724 return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved();
725 }
726
maxValue() const727 SMILTime SVGSMILElement::maxValue() const
728 {
729 if (m_cachedMax != invalidCachedTime)
730 return m_cachedMax;
731 const AtomicString& value = fastGetAttribute(SVGNames::maxAttr);
732 SMILTime result = parseClockValue(value);
733 return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result;
734 }
735
minValue() const736 SMILTime SVGSMILElement::minValue() const
737 {
738 if (m_cachedMin != invalidCachedTime)
739 return m_cachedMin;
740 const AtomicString& value = fastGetAttribute(SVGNames::minAttr);
741 SMILTime result = parseClockValue(value);
742 return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
743 }
744
simpleDuration() const745 SMILTime SVGSMILElement::simpleDuration() const
746 {
747 return min(dur(), SMILTime::indefinite());
748 }
749
addBeginTime(SMILTime eventTime,SMILTime beginTime,SMILTimeWithOrigin::Origin origin)750 void SVGSMILElement::addBeginTime(SMILTime eventTime, SMILTime beginTime, SMILTimeWithOrigin::Origin origin)
751 {
752 ASSERT(!std::isnan(beginTime.value()));
753 m_beginTimes.append(SMILTimeWithOrigin(beginTime, origin));
754 sortTimeList(m_beginTimes);
755 beginListChanged(eventTime);
756 }
757
addEndTime(SMILTime eventTime,SMILTime endTime,SMILTimeWithOrigin::Origin origin)758 void SVGSMILElement::addEndTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin origin)
759 {
760 ASSERT(!std::isnan(endTime.value()));
761 m_endTimes.append(SMILTimeWithOrigin(endTime, origin));
762 sortTimeList(m_endTimes);
763 endListChanged(eventTime);
764 }
765
extractTimeFromVector(const SMILTimeWithOrigin * position)766 inline SMILTime extractTimeFromVector(const SMILTimeWithOrigin* position)
767 {
768 return position->time();
769 }
770
findInstanceTime(BeginOrEnd beginOrEnd,SMILTime minimumTime,bool equalsMinimumOK) const771 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
772 {
773 const Vector<SMILTimeWithOrigin>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
774 int sizeOfList = list.size();
775
776 if (!sizeOfList)
777 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
778
779 const SMILTimeWithOrigin* result = approximateBinarySearch<const SMILTimeWithOrigin, SMILTime>(list, sizeOfList, minimumTime, extractTimeFromVector);
780 int indexOfResult = result - list.begin();
781 ASSERT_WITH_SECURITY_IMPLICATION(indexOfResult < sizeOfList);
782
783 if (list[indexOfResult].time() < minimumTime && indexOfResult < sizeOfList - 1)
784 ++indexOfResult;
785
786 const SMILTime& currentTime = list[indexOfResult].time();
787
788 // The special value "indefinite" does not yield an instance time in the begin list.
789 if (currentTime.isIndefinite() && beginOrEnd == Begin)
790 return SMILTime::unresolved();
791
792 if (currentTime < minimumTime)
793 return SMILTime::unresolved();
794 if (currentTime > minimumTime)
795 return currentTime;
796
797 ASSERT(currentTime == minimumTime);
798 if (equalsMinimumOK)
799 return currentTime;
800
801 // If the equals is not accepted, return the next bigger item in the list.
802 SMILTime nextTime = currentTime;
803 while (indexOfResult < sizeOfList - 1) {
804 nextTime = list[indexOfResult + 1].time();
805 if (nextTime > minimumTime)
806 return nextTime;
807 ++indexOfResult;
808 }
809
810 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite();
811 }
812
repeatingDuration() const813 SMILTime SVGSMILElement::repeatingDuration() const
814 {
815 // Computing the active duration
816 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
817 SMILTime repeatCount = this->repeatCount();
818 SMILTime repeatDur = this->repeatDur();
819 SMILTime simpleDuration = this->simpleDuration();
820 if (!simpleDuration || (repeatDur.isUnresolved() && repeatCount.isUnresolved()))
821 return simpleDuration;
822 SMILTime repeatCountDuration = simpleDuration * repeatCount;
823 return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
824 }
825
resolveActiveEnd(SMILTime resolvedBegin,SMILTime resolvedEnd) const826 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
827 {
828 // Computing the active duration
829 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
830 SMILTime preliminaryActiveDuration;
831 if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
832 preliminaryActiveDuration = resolvedEnd - resolvedBegin;
833 else if (!resolvedEnd.isFinite())
834 preliminaryActiveDuration = repeatingDuration();
835 else
836 preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
837
838 SMILTime minValue = this->minValue();
839 SMILTime maxValue = this->maxValue();
840 if (minValue > maxValue) {
841 // Ignore both.
842 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
843 minValue = 0;
844 maxValue = SMILTime::indefinite();
845 }
846 return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
847 }
848
resolveInterval(bool first,SMILTime & beginResult,SMILTime & endResult) const849 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const
850 {
851 // See the pseudocode in http://www.w3.org/TR/SMIL3/smil-timing.html#q90.
852 SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
853 SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
854 while (true) {
855 bool equalsMinimumOK = !first || m_intervalEnd > m_intervalBegin;
856 SMILTime tempBegin = findInstanceTime(Begin, beginAfter, equalsMinimumOK);
857 if (tempBegin.isUnresolved())
858 break;
859 SMILTime tempEnd;
860 if (m_endTimes.isEmpty())
861 tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
862 else {
863 tempEnd = findInstanceTime(End, tempBegin, true);
864 if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd))
865 tempEnd = findInstanceTime(End, tempBegin, false);
866 if (tempEnd.isUnresolved()) {
867 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions)
868 break;
869 }
870 tempEnd = resolveActiveEnd(tempBegin, tempEnd);
871 }
872 if (!first || (tempEnd > 0 || (!tempBegin.value() && !tempEnd.value()))) {
873 beginResult = tempBegin;
874 endResult = tempEnd;
875 return;
876 }
877
878 beginAfter = tempEnd;
879 lastIntervalTempEnd = tempEnd;
880 }
881 beginResult = SMILTime::unresolved();
882 endResult = SMILTime::unresolved();
883 }
884
resolveFirstInterval()885 void SVGSMILElement::resolveFirstInterval()
886 {
887 SMILTime begin;
888 SMILTime end;
889 resolveInterval(true, begin, end);
890 ASSERT(!begin.isIndefinite());
891
892 if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {
893 m_intervalBegin = begin;
894 m_intervalEnd = end;
895 notifyDependentsIntervalChanged();
896 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
897
898 if (m_timeContainer)
899 m_timeContainer->notifyIntervalsChanged();
900 }
901 }
902
resolveNextInterval()903 bool SVGSMILElement::resolveNextInterval()
904 {
905 SMILTime begin;
906 SMILTime end;
907 resolveInterval(false, begin, end);
908 ASSERT(!begin.isIndefinite());
909
910 if (!begin.isUnresolved() && begin != m_intervalBegin) {
911 m_intervalBegin = begin;
912 m_intervalEnd = end;
913 notifyDependentsIntervalChanged();
914 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
915 return true;
916 }
917
918 return false;
919 }
920
nextProgressTime() const921 SMILTime SVGSMILElement::nextProgressTime() const
922 {
923 return m_nextProgressTime;
924 }
925
beginListChanged(SMILTime eventTime)926 void SVGSMILElement::beginListChanged(SMILTime eventTime)
927 {
928 if (m_isWaitingForFirstInterval)
929 resolveFirstInterval();
930 else {
931 SMILTime newBegin = findInstanceTime(Begin, eventTime, true);
932 if (newBegin.isFinite() && (m_intervalEnd <= eventTime || newBegin < m_intervalBegin)) {
933 // Begin time changed, re-resolve the interval.
934 SMILTime oldBegin = m_intervalBegin;
935 m_intervalEnd = eventTime;
936 resolveInterval(false, m_intervalBegin, m_intervalEnd);
937 ASSERT(!m_intervalBegin.isUnresolved());
938 if (m_intervalBegin != oldBegin) {
939 if (m_activeState == Active && m_intervalBegin > eventTime) {
940 m_activeState = determineActiveState(eventTime);
941 if (m_activeState != Active)
942 endedActiveInterval();
943 }
944 notifyDependentsIntervalChanged();
945 }
946 }
947 }
948 m_nextProgressTime = elapsed();
949
950 if (m_timeContainer)
951 m_timeContainer->notifyIntervalsChanged();
952 }
953
endListChanged(SMILTime)954 void SVGSMILElement::endListChanged(SMILTime)
955 {
956 SMILTime elapsed = this->elapsed();
957 if (m_isWaitingForFirstInterval)
958 resolveFirstInterval();
959 else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
960 SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
961 if (newEnd < m_intervalEnd) {
962 newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
963 if (newEnd != m_intervalEnd) {
964 m_intervalEnd = newEnd;
965 notifyDependentsIntervalChanged();
966 }
967 }
968 }
969 m_nextProgressTime = elapsed;
970
971 if (m_timeContainer)
972 m_timeContainer->notifyIntervalsChanged();
973 }
974
checkRestart(SMILTime elapsed)975 void SVGSMILElement::checkRestart(SMILTime elapsed)
976 {
977 ASSERT(!m_isWaitingForFirstInterval);
978 ASSERT(elapsed >= m_intervalBegin);
979
980 Restart restart = this->restart();
981 if (restart == RestartNever)
982 return;
983
984 if (elapsed < m_intervalEnd) {
985 if (restart != RestartAlways)
986 return;
987 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
988 if (nextBegin < m_intervalEnd) {
989 m_intervalEnd = nextBegin;
990 notifyDependentsIntervalChanged();
991 }
992 }
993
994 if (elapsed >= m_intervalEnd)
995 resolveNextInterval();
996 }
997
seekToIntervalCorrespondingToTime(SMILTime elapsed)998 void SVGSMILElement::seekToIntervalCorrespondingToTime(SMILTime elapsed)
999 {
1000 ASSERT(!m_isWaitingForFirstInterval);
1001 ASSERT(elapsed >= m_intervalBegin);
1002
1003 // Manually seek from interval to interval, just as if the animation would run regulary.
1004 while (true) {
1005 // Figure out the next value in the begin time list after the current interval begin.
1006 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
1007
1008 // If the 'nextBegin' time is unresolved (eg. just one defined interval), we're done seeking.
1009 if (nextBegin.isUnresolved())
1010 return;
1011
1012 // If the 'nextBegin' time is larger than or equal to the current interval end time, we're done seeking.
1013 // If the 'elapsed' time is smaller than the next begin interval time, we're done seeking.
1014 if (nextBegin < m_intervalEnd && elapsed >= nextBegin) {
1015 // End current interval, and start a new interval from the 'nextBegin' time.
1016 m_intervalEnd = nextBegin;
1017 if (!resolveNextInterval())
1018 break;
1019 continue;
1020 }
1021
1022 // If the desired 'elapsed' time is past the current interval, advance to the next.
1023 if (elapsed >= m_intervalEnd) {
1024 if (!resolveNextInterval())
1025 break;
1026 continue;
1027 }
1028
1029 return;
1030 }
1031 }
1032
calculateAnimationPercentAndRepeat(SMILTime elapsed,unsigned & repeat) const1033 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const
1034 {
1035 SMILTime simpleDuration = this->simpleDuration();
1036 repeat = 0;
1037 if (simpleDuration.isIndefinite()) {
1038 repeat = 0;
1039 return 0.f;
1040 }
1041 if (!simpleDuration) {
1042 repeat = 0;
1043 return 1.f;
1044 }
1045 ASSERT(m_intervalBegin.isFinite());
1046 ASSERT(simpleDuration.isFinite());
1047 SMILTime activeTime = elapsed - m_intervalBegin;
1048 SMILTime repeatingDuration = this->repeatingDuration();
1049 if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
1050 repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value());
1051 if (!fmod(repeatingDuration.value(), simpleDuration.value()))
1052 repeat--;
1053
1054 double percent = (m_intervalEnd.value() - m_intervalBegin.value()) / simpleDuration.value();
1055 percent = percent - floor(percent);
1056 if (percent < numeric_limits<float>::epsilon() || 1 - percent < numeric_limits<float>::epsilon())
1057 return 1.0f;
1058 return narrowPrecisionToFloat(percent);
1059 }
1060 repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
1061 SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
1062 return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
1063 }
1064
calculateNextProgressTime(SMILTime elapsed) const1065 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
1066 {
1067 if (m_activeState == Active) {
1068 // If duration is indefinite the value does not actually change over time. Same is true for <set>.
1069 SMILTime simpleDuration = this->simpleDuration();
1070 if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) {
1071 SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
1072 // We are supposed to do freeze semantics when repeating ends, even if the element is still active.
1073 // Take care that we get a timer callback at that point.
1074 if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite())
1075 return repeatingDurationEnd;
1076 return m_intervalEnd;
1077 }
1078 return elapsed + 0.025;
1079 }
1080 return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
1081 }
1082
determineActiveState(SMILTime elapsed) const1083 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
1084 {
1085 if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd)
1086 return Active;
1087
1088 return fill() == FillFreeze ? Frozen : Inactive;
1089 }
1090
isContributing(SMILTime elapsed) const1091 bool SVGSMILElement::isContributing(SMILTime elapsed) const
1092 {
1093 // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
1094 return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
1095 }
1096
progress(SMILTime elapsed,SVGSMILElement * resultElement,bool seekToTime)1097 bool SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement, bool seekToTime)
1098 {
1099 ASSERT(resultElement);
1100 ASSERT(m_timeContainer);
1101 ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
1102
1103 if (!m_conditionsConnected)
1104 connectConditions();
1105
1106 if (!m_intervalBegin.isFinite()) {
1107 ASSERT(m_activeState == Inactive);
1108 m_nextProgressTime = SMILTime::unresolved();
1109 return false;
1110 }
1111
1112 if (elapsed < m_intervalBegin) {
1113 ASSERT(m_activeState != Active);
1114 if (m_activeState == Frozen) {
1115 if (this == resultElement)
1116 resetAnimatedType();
1117 updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
1118 }
1119 m_nextProgressTime = m_intervalBegin;
1120 return false;
1121 }
1122
1123 m_previousIntervalBegin = m_intervalBegin;
1124
1125 if (m_isWaitingForFirstInterval) {
1126 m_isWaitingForFirstInterval = false;
1127 resolveFirstInterval();
1128 }
1129
1130 // This call may obtain a new interval -- never call calculateAnimationPercentAndRepeat() before!
1131 if (seekToTime) {
1132 seekToIntervalCorrespondingToTime(elapsed);
1133 if (elapsed < m_intervalBegin) {
1134 // elapsed is not within an interval.
1135 m_nextProgressTime = m_intervalBegin;
1136 return false;
1137 }
1138 }
1139
1140 unsigned repeat = 0;
1141 float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
1142 checkRestart(elapsed);
1143
1144 ActiveState oldActiveState = m_activeState;
1145 m_activeState = determineActiveState(elapsed);
1146 bool animationIsContributing = isContributing(elapsed);
1147
1148 // 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.
1149 if (this == resultElement && animationIsContributing)
1150 resetAnimatedType();
1151
1152 if (animationIsContributing) {
1153 if (oldActiveState == Inactive) {
1154 smilBeginEventSender().dispatchEventSoon(this);
1155 startedActiveInterval();
1156 }
1157
1158 if (repeat && repeat != m_lastRepeat)
1159 dispatchRepeatEvents(repeat);
1160
1161 updateAnimation(percent, repeat, resultElement);
1162 m_lastPercent = percent;
1163 m_lastRepeat = repeat;
1164 }
1165
1166 if (oldActiveState == Active && m_activeState != Active) {
1167 smilEndEventSender().dispatchEventSoon(this);
1168 endedActiveInterval();
1169 if (m_activeState != Frozen && this == resultElement)
1170 clearAnimatedType(m_targetElement);
1171 }
1172
1173 // Triggering all the pending events if the animation timeline is changed.
1174 if (seekToTime) {
1175 if (m_activeState == Inactive)
1176 smilBeginEventSender().dispatchEventSoon(this);
1177
1178 if (repeat) {
1179 for (unsigned repeatEventCount = 1; repeatEventCount < repeat; repeatEventCount++)
1180 dispatchRepeatEvents(repeatEventCount);
1181 if (m_activeState == Inactive)
1182 dispatchRepeatEvents(repeat);
1183 }
1184
1185 if (m_activeState == Inactive || m_activeState == Frozen)
1186 smilEndEventSender().dispatchEventSoon(this);
1187 }
1188
1189 m_nextProgressTime = calculateNextProgressTime(elapsed);
1190 return animationIsContributing;
1191 }
1192
notifyDependentsIntervalChanged()1193 void SVGSMILElement::notifyDependentsIntervalChanged()
1194 {
1195 ASSERT(m_intervalBegin.isFinite());
1196 DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ());
1197 if (!loopBreaker.add(this).isNewEntry)
1198 return;
1199
1200 TimeDependentSet::iterator end = m_timeDependents.end();
1201 for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) {
1202 SVGSMILElement* dependent = *it;
1203 dependent->createInstanceTimesFromSyncbase(this);
1204 }
1205
1206 loopBreaker.remove(this);
1207 }
1208
createInstanceTimesFromSyncbase(SVGSMILElement * syncbase)1209 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase)
1210 {
1211 // FIXME: To be really correct, this should handle updating exising interval by changing
1212 // the associated times instead of creating new ones.
1213 for (unsigned n = 0; n < m_conditions.size(); ++n) {
1214 Condition& condition = m_conditions[n];
1215 if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
1216 ASSERT(condition.m_name == "begin" || condition.m_name == "end");
1217 // No nested time containers in SVG, no need for crazy time space conversions. Phew!
1218 SMILTime time = 0;
1219 if (condition.m_name == "begin")
1220 time = syncbase->m_intervalBegin + condition.m_offset;
1221 else
1222 time = syncbase->m_intervalEnd + condition.m_offset;
1223 ASSERT(time.isFinite());
1224 if (condition.m_beginOrEnd == Begin)
1225 addBeginTime(elapsed(), time);
1226 else
1227 addEndTime(elapsed(), time);
1228 }
1229 }
1230 }
1231
addTimeDependent(SVGSMILElement * animation)1232 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
1233 {
1234 m_timeDependents.add(animation);
1235 if (m_intervalBegin.isFinite())
1236 animation->createInstanceTimesFromSyncbase(this);
1237 }
1238
removeTimeDependent(SVGSMILElement * animation)1239 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
1240 {
1241 m_timeDependents.remove(animation);
1242 }
1243
handleConditionEvent(Event * event,Condition * condition)1244 void SVGSMILElement::handleConditionEvent(Event* event, Condition* condition)
1245 {
1246 if (event->type() == "repeatn" && toRepeatEvent(event)->repeat() != condition->m_repeat)
1247 return;
1248
1249 SMILTime elapsed = this->elapsed();
1250 if (condition->m_beginOrEnd == Begin)
1251 addBeginTime(elapsed, elapsed + condition->m_offset);
1252 else
1253 addEndTime(elapsed, elapsed + condition->m_offset);
1254 }
1255
beginByLinkActivation()1256 void SVGSMILElement::beginByLinkActivation()
1257 {
1258 SMILTime elapsed = this->elapsed();
1259 addBeginTime(elapsed, elapsed);
1260 }
1261
endedActiveInterval()1262 void SVGSMILElement::endedActiveInterval()
1263 {
1264 clearTimesWithDynamicOrigins(m_beginTimes);
1265 clearTimesWithDynamicOrigins(m_endTimes);
1266 }
1267
dispatchRepeatEvents(unsigned count)1268 void SVGSMILElement::dispatchRepeatEvents(unsigned count)
1269 {
1270 m_repeatEventCountList.append(count);
1271 smilRepeatEventSender().dispatchEventSoon(this);
1272 smilRepeatNEventSender().dispatchEventSoon(this);
1273 }
1274
dispatchPendingEvent(SMILEventSender * eventSender)1275 void SVGSMILElement::dispatchPendingEvent(SMILEventSender* eventSender)
1276 {
1277 ASSERT(eventSender == &smilEndEventSender() || eventSender == &smilBeginEventSender() || eventSender == &smilRepeatEventSender() || eventSender == &smilRepeatNEventSender());
1278 const AtomicString& eventType = eventSender->eventType();
1279 if (eventType == "repeatn") {
1280 unsigned repeatEventCount = m_repeatEventCountList.first();
1281 m_repeatEventCountList.remove(0);
1282 dispatchEvent(RepeatEvent::create(eventType, repeatEventCount));
1283 } else {
1284 dispatchEvent(Event::create(eventType));
1285 }
1286 }
1287
1288 }
1289