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
28 #if ENABLE(SVG_ANIMATION)
29 #include "SVGSMILElement.h"
30
31 #include "Attribute.h"
32 #include "CSSPropertyNames.h"
33 #include "Document.h"
34 #include "Event.h"
35 #include "EventListener.h"
36 #include "FloatConversion.h"
37 #include "FrameView.h"
38 #include "HTMLNames.h"
39 #include "SMILTimeContainer.h"
40 #include "SVGDocumentExtensions.h"
41 #include "SVGNames.h"
42 #include "SVGParserUtilities.h"
43 #include "SVGSVGElement.h"
44 #include "SVGURIReference.h"
45 #include "XLinkNames.h"
46 #include <wtf/MathExtras.h>
47 #include <wtf/StdLibExtras.h>
48 #include <wtf/Vector.h>
49
50 using namespace std;
51
52 namespace WebCore {
53
54 // This is used for duration type time values that can't be negative.
55 static const double invalidCachedTime = -1.;
56
57 class ConditionEventListener : public EventListener {
58 public:
create(SVGSMILElement * animation,SVGSMILElement::Condition * condition)59 static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
60 {
61 return adoptRef(new ConditionEventListener(animation, condition));
62 }
63
cast(const EventListener * listener)64 static const ConditionEventListener* cast(const EventListener* listener)
65 {
66 return listener->type() == ConditionEventListenerType
67 ? static_cast<const ConditionEventListener*>(listener)
68 : 0;
69 }
70
71 virtual bool operator==(const EventListener& other);
72
disconnectAnimation()73 void disconnectAnimation()
74 {
75 m_animation = 0;
76 }
77
78 private:
ConditionEventListener(SVGSMILElement * animation,SVGSMILElement::Condition * condition)79 ConditionEventListener(SVGSMILElement* animation, SVGSMILElement::Condition* condition)
80 : EventListener(ConditionEventListenerType)
81 , m_animation(animation)
82 , m_condition(condition)
83 {
84 }
85
86 virtual void handleEvent(ScriptExecutionContext*, Event*);
87
88 SVGSMILElement* m_animation;
89 SVGSMILElement::Condition* m_condition;
90 };
91
operator ==(const EventListener & listener)92 bool ConditionEventListener::operator==(const EventListener& listener)
93 {
94 if (const ConditionEventListener* conditionEventListener = ConditionEventListener::cast(&listener))
95 return m_animation == conditionEventListener->m_animation && m_condition == conditionEventListener->m_condition;
96 return false;
97 }
98
handleEvent(ScriptExecutionContext *,Event * event)99 void ConditionEventListener::handleEvent(ScriptExecutionContext*, Event* event)
100 {
101 if (!m_animation)
102 return;
103 m_animation->handleConditionEvent(event, m_condition);
104 }
105
Condition(Type type,BeginOrEnd beginOrEnd,const String & baseID,const String & name,SMILTime offset,int repeats)106 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeats)
107 : m_type(type)
108 , m_beginOrEnd(beginOrEnd)
109 , m_baseID(baseID)
110 , m_name(name)
111 , m_offset(offset)
112 , m_repeats(repeats)
113 {
114 }
115
SVGSMILElement(const QualifiedName & tagName,Document * doc)116 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document* doc)
117 : SVGElement(tagName, doc)
118 , m_attributeName(anyQName())
119 , m_targetElement(0)
120 , m_conditionsConnected(false)
121 , m_hasEndEventConditions(false)
122 , m_intervalBegin(SMILTime::unresolved())
123 , m_intervalEnd(SMILTime::unresolved())
124 , m_previousIntervalBegin(SMILTime::unresolved())
125 , m_isWaitingForFirstInterval(true)
126 , m_activeState(Inactive)
127 , m_lastPercent(0)
128 , m_lastRepeat(0)
129 , m_nextProgressTime(0)
130 , m_documentOrderIndex(0)
131 , m_cachedDur(invalidCachedTime)
132 , m_cachedRepeatDur(invalidCachedTime)
133 , m_cachedRepeatCount(invalidCachedTime)
134 , m_cachedMin(invalidCachedTime)
135 , m_cachedMax(invalidCachedTime)
136 {
137 }
138
~SVGSMILElement()139 SVGSMILElement::~SVGSMILElement()
140 {
141 disconnectConditions();
142 if (m_timeContainer)
143 m_timeContainer->unschedule(this);
144 if (m_targetElement)
145 document()->accessSVGExtensions()->removeAnimationElementFromTarget(this, m_targetElement);
146 }
147
constructQualifiedName(const SVGElement * svgElement,const String & attributeName)148 static inline QualifiedName constructQualifiedName(const SVGElement* svgElement, const String& attributeName)
149 {
150 ASSERT(svgElement);
151 if (attributeName.isEmpty())
152 return anyQName();
153 if (!attributeName.contains(':'))
154 return QualifiedName(nullAtom, attributeName, nullAtom);
155
156 String prefix;
157 String localName;
158 ExceptionCode ec = 0;
159 if (!Document::parseQualifiedName(attributeName, prefix, localName, ec))
160 return anyQName();
161 ASSERT(!ec);
162
163 String namespaceURI = svgElement->lookupNamespaceURI(prefix);
164 if (namespaceURI.isEmpty())
165 return anyQName();
166
167 return QualifiedName(nullAtom, localName, namespaceURI);
168 }
169
insertedIntoDocument()170 void SVGSMILElement::insertedIntoDocument()
171 {
172 SVGElement::insertedIntoDocument();
173 #ifndef NDEBUG
174 // Verify we are not in <use> instance tree.
175 for (ContainerNode* n = this; n; n = n->parentNode())
176 ASSERT(!n->isShadowRoot());
177 #endif
178 m_attributeName = constructQualifiedName(this, getAttribute(SVGNames::attributeNameAttr));
179 SVGSVGElement* owner = ownerSVGElement();
180 if (!owner)
181 return;
182 m_timeContainer = owner->timeContainer();
183 ASSERT(m_timeContainer);
184 m_timeContainer->setDocumentOrderIndexesDirty();
185 reschedule();
186 }
187
removedFromDocument()188 void SVGSMILElement::removedFromDocument()
189 {
190 m_attributeName = anyQName();
191 if (m_timeContainer) {
192 m_timeContainer->unschedule(this);
193 m_timeContainer = 0;
194 }
195 if (m_targetElement) {
196 document()->accessSVGExtensions()->removeAnimationElementFromTarget(this, m_targetElement);
197 m_targetElement = 0;
198 }
199 // Calling disconnectConditions() may kill us if there are syncbase conditions.
200 // OK, but we don't want to die inside the call.
201 RefPtr<SVGSMILElement> keepAlive(this);
202 disconnectConditions();
203 SVGElement::removedFromDocument();
204 }
205
finishParsingChildren()206 void SVGSMILElement::finishParsingChildren()
207 {
208 SVGElement::finishParsingChildren();
209
210 // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
211 if (!hasAttribute(SVGNames::beginAttr))
212 m_beginTimes.append(0);
213
214 if (m_isWaitingForFirstInterval) {
215 resolveFirstInterval();
216 reschedule();
217 }
218 }
219
parseOffsetValue(const String & data)220 SMILTime SVGSMILElement::parseOffsetValue(const String& data)
221 {
222 bool ok;
223 double result = 0;
224 String parse = data.stripWhiteSpace();
225 if (parse.endsWith("h"))
226 result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
227 else if (parse.endsWith("min"))
228 result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
229 else if (parse.endsWith("ms"))
230 result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
231 else if (parse.endsWith("s"))
232 result = parse.left(parse.length() - 1).toDouble(&ok);
233 else
234 result = parse.toDouble(&ok);
235 if (!ok)
236 return SMILTime::unresolved();
237 return result;
238 }
239
parseClockValue(const String & data)240 SMILTime SVGSMILElement::parseClockValue(const String& data)
241 {
242 if (data.isNull())
243 return SMILTime::unresolved();
244
245 String parse = data.stripWhiteSpace();
246
247 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite"));
248 if (parse == indefiniteValue)
249 return SMILTime::indefinite();
250
251 double result = 0;
252 bool ok;
253 size_t doublePointOne = parse.find(':');
254 size_t doublePointTwo = parse.find(':', doublePointOne + 1);
255 if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) {
256 result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
257 if (!ok)
258 return SMILTime::unresolved();
259 result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
260 if (!ok)
261 return SMILTime::unresolved();
262 result += parse.substring(6).toDouble(&ok);
263 } else if (doublePointOne == 2 && doublePointTwo == notFound && parse.length() >= 5) {
264 result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
265 if (!ok)
266 return SMILTime::unresolved();
267 result += parse.substring(3).toDouble(&ok);
268 } else
269 return parseOffsetValue(parse);
270
271 if (!ok)
272 return SMILTime::unresolved();
273 return result;
274 }
275
sortTimeList(Vector<SMILTime> & timeList)276 static void sortTimeList(Vector<SMILTime>& timeList)
277 {
278 std::sort(timeList.begin(), timeList.end());
279 }
280
parseCondition(const String & value,BeginOrEnd beginOrEnd)281 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd)
282 {
283 String parseString = value.stripWhiteSpace();
284
285 double sign = 1.;
286 bool ok;
287 size_t pos = parseString.find('+');
288 if (pos == notFound) {
289 pos = parseString.find('-');
290 if (pos != notFound)
291 sign = -1.;
292 }
293 String conditionString;
294 SMILTime offset = 0;
295 if (pos == notFound)
296 conditionString = parseString;
297 else {
298 conditionString = parseString.left(pos).stripWhiteSpace();
299 String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
300 offset = parseOffsetValue(offsetString);
301 if (offset.isUnresolved())
302 return false;
303 offset = offset * sign;
304 }
305 if (conditionString.isEmpty())
306 return false;
307 pos = conditionString.find('.');
308
309 String baseID;
310 String nameString;
311 if (pos == notFound)
312 nameString = conditionString;
313 else {
314 baseID = conditionString.left(pos);
315 nameString = conditionString.substring(pos + 1);
316 }
317 if (nameString.isEmpty())
318 return false;
319
320 Condition::Type type;
321 int repeats = -1;
322 if (nameString.startsWith("repeat(") && nameString.endsWith(")")) {
323 // FIXME: For repeat events we just need to add the data carrying TimeEvent class and
324 // fire the events at appropiate times.
325 repeats = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
326 if (!ok)
327 return false;
328 nameString = "repeat";
329 type = Condition::EventBase;
330 } else if (nameString == "begin" || nameString == "end") {
331 if (baseID.isEmpty())
332 return false;
333 type = Condition::Syncbase;
334 } else if (nameString.startsWith("accesskey(")) {
335 // FIXME: accesskey() support.
336 type = Condition::AccessKey;
337 } else
338 type = Condition::EventBase;
339
340 m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeats));
341
342 if (type == Condition::EventBase && beginOrEnd == End)
343 m_hasEndEventConditions = true;
344
345 return true;
346 }
347
isSMILElement(Node * node)348 bool SVGSMILElement::isSMILElement(Node* node)
349 {
350 if (!node)
351 return false;
352 return node->hasTagName(SVGNames::setTag) || node->hasTagName(SVGNames::animateTag) || node->hasTagName(SVGNames::animateMotionTag)
353 || node->hasTagName(SVGNames::animateTransformTag) || node->hasTagName(SVGNames::animateColorTag);
354 }
355
parseBeginOrEnd(const String & parseString,BeginOrEnd beginOrEnd)356 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd)
357 {
358 Vector<SMILTime>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
359 if (beginOrEnd == End)
360 m_hasEndEventConditions = false;
361 HashSet<double> existing;
362 for (unsigned n = 0; n < timeList.size(); ++n)
363 existing.add(timeList[n].value());
364 Vector<String> splitString;
365 parseString.split(';', splitString);
366 for (unsigned n = 0; n < splitString.size(); ++n) {
367 SMILTime value = parseClockValue(splitString[n]);
368 if (value.isUnresolved())
369 parseCondition(splitString[n], beginOrEnd);
370 else if (!existing.contains(value.value()))
371 timeList.append(value);
372 }
373 sortTimeList(timeList);
374 }
375
parseMappedAttribute(Attribute * attr)376 void SVGSMILElement::parseMappedAttribute(Attribute* attr)
377 {
378 if (attr->name() == SVGNames::beginAttr) {
379 if (!m_conditions.isEmpty()) {
380 disconnectConditions();
381 m_conditions.clear();
382 parseBeginOrEnd(getAttribute(SVGNames::endAttr), End);
383 }
384 parseBeginOrEnd(attr->value().string(), Begin);
385 if (inDocument())
386 connectConditions();
387 } else if (attr->name() == SVGNames::endAttr) {
388 if (!m_conditions.isEmpty()) {
389 disconnectConditions();
390 m_conditions.clear();
391 parseBeginOrEnd(getAttribute(SVGNames::beginAttr), Begin);
392 }
393 parseBeginOrEnd(attr->value().string(), End);
394 if (inDocument())
395 connectConditions();
396 } else
397 SVGElement::parseMappedAttribute(attr);
398 }
399
attributeChanged(Attribute * attr,bool preserveDecls)400 void SVGSMILElement::attributeChanged(Attribute* attr, bool preserveDecls)
401 {
402 SVGElement::attributeChanged(attr, preserveDecls);
403
404 const QualifiedName& attrName = attr->name();
405 if (attrName == SVGNames::durAttr)
406 m_cachedDur = invalidCachedTime;
407 else if (attrName == SVGNames::repeatDurAttr)
408 m_cachedRepeatDur = invalidCachedTime;
409 else if (attrName == SVGNames::repeatCountAttr)
410 m_cachedRepeatCount = invalidCachedTime;
411 else if (attrName == SVGNames::minAttr)
412 m_cachedMin = invalidCachedTime;
413 else if (attrName == SVGNames::maxAttr)
414 m_cachedMax = invalidCachedTime;
415 else if (attrName == SVGNames::attributeNameAttr) {
416 if (inDocument())
417 m_attributeName = constructQualifiedName(this, attr->value());
418 }
419
420 if (inDocument()) {
421 if (attrName == SVGNames::beginAttr)
422 beginListChanged();
423 else if (attrName == SVGNames::endAttr)
424 endListChanged();
425 }
426 }
427
eventBaseFor(const Condition & condition) const428 inline Element* SVGSMILElement::eventBaseFor(const Condition& condition) const
429 {
430 return condition.m_baseID.isEmpty() ? targetElement() : document()->getElementById(condition.m_baseID);
431 }
432
connectConditions()433 void SVGSMILElement::connectConditions()
434 {
435 if (m_conditionsConnected)
436 disconnectConditions();
437 m_conditionsConnected = true;
438 for (unsigned n = 0; n < m_conditions.size(); ++n) {
439 Condition& condition = m_conditions[n];
440 if (condition.m_type == Condition::EventBase) {
441 ASSERT(!condition.m_syncbase);
442 Element* eventBase = eventBaseFor(condition);
443 if (!eventBase)
444 continue;
445 ASSERT(!condition.m_eventListener);
446 condition.m_eventListener = ConditionEventListener::create(this, &condition);
447 eventBase->addEventListener(condition.m_name, condition.m_eventListener, false);
448 } else if (condition.m_type == Condition::Syncbase) {
449 ASSERT(!condition.m_baseID.isEmpty());
450 condition.m_syncbase = document()->getElementById(condition.m_baseID);
451 if (!isSMILElement(condition.m_syncbase.get())) {
452 condition.m_syncbase = 0;
453 continue;
454 }
455 SVGSMILElement* syncbase = static_cast<SVGSMILElement*>(condition.m_syncbase.get());
456 syncbase->addTimeDependent(this);
457 }
458 }
459 }
460
disconnectConditions()461 void SVGSMILElement::disconnectConditions()
462 {
463 if (!m_conditionsConnected)
464 return;
465 m_conditionsConnected = false;
466 for (unsigned n = 0; n < m_conditions.size(); ++n) {
467 Condition& condition = m_conditions[n];
468 if (condition.m_type == Condition::EventBase) {
469 ASSERT(!condition.m_syncbase);
470 if (!condition.m_eventListener)
471 continue;
472 // Note: It's a memory optimization to try to remove our condition
473 // event listener, but it's not guaranteed to work, since we have
474 // no guarantee that eventBaseFor() will be able to find our condition's
475 // original eventBase. So, we also have to disconnect ourselves from
476 // our condition event listener, in case it later fires.
477 Element* eventBase = eventBaseFor(condition);
478 if (eventBase)
479 eventBase->removeEventListener(condition.m_name, condition.m_eventListener.get(), false);
480 condition.m_eventListener->disconnectAnimation();
481 condition.m_eventListener = 0;
482 } else if (condition.m_type == Condition::Syncbase) {
483 if (condition.m_syncbase) {
484 ASSERT(isSMILElement(condition.m_syncbase.get()));
485 static_cast<SVGSMILElement*>(condition.m_syncbase.get())->removeTimeDependent(this);
486 }
487 }
488 condition.m_syncbase = 0;
489 }
490 }
491
reschedule()492 void SVGSMILElement::reschedule()
493 {
494 if (m_timeContainer)
495 m_timeContainer->schedule(this);
496 }
497
targetElement() const498 SVGElement* SVGSMILElement::targetElement() const
499 {
500 if (m_targetElement)
501 return m_targetElement;
502
503 String href = xlinkHref();
504 ContainerNode* target = href.isEmpty() ? parentNode() : document()->getElementById(SVGURIReference::getTarget(href));
505 if (!target || !target->isSVGElement())
506 return 0;
507
508 m_targetElement = static_cast<SVGElement*>(target);
509 document()->accessSVGExtensions()->addAnimationElementToTarget(const_cast<SVGSMILElement*>(this), m_targetElement);
510 return m_targetElement;
511 }
512
elapsed() const513 SMILTime SVGSMILElement::elapsed() const
514 {
515 return m_timeContainer ? m_timeContainer->elapsed() : 0;
516 }
517
isInactive() const518 bool SVGSMILElement::isInactive() const
519 {
520 return m_activeState == Inactive;
521 }
522
isFrozen() const523 bool SVGSMILElement::isFrozen() const
524 {
525 return m_activeState == Frozen;
526 }
527
restart() const528 SVGSMILElement::Restart SVGSMILElement::restart() const
529 {
530 DEFINE_STATIC_LOCAL(const AtomicString, never, ("never"));
531 DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive"));
532 const AtomicString& value = getAttribute(SVGNames::restartAttr);
533 if (value == never)
534 return RestartNever;
535 if (value == whenNotActive)
536 return RestartWhenNotActive;
537 return RestartAlways;
538 }
539
fill() const540 SVGSMILElement::FillMode SVGSMILElement::fill() const
541 {
542 DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze"));
543 const AtomicString& value = getAttribute(SVGNames::fillAttr);
544 return value == freeze ? FillFreeze : FillRemove;
545 }
546
xlinkHref() const547 String SVGSMILElement::xlinkHref() const
548 {
549 return getAttribute(XLinkNames::hrefAttr);
550 }
551
dur() const552 SMILTime SVGSMILElement::dur() const
553 {
554 if (m_cachedDur != invalidCachedTime)
555 return m_cachedDur;
556 const AtomicString& value = getAttribute(SVGNames::durAttr);
557 SMILTime clockValue = parseClockValue(value);
558 return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
559 }
560
repeatDur() const561 SMILTime SVGSMILElement::repeatDur() const
562 {
563 if (m_cachedRepeatDur != invalidCachedTime)
564 return m_cachedRepeatDur;
565 const AtomicString& value = getAttribute(SVGNames::repeatDurAttr);
566 SMILTime clockValue = parseClockValue(value);
567 m_cachedRepeatDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
568 return m_cachedRepeatDur;
569 }
570
571 // So a count is not really a time but let just all pretend we did not notice.
repeatCount() const572 SMILTime SVGSMILElement::repeatCount() const
573 {
574 if (m_cachedRepeatCount != invalidCachedTime)
575 return m_cachedRepeatCount;
576 const AtomicString& value = getAttribute(SVGNames::repeatCountAttr);
577 if (value.isNull())
578 return SMILTime::unresolved();
579
580 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite"));
581 if (value == indefiniteValue)
582 return SMILTime::indefinite();
583 bool ok;
584 double result = value.string().toDouble(&ok);
585 return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved();
586 }
587
maxValue() const588 SMILTime SVGSMILElement::maxValue() const
589 {
590 if (m_cachedMax != invalidCachedTime)
591 return m_cachedMax;
592 const AtomicString& value = getAttribute(SVGNames::maxAttr);
593 SMILTime result = parseClockValue(value);
594 return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result;
595 }
596
minValue() const597 SMILTime SVGSMILElement::minValue() const
598 {
599 if (m_cachedMin != invalidCachedTime)
600 return m_cachedMin;
601 const AtomicString& value = getAttribute(SVGNames::minAttr);
602 SMILTime result = parseClockValue(value);
603 return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
604 }
605
simpleDuration() const606 SMILTime SVGSMILElement::simpleDuration() const
607 {
608 return min(dur(), SMILTime::indefinite());
609 }
610
addBeginTime(SMILTime time)611 void SVGSMILElement::addBeginTime(SMILTime time)
612 {
613 m_beginTimes.append(time);
614 sortTimeList(m_beginTimes);
615 beginListChanged();
616 }
617
addEndTime(SMILTime time)618 void SVGSMILElement::addEndTime(SMILTime time)
619 {
620 m_endTimes.append(time);
621 sortTimeList(m_endTimes);
622 endListChanged();
623 }
624
findInstanceTime(BeginOrEnd beginOrEnd,SMILTime minimumTime,bool equalsMinimumOK) const625 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
626 {
627 // FIXME: This searches from the beginning which is inefficient. The list is usually not long
628 // (one entry in common cases) but you can construct a case where it does grow.
629 const Vector<SMILTime>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
630 for (unsigned n = 0; n < list.size(); ++n) {
631 SMILTime time = list[n];
632 ASSERT(!time.isUnresolved());
633 if (time.isIndefinite() && beginOrEnd == Begin) {
634 // "The special value "indefinite" does not yield an instance time in the begin list."
635 continue;
636 }
637 if (equalsMinimumOK) {
638 if (time >= minimumTime)
639 return time;
640 } else if (time > minimumTime)
641 return time;
642 }
643 return SMILTime::unresolved();
644 }
645
repeatingDuration() const646 SMILTime SVGSMILElement::repeatingDuration() const
647 {
648 // Computing the active duration
649 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
650 SMILTime repeatCount = this->repeatCount();
651 SMILTime repeatDur = this->repeatDur();
652 SMILTime simpleDuration = this->simpleDuration();
653 if (!simpleDuration || (repeatDur.isUnresolved() && repeatCount.isUnresolved()))
654 return simpleDuration;
655 SMILTime repeatCountDuration = simpleDuration * repeatCount;
656 return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
657 }
658
resolveActiveEnd(SMILTime resolvedBegin,SMILTime resolvedEnd) const659 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
660 {
661 // Computing the active duration
662 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
663 SMILTime preliminaryActiveDuration;
664 if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
665 preliminaryActiveDuration = resolvedEnd - resolvedBegin;
666 else if (!resolvedEnd.isFinite())
667 preliminaryActiveDuration = repeatingDuration();
668 else
669 preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
670
671 SMILTime minValue = this->minValue();
672 SMILTime maxValue = this->maxValue();
673 if (minValue > maxValue) {
674 // Ignore both.
675 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
676 minValue = 0;
677 maxValue = SMILTime::indefinite();
678 }
679 return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
680 }
681
resolveInterval(bool first,SMILTime & beginResult,SMILTime & endResult) const682 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const
683 {
684 // See the pseudocode in
685 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#Timing-BeginEnd-LifeCycle
686 SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
687 SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
688 while (true) {
689 SMILTime tempBegin = findInstanceTime(Begin, beginAfter, true);
690 if (tempBegin.isUnresolved())
691 break;
692 SMILTime tempEnd;
693 if (m_endTimes.isEmpty())
694 tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
695 else {
696 tempEnd = findInstanceTime(End, tempBegin, true);
697 if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd))
698 tempEnd = findInstanceTime(End, tempBegin, false);
699 if (tempEnd.isUnresolved()) {
700 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions)
701 break;
702 }
703 tempEnd = resolveActiveEnd(tempBegin, tempEnd);
704 }
705 if (tempEnd > 0 || !first) {
706 beginResult = tempBegin;
707 endResult = tempEnd;
708 return;
709 }
710 if (restart() == RestartNever)
711 break;
712
713 beginAfter = tempEnd;
714 lastIntervalTempEnd = tempEnd;
715 }
716 beginResult = SMILTime::unresolved();
717 endResult = SMILTime::unresolved();
718 }
719
resolveFirstInterval()720 void SVGSMILElement::resolveFirstInterval()
721 {
722 SMILTime begin;
723 SMILTime end;
724 resolveInterval(true, begin, end);
725 ASSERT(!begin.isIndefinite());
726
727 if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {
728 bool wasUnresolved = m_intervalBegin.isUnresolved();
729 m_intervalBegin = begin;
730 m_intervalEnd = end;
731 notifyDependentsIntervalChanged(wasUnresolved ? NewInterval : ExistingInterval);
732 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
733 reschedule();
734 }
735 }
736
resolveNextInterval()737 void SVGSMILElement::resolveNextInterval()
738 {
739 SMILTime begin;
740 SMILTime end;
741 resolveInterval(false, begin, end);
742 ASSERT(!begin.isIndefinite());
743
744 if (!begin.isUnresolved() && begin != m_intervalBegin) {
745 m_intervalBegin = begin;
746 m_intervalEnd = end;
747 notifyDependentsIntervalChanged(NewInterval);
748 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
749 }
750 }
751
nextProgressTime() const752 SMILTime SVGSMILElement::nextProgressTime() const
753 {
754 return m_nextProgressTime;
755 }
756
beginListChanged()757 void SVGSMILElement::beginListChanged()
758 {
759 SMILTime elapsed = this->elapsed();
760 if (m_isWaitingForFirstInterval)
761 resolveFirstInterval();
762 else if (elapsed < m_intervalBegin) {
763 SMILTime newBegin = findInstanceTime(Begin, elapsed, false);
764 if (newBegin < m_intervalBegin) {
765 // Begin time changed, re-resolve the interval.
766 SMILTime oldBegin = m_intervalBegin;
767 m_intervalBegin = elapsed;
768 resolveInterval(false, m_intervalBegin, m_intervalEnd);
769 ASSERT(!m_intervalBegin.isUnresolved());
770 if (m_intervalBegin != oldBegin)
771 notifyDependentsIntervalChanged(ExistingInterval);
772 }
773 }
774 m_nextProgressTime = elapsed;
775 reschedule();
776 }
777
endListChanged()778 void SVGSMILElement::endListChanged()
779 {
780 SMILTime elapsed = this->elapsed();
781 if (m_isWaitingForFirstInterval)
782 resolveFirstInterval();
783 else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
784 SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
785 if (newEnd < m_intervalEnd) {
786 newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
787 if (newEnd != m_intervalEnd) {
788 m_intervalEnd = newEnd;
789 notifyDependentsIntervalChanged(ExistingInterval);
790 }
791 }
792 }
793 m_nextProgressTime = elapsed;
794 reschedule();
795 }
796
checkRestart(SMILTime elapsed)797 void SVGSMILElement::checkRestart(SMILTime elapsed)
798 {
799 ASSERT(!m_isWaitingForFirstInterval);
800 ASSERT(elapsed >= m_intervalBegin);
801
802 Restart restart = this->restart();
803 if (restart == RestartNever)
804 return;
805
806 if (elapsed < m_intervalEnd) {
807 if (restart != RestartAlways)
808 return;
809 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
810 if (nextBegin < m_intervalEnd) {
811 m_intervalEnd = nextBegin;
812 notifyDependentsIntervalChanged(ExistingInterval);
813 }
814 }
815 if (elapsed >= m_intervalEnd)
816 resolveNextInterval();
817 }
818
calculateAnimationPercentAndRepeat(SMILTime elapsed,unsigned & repeat) const819 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const
820 {
821 SMILTime simpleDuration = this->simpleDuration();
822 repeat = 0;
823 if (simpleDuration.isIndefinite()) {
824 repeat = 0;
825 return 0.f;
826 }
827 if (!simpleDuration) {
828 repeat = 0;
829 return 1.f;
830 }
831 ASSERT(m_intervalBegin.isFinite());
832 ASSERT(simpleDuration.isFinite());
833 SMILTime activeTime = elapsed - m_intervalBegin;
834 SMILTime repeatingDuration = this->repeatingDuration();
835 if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
836 repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value());
837 if (fmod(repeatingDuration.value(), !simpleDuration.value()))
838 repeat--;
839 return 1.f;
840 }
841 repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
842 SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
843 return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
844 }
845
calculateNextProgressTime(SMILTime elapsed) const846 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
847 {
848 if (m_activeState == Active) {
849 // If duration is indefinite the value does not actually change over time. Same is true for <set>.
850 SMILTime simpleDuration = this->simpleDuration();
851 if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) {
852 SMILTime repeatCount = this->repeatCount();
853 SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
854 // We are supposed to do freeze semantics when repeating ends, even if the element is still active.
855 // Take care that we get a timer callback at that point.
856 if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite())
857 return repeatingDurationEnd;
858 return m_intervalEnd;
859 }
860 return elapsed + 0.025;
861 }
862 return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
863 }
864
determineActiveState(SMILTime elapsed) const865 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
866 {
867 if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd)
868 return Active;
869
870 if (m_activeState == Active)
871 return fill() == FillFreeze ? Frozen : Inactive;
872
873 return m_activeState;
874 }
875
isContributing(SMILTime elapsed) const876 bool SVGSMILElement::isContributing(SMILTime elapsed) const
877 {
878 // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
879 return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
880 }
881
progress(SMILTime elapsed,SVGSMILElement * resultElement)882 void SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement)
883 {
884 ASSERT(m_timeContainer);
885 ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
886
887 if (!m_conditionsConnected)
888 connectConditions();
889
890 if (!m_intervalBegin.isFinite()) {
891 ASSERT(m_activeState == Inactive);
892 m_nextProgressTime = SMILTime::unresolved();
893 return;
894 }
895
896 if (elapsed < m_intervalBegin) {
897 ASSERT(m_activeState != Active);
898 if (m_activeState == Frozen && resultElement)
899 updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
900 m_nextProgressTime = m_intervalBegin;
901 return;
902 }
903
904 m_previousIntervalBegin = m_intervalBegin;
905
906 if (m_activeState == Inactive) {
907 m_isWaitingForFirstInterval = false;
908 m_activeState = Active;
909 startedActiveInterval();
910 }
911
912 unsigned repeat;
913 float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
914
915 checkRestart(elapsed);
916
917 ActiveState oldActiveState = m_activeState;
918 m_activeState = determineActiveState(elapsed);
919
920 if (isContributing(elapsed)) {
921 if (resultElement)
922 updateAnimation(percent, repeat, resultElement);
923 m_lastPercent = percent;
924 m_lastRepeat = repeat;
925 }
926
927 if (oldActiveState == Active && m_activeState != Active)
928 endedActiveInterval();
929
930 m_nextProgressTime = calculateNextProgressTime(elapsed);
931 }
932
notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)933 void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)
934 {
935 ASSERT(m_intervalBegin.isFinite());
936 DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ());
937 if (loopBreaker.contains(this))
938 return;
939 loopBreaker.add(this);
940
941 TimeDependentSet::iterator end = m_timeDependents.end();
942 for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) {
943 SVGSMILElement* dependent = *it;
944 dependent->createInstanceTimesFromSyncbase(this, newOrExisting);
945 }
946
947 loopBreaker.remove(this);
948 }
949
createInstanceTimesFromSyncbase(SVGSMILElement * syncbase,NewOrExistingInterval)950 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval)
951 {
952 // FIXME: To be really correct, this should handle updating exising interval by changing
953 // the associated times instead of creating new ones.
954 for (unsigned n = 0; n < m_conditions.size(); ++n) {
955 Condition& condition = m_conditions[n];
956 if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
957 ASSERT(condition.m_name == "begin" || condition.m_name == "end");
958 // No nested time containers in SVG, no need for crazy time space conversions. Phew!
959 SMILTime time = 0;
960 if (condition.m_name == "begin")
961 time = syncbase->m_intervalBegin + condition.m_offset;
962 else
963 time = syncbase->m_intervalEnd + condition.m_offset;
964 ASSERT(time.isFinite());
965 if (condition.m_beginOrEnd == Begin)
966 addBeginTime(time);
967 else
968 addEndTime(time);
969 }
970 }
971 }
972
addTimeDependent(SVGSMILElement * animation)973 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
974 {
975 m_timeDependents.add(animation);
976 if (m_intervalBegin.isFinite())
977 animation->createInstanceTimesFromSyncbase(this, NewInterval);
978 }
979
removeTimeDependent(SVGSMILElement * animation)980 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
981 {
982 m_timeDependents.remove(animation);
983 }
984
handleConditionEvent(Event *,Condition * condition)985 void SVGSMILElement::handleConditionEvent(Event*, Condition* condition)
986 {
987 if (condition->m_beginOrEnd == Begin)
988 addBeginTime(elapsed() + condition->m_offset);
989 else
990 addEndTime(elapsed() + condition->m_offset);
991 }
992
beginByLinkActivation()993 void SVGSMILElement::beginByLinkActivation()
994 {
995 addBeginTime(elapsed());
996 }
997
998 }
999
1000 #endif
1001