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