KHtml

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

KDE's Doxygen guidelines are available online.