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

KHTML

  • sources
  • kde-4.12
  • kdelibs
  • 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 
47 using namespace std;
48 
49 namespace WebCore {
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 public:
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  }
75 private:
76  SVGSMILElement* m_animation;
77  SVGSMILElement::Condition* m_condition;
78  Element* m_eventBase;
79 };
80 
81 SVGSMILElement::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 
91 SVGSMILElement::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 
112 SVGSMILElement::~SVGSMILElement()
113 {
114  disconnectConditions();
115  if (m_timeContainer)
116  m_timeContainer->unschedule(this);
117 }
118 
119 void 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 
136 void 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 
149 void 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 
163 SMILTime 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 
183 SMILTime 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 
219 static void sortTimeList(Vector<SMILTime>& timeList)
220 {
221  std::sort(timeList.begin(), timeList.end());
222 }
223 
224 bool 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 
291 bool 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 
299 void 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 
319 void 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 
343 void 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 
367 void 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 
394 void 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 
417 void SVGSMILElement::reschedule()
418 {
419  if (m_timeContainer)
420  m_timeContainer->schedule(this);
421 }
422 
423 SVGElement* 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 
432 String SVGSMILElement::attributeName() const
433 {
434  return getAttribute(SVGNames::attributeNameAttr).string().stripWhiteSpace();
435 }
436 
437 SMILTime SVGSMILElement::elapsed() const
438 {
439  return m_timeContainer ? m_timeContainer->elapsed() : 0;
440 }
441 
442 bool SVGSMILElement::isInactive() const
443 {
444  return m_activeState == Inactive;
445 }
446 
447 bool SVGSMILElement::isFrozen() const
448 {
449  return m_activeState == Frozen;
450 }
451 
452 SVGSMILElement::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 
464 SVGSMILElement::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 
471 String SVGSMILElement::xlinkHref() const
472 {
473  return getAttribute(XLinkNames::hrefAttr);
474 }
475 
476 SMILTime 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 
485 SMILTime 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.
495 SMILTime 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 
511 SMILTime 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 
520 SMILTime 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 
529 SMILTime SVGSMILElement::simpleDuration() const
530 {
531  return min(dur(), SMILTime::indefinite());
532 }
533 
534 void SVGSMILElement::addBeginTime(SMILTime time)
535 {
536  m_beginTimes.append(time);
537  sortTimeList(m_beginTimes);
538  beginListChanged();
539 }
540 
541 void SVGSMILElement::addEndTime(SMILTime time)
542 {
543  m_endTimes.append(time);
544  sortTimeList(m_endTimes);
545  endListChanged();
546 }
547 
548 SMILTime 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 
569 SMILTime 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 
582 SMILTime 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 
605 void 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 
642 void 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 
659 void 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 
674 SMILTime SVGSMILElement::nextProgressTime() const
675 {
676  return m_nextProgressTime;
677 }
678 
679 void 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 
700 void 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 
719 void 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 
741 float 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 
768 SMILTime 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 
787 SVGSMILElement::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 
798 bool 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 
804 void 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 
855 void 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 
872 void 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 
895 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
896 {
897  m_timeDependents.add(animation);
898  if (m_intervalBegin.isFinite())
899  animation->createInstanceTimesFromSyncbase(this, NewInterval);
900 }
901 
902 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
903 {
904  m_timeDependents.remove(animation);
905 }
906 
907 void 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 
915 void SVGSMILElement::beginByLinkActivation()
916 {
917  addBeginTime(elapsed());
918 }
919 
920 }
921 
922 #endif
923 
WebCore::SVGNames::minAttr
DOM::QualifiedName minAttr
Definition: SVGNames.cpp:225
Type
Type
WebCore::SVGNames::attributeNameAttr
DOM::QualifiedName attributeNameAttr
Definition: SVGNames.cpp:111
WebCore::SVGNames::animateTransformTag
DOM::QualifiedName animateTransformTag
Definition: SVGNames.cpp:28
SVGParserUtilities.h
DOM::DOMString::string
QString string() const
Definition: dom_string.cpp:236
WebCore::SVGNames::repeatCountAttr
DOM::QualifiedName repeatCountAttr
Definition: SVGNames.cpp:265
WebCore::XLinkNames::hrefAttr
DOM::QualifiedName hrefAttr
Definition: SVGNames.cpp:13
SMILTimeContainer.h
WebCore::SVGNames::repeatDurAttr
DOM::QualifiedName repeatDurAttr
Definition: SVGNames.cpp:266
SVGSMILElement.h
WebCore::SVGNames::maxAttr
DOM::QualifiedName maxAttr
Definition: SVGNames.cpp:222
End
SVGSVGElement.h
WebCore::SVGNames::beginAttr
DOM::QualifiedName beginAttr
Definition: SVGNames.cpp:118
WebCore::narrowPrecisionToFloat
float narrowPrecisionToFloat(T)
WebCore::SVGNames::animateColorTag
DOM::QualifiedName animateColorTag
Definition: SVGNames.cpp:26
FloatConversion.h
Begin
WebCore::SVGNames::endAttr
DOM::QualifiedName endAttr
Definition: SVGNames.cpp:150
WebCore::SVGNames::fillAttr
DOM::QualifiedName fillAttr
Definition: SVGNames.cpp:158
WebCore::SVGNames::setTag
DOM::QualifiedName setTag
Definition: SVGNames.cpp:29
SVGNames.h
WebCore::SVGNames::animateTag
DOM::QualifiedName animateTag
Definition: SVGNames.cpp:25
ok
KGuiItem ok()
SVGURIReference.h
WebCore::SVGNames::durAttr
DOM::QualifiedName durAttr
Definition: SVGNames.cpp:144
DOM::DOMString::substring
DOMString substring(unsigned pos, unsigned len=UINT_MAX) const
Definition: dom_string.cpp:180
XLinkNames.h
WebCore::String
DOM::DOMString String
Definition: PlatformString.h:8
end
const KShortcut & end()
parse
QList< Action > parse(QSettings &ini)
WebCore::SVGNames::restartAttr
DOM::QualifiedName restartAttr
Definition: SVGNames.cpp:269
WebCore::SVGNames::animateMotionTag
DOM::QualifiedName animateMotionTag
Definition: SVGNames.cpp:27
begin
const KShortcut & begin()
list
QStringList list(const QString &fileClass)
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:51:22 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



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