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