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