• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KHTML

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

KDE's Doxygen guidelines are available online.

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal