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