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 #include "core/svg/animation/SVGSMILElement.h" 28 29 #include "SVGNames.h" 30 #include "XLinkNames.h" 31 #include "bindings/v8/ExceptionStatePlaceholder.h" 32 #include "core/dom/Document.h" 33 #include "core/events/EventListener.h" 34 #include "core/events/EventSender.h" 35 #include "core/svg/SVGDocumentExtensions.h" 36 #include "core/svg/SVGSVGElement.h" 37 #include "core/svg/SVGURIReference.h" 38 #include "core/svg/animation/SMILTimeContainer.h" 39 #include "platform/FloatConversion.h" 40 #include "wtf/MathExtras.h" 41 #include "wtf/StdLibExtras.h" 42 #include "wtf/Vector.h" 43 44 using namespace std; 45 46 namespace WebCore { 47 48 class RepeatEvent : public Event { 49 public: 50 static PassRefPtr<RepeatEvent> create(const AtomicString& type, int repeat) 51 { 52 return adoptRef(new RepeatEvent(type, false, false, repeat)); 53 } 54 55 ~RepeatEvent() { } 56 57 int repeat() const { return m_repeat; } 58 protected: 59 RepeatEvent(const AtomicString& type, bool canBubble, bool cancelable, int repeat = -1) 60 : Event(type, canBubble, cancelable) 61 , m_repeat(repeat) 62 { 63 } 64 private: 65 int m_repeat; 66 }; 67 68 inline RepeatEvent* toRepeatEvent(Event* event) 69 { 70 ASSERT_WITH_SECURITY_IMPLICATION(!event || event->type() == "repeatn"); 71 return static_cast<RepeatEvent*>(event); 72 } 73 74 static SMILEventSender& smilEndEventSender() 75 { 76 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("endEvent")); 77 return sender; 78 } 79 80 static SMILEventSender& smilBeginEventSender() 81 { 82 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("beginEvent")); 83 return sender; 84 } 85 86 static SMILEventSender& smilRepeatEventSender() 87 { 88 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("repeatEvent")); 89 return sender; 90 } 91 92 static SMILEventSender& smilRepeatNEventSender() 93 { 94 DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("repeatn")); 95 return sender; 96 } 97 98 // This is used for duration type time values that can't be negative. 99 static const double invalidCachedTime = -1.; 100 101 class ConditionEventListener : public EventListener { 102 public: 103 static PassRefPtr<ConditionEventListener> create(SVGSMILElement* animation, SVGSMILElement::Condition* condition) 104 { 105 return adoptRef(new ConditionEventListener(animation, condition)); 106 } 107 108 static const ConditionEventListener* cast(const EventListener* listener) 109 { 110 return listener->type() == ConditionEventListenerType 111 ? static_cast<const ConditionEventListener*>(listener) 112 : 0; 113 } 114 115 virtual bool operator==(const EventListener& other); 116 117 void disconnectAnimation() 118 { 119 m_animation = 0; 120 } 121 122 private: 123 ConditionEventListener(SVGSMILElement* animation, SVGSMILElement::Condition* condition) 124 : EventListener(ConditionEventListenerType) 125 , m_animation(animation) 126 , m_condition(condition) 127 { 128 } 129 130 virtual void handleEvent(ExecutionContext*, Event*); 131 132 SVGSMILElement* m_animation; 133 SVGSMILElement::Condition* m_condition; 134 }; 135 136 bool ConditionEventListener::operator==(const EventListener& listener) 137 { 138 if (const ConditionEventListener* conditionEventListener = ConditionEventListener::cast(&listener)) 139 return m_animation == conditionEventListener->m_animation && m_condition == conditionEventListener->m_condition; 140 return false; 141 } 142 143 void ConditionEventListener::handleEvent(ExecutionContext*, Event* event) 144 { 145 if (!m_animation) 146 return; 147 m_animation->handleConditionEvent(event, m_condition); 148 } 149 150 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeat) 151 : m_type(type) 152 , m_beginOrEnd(beginOrEnd) 153 , m_baseID(baseID) 154 , m_name(name) 155 , m_offset(offset) 156 , m_repeat(repeat) 157 { 158 } 159 160 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document& doc) 161 : SVGElement(tagName, doc) 162 , m_attributeName(anyQName()) 163 , m_targetElement(0) 164 , m_conditionsConnected(false) 165 , m_hasEndEventConditions(false) 166 , m_isWaitingForFirstInterval(true) 167 , m_intervalBegin(SMILTime::unresolved()) 168 , m_intervalEnd(SMILTime::unresolved()) 169 , m_previousIntervalBegin(SMILTime::unresolved()) 170 , m_activeState(Inactive) 171 , m_lastPercent(0) 172 , m_lastRepeat(0) 173 , m_nextProgressTime(0) 174 , m_documentOrderIndex(0) 175 , m_cachedDur(invalidCachedTime) 176 , m_cachedRepeatDur(invalidCachedTime) 177 , m_cachedRepeatCount(invalidCachedTime) 178 , m_cachedMin(invalidCachedTime) 179 , m_cachedMax(invalidCachedTime) 180 { 181 resolveFirstInterval(); 182 } 183 184 SVGSMILElement::~SVGSMILElement() 185 { 186 clearResourceReferences(); 187 smilEndEventSender().cancelEvent(this); 188 smilBeginEventSender().cancelEvent(this); 189 smilRepeatEventSender().cancelEvent(this); 190 smilRepeatNEventSender().cancelEvent(this); 191 disconnectConditions(); 192 if (m_timeContainer && m_targetElement && hasValidAttributeName()) 193 m_timeContainer->unschedule(this, m_targetElement, m_attributeName); 194 } 195 196 void SVGSMILElement::clearResourceReferences() 197 { 198 document().accessSVGExtensions()->removeAllTargetReferencesForElement(this); 199 } 200 201 void SVGSMILElement::buildPendingResource() 202 { 203 clearResourceReferences(); 204 205 if (!inDocument()) { 206 // Reset the target element if we are no longer in the document. 207 setTargetElement(0); 208 return; 209 } 210 211 String id; 212 String href = getAttribute(XLinkNames::hrefAttr); 213 Element* target; 214 if (href.isEmpty()) 215 target = parentNode() && parentNode()->isElementNode() ? toElement(parentNode()) : 0; 216 else 217 target = SVGURIReference::targetElementFromIRIString(href, document(), &id); 218 SVGElement* svgTarget = target && target->isSVGElement() ? toSVGElement(target) : 0; 219 220 if (svgTarget && !svgTarget->inDocument()) 221 svgTarget = 0; 222 223 if (svgTarget != targetElement()) 224 setTargetElement(svgTarget); 225 226 if (!svgTarget) { 227 // Do not register as pending if we are already pending this resource. 228 if (document().accessSVGExtensions()->isElementPendingResource(this, id)) 229 return; 230 231 if (!id.isEmpty()) { 232 document().accessSVGExtensions()->addPendingResource(id, this); 233 ASSERT(hasPendingResources()); 234 } 235 } else { 236 // Register us with the target in the dependencies map. Any change of hrefElement 237 // that leads to relayout/repainting now informs us, so we can react to it. 238 document().accessSVGExtensions()->addElementReferencingTarget(this, svgTarget); 239 } 240 } 241 242 static inline QualifiedName constructQualifiedName(const SVGElement* svgElement, const AtomicString& attributeName) 243 { 244 ASSERT(svgElement); 245 if (attributeName.isEmpty()) 246 return anyQName(); 247 if (!attributeName.contains(':')) 248 return QualifiedName(nullAtom, attributeName, nullAtom); 249 250 AtomicString prefix; 251 AtomicString localName; 252 if (!Document::parseQualifiedName(attributeName, prefix, localName, ASSERT_NO_EXCEPTION)) 253 return anyQName(); 254 255 const AtomicString& namespaceURI = svgElement->lookupNamespaceURI(prefix); 256 if (namespaceURI.isEmpty()) 257 return anyQName(); 258 259 return QualifiedName(nullAtom, localName, namespaceURI); 260 } 261 262 static inline void clearTimesWithDynamicOrigins(Vector<SMILTimeWithOrigin>& timeList) 263 { 264 for (int i = timeList.size() - 1; i >= 0; --i) { 265 if (timeList[i].originIsScript()) 266 timeList.remove(i); 267 } 268 } 269 270 void SVGSMILElement::reset() 271 { 272 clearAnimatedType(m_targetElement); 273 274 m_activeState = Inactive; 275 m_isWaitingForFirstInterval = true; 276 m_intervalBegin = SMILTime::unresolved(); 277 m_intervalEnd = SMILTime::unresolved(); 278 m_previousIntervalBegin = SMILTime::unresolved(); 279 m_lastPercent = 0; 280 m_lastRepeat = 0; 281 m_nextProgressTime = 0; 282 resolveFirstInterval(); 283 } 284 285 Node::InsertionNotificationRequest SVGSMILElement::insertedInto(ContainerNode* rootParent) 286 { 287 SVGElement::insertedInto(rootParent); 288 if (!rootParent->inDocument()) 289 return InsertionDone; 290 291 // Verify we are not in <use> instance tree. 292 ASSERT(!isInShadowTree() || !parentOrShadowHostElement() || !parentOrShadowHostElement()->isSVGElement()); 293 294 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr))); 295 SVGSVGElement* owner = ownerSVGElement(); 296 if (!owner) 297 return InsertionDone; 298 299 m_timeContainer = owner->timeContainer(); 300 ASSERT(m_timeContainer); 301 m_timeContainer->setDocumentOrderIndexesDirty(); 302 303 // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated." 304 if (!fastHasAttribute(SVGNames::beginAttr)) 305 m_beginTimes.append(SMILTimeWithOrigin()); 306 307 if (m_isWaitingForFirstInterval) 308 resolveFirstInterval(); 309 310 if (m_timeContainer) 311 m_timeContainer->notifyIntervalsChanged(); 312 313 buildPendingResource(); 314 315 return InsertionDone; 316 } 317 318 void SVGSMILElement::removedFrom(ContainerNode* rootParent) 319 { 320 if (rootParent->inDocument()) { 321 clearResourceReferences(); 322 disconnectConditions(); 323 setTargetElement(0); 324 setAttributeName(anyQName()); 325 animationAttributeChanged(); 326 m_timeContainer = 0; 327 } 328 329 SVGElement::removedFrom(rootParent); 330 } 331 332 bool SVGSMILElement::hasValidAttributeName() 333 { 334 return attributeName() != anyQName(); 335 } 336 337 SMILTime SVGSMILElement::parseOffsetValue(const String& data) 338 { 339 bool ok; 340 double result = 0; 341 String parse = data.stripWhiteSpace(); 342 if (parse.endsWith('h')) 343 result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60; 344 else if (parse.endsWith("min")) 345 result = parse.left(parse.length() - 3).toDouble(&ok) * 60; 346 else if (parse.endsWith("ms")) 347 result = parse.left(parse.length() - 2).toDouble(&ok) / 1000; 348 else if (parse.endsWith('s')) 349 result = parse.left(parse.length() - 1).toDouble(&ok); 350 else 351 result = parse.toDouble(&ok); 352 if (!ok) 353 return SMILTime::unresolved(); 354 return result; 355 } 356 357 SMILTime SVGSMILElement::parseClockValue(const String& data) 358 { 359 if (data.isNull()) 360 return SMILTime::unresolved(); 361 362 String parse = data.stripWhiteSpace(); 363 364 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral)); 365 if (parse == indefiniteValue) 366 return SMILTime::indefinite(); 367 368 double result = 0; 369 bool ok; 370 size_t doublePointOne = parse.find(':'); 371 size_t doublePointTwo = parse.find(':', doublePointOne + 1); 372 if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) { 373 result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60; 374 if (!ok) 375 return SMILTime::unresolved(); 376 result += parse.substring(3, 2).toUIntStrict(&ok) * 60; 377 if (!ok) 378 return SMILTime::unresolved(); 379 result += parse.substring(6).toDouble(&ok); 380 } else if (doublePointOne == 2 && doublePointTwo == kNotFound && parse.length() >= 5) { 381 result += parse.substring(0, 2).toUIntStrict(&ok) * 60; 382 if (!ok) 383 return SMILTime::unresolved(); 384 result += parse.substring(3).toDouble(&ok); 385 } else 386 return parseOffsetValue(parse); 387 388 if (!ok) 389 return SMILTime::unresolved(); 390 return result; 391 } 392 393 static void sortTimeList(Vector<SMILTimeWithOrigin>& timeList) 394 { 395 std::sort(timeList.begin(), timeList.end()); 396 } 397 398 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd) 399 { 400 String parseString = value.stripWhiteSpace(); 401 402 double sign = 1.; 403 bool ok; 404 size_t pos = parseString.find('+'); 405 if (pos == kNotFound) { 406 pos = parseString.find('-'); 407 if (pos != kNotFound) 408 sign = -1.; 409 } 410 String conditionString; 411 SMILTime offset = 0; 412 if (pos == kNotFound) 413 conditionString = parseString; 414 else { 415 conditionString = parseString.left(pos).stripWhiteSpace(); 416 String offsetString = parseString.substring(pos + 1).stripWhiteSpace(); 417 offset = parseOffsetValue(offsetString); 418 if (offset.isUnresolved()) 419 return false; 420 offset = offset * sign; 421 } 422 if (conditionString.isEmpty()) 423 return false; 424 pos = conditionString.find('.'); 425 426 String baseID; 427 String nameString; 428 if (pos == kNotFound) 429 nameString = conditionString; 430 else { 431 baseID = conditionString.left(pos); 432 nameString = conditionString.substring(pos + 1); 433 } 434 if (nameString.isEmpty()) 435 return false; 436 437 Condition::Type type; 438 int repeat = -1; 439 if (nameString.startsWith("repeat(") && nameString.endsWith(')')) { 440 repeat = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok); 441 if (!ok) 442 return false; 443 nameString = "repeatn"; 444 type = Condition::EventBase; 445 } else if (nameString == "begin" || nameString == "end") { 446 if (baseID.isEmpty()) 447 return false; 448 type = Condition::Syncbase; 449 } else if (nameString.startsWith("accesskey(")) { 450 // FIXME: accesskey() support. 451 type = Condition::AccessKey; 452 } else 453 type = Condition::EventBase; 454 455 m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeat)); 456 457 if (type == Condition::EventBase && beginOrEnd == End) 458 m_hasEndEventConditions = true; 459 460 return true; 461 } 462 463 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd) 464 { 465 Vector<SMILTimeWithOrigin>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes; 466 if (beginOrEnd == End) 467 m_hasEndEventConditions = false; 468 HashSet<double> existing; 469 for (unsigned n = 0; n < timeList.size(); ++n) 470 existing.add(timeList[n].time().value()); 471 Vector<String> splitString; 472 parseString.split(';', splitString); 473 for (unsigned n = 0; n < splitString.size(); ++n) { 474 SMILTime value = parseClockValue(splitString[n]); 475 if (value.isUnresolved()) 476 parseCondition(splitString[n], beginOrEnd); 477 else if (!existing.contains(value.value())) 478 timeList.append(SMILTimeWithOrigin(value, SMILTimeWithOrigin::ParserOrigin)); 479 } 480 sortTimeList(timeList); 481 } 482 483 bool SVGSMILElement::isSupportedAttribute(const QualifiedName& attrName) 484 { 485 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 486 if (supportedAttributes.isEmpty()) { 487 supportedAttributes.add(SVGNames::beginAttr); 488 supportedAttributes.add(SVGNames::endAttr); 489 supportedAttributes.add(SVGNames::durAttr); 490 supportedAttributes.add(SVGNames::repeatDurAttr); 491 supportedAttributes.add(SVGNames::repeatCountAttr); 492 supportedAttributes.add(SVGNames::minAttr); 493 supportedAttributes.add(SVGNames::maxAttr); 494 supportedAttributes.add(SVGNames::attributeNameAttr); 495 supportedAttributes.add(XLinkNames::hrefAttr); 496 } 497 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); 498 } 499 500 void SVGSMILElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 501 { 502 if (name == SVGNames::beginAttr) { 503 if (!m_conditions.isEmpty()) { 504 disconnectConditions(); 505 m_conditions.clear(); 506 parseBeginOrEnd(fastGetAttribute(SVGNames::endAttr), End); 507 } 508 parseBeginOrEnd(value.string(), Begin); 509 if (inDocument()) 510 connectConditions(); 511 } else if (name == SVGNames::endAttr) { 512 if (!m_conditions.isEmpty()) { 513 disconnectConditions(); 514 m_conditions.clear(); 515 parseBeginOrEnd(fastGetAttribute(SVGNames::beginAttr), Begin); 516 } 517 parseBeginOrEnd(value.string(), End); 518 if (inDocument()) 519 connectConditions(); 520 } else 521 SVGElement::parseAttribute(name, value); 522 } 523 524 void SVGSMILElement::svgAttributeChanged(const QualifiedName& attrName) 525 { 526 if (!isSupportedAttribute(attrName)) { 527 SVGElement::svgAttributeChanged(attrName); 528 return; 529 } 530 531 if (attrName == SVGNames::durAttr) 532 m_cachedDur = invalidCachedTime; 533 else if (attrName == SVGNames::repeatDurAttr) 534 m_cachedRepeatDur = invalidCachedTime; 535 else if (attrName == SVGNames::repeatCountAttr) 536 m_cachedRepeatCount = invalidCachedTime; 537 else if (attrName == SVGNames::minAttr) 538 m_cachedMin = invalidCachedTime; 539 else if (attrName == SVGNames::maxAttr) 540 m_cachedMax = invalidCachedTime; 541 else if (attrName == SVGNames::attributeNameAttr) 542 setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr))); 543 else if (attrName.matches(XLinkNames::hrefAttr)) { 544 SVGElementInstance::InvalidationGuard invalidationGuard(this); 545 buildPendingResource(); 546 if (m_targetElement) 547 clearAnimatedType(m_targetElement); 548 } else if (inDocument()) { 549 if (attrName == SVGNames::beginAttr) 550 beginListChanged(elapsed()); 551 else if (attrName == SVGNames::endAttr) 552 endListChanged(elapsed()); 553 } 554 555 animationAttributeChanged(); 556 } 557 558 inline Element* SVGSMILElement::eventBaseFor(const Condition& condition) 559 { 560 return condition.m_baseID.isEmpty() ? targetElement() : treeScope().getElementById(condition.m_baseID); 561 } 562 563 void SVGSMILElement::connectConditions() 564 { 565 if (m_conditionsConnected) 566 disconnectConditions(); 567 m_conditionsConnected = true; 568 for (unsigned n = 0; n < m_conditions.size(); ++n) { 569 Condition& condition = m_conditions[n]; 570 if (condition.m_type == Condition::EventBase) { 571 ASSERT(!condition.m_syncbase); 572 Element* eventBase = eventBaseFor(condition); 573 if (!eventBase) 574 continue; 575 ASSERT(!condition.m_eventListener); 576 condition.m_eventListener = ConditionEventListener::create(this, &condition); 577 eventBase->addEventListener(condition.m_name, condition.m_eventListener, false); 578 } else if (condition.m_type == Condition::Syncbase) { 579 ASSERT(!condition.m_baseID.isEmpty()); 580 condition.m_syncbase = treeScope().getElementById(condition.m_baseID); 581 if (!condition.m_syncbase || !isSVGSMILElement(*condition.m_syncbase)) { 582 condition.m_syncbase = 0; 583 continue; 584 } 585 toSVGSMILElement(condition.m_syncbase.get())->addTimeDependent(this); 586 } 587 } 588 } 589 590 void SVGSMILElement::disconnectConditions() 591 { 592 if (!m_conditionsConnected) 593 return; 594 m_conditionsConnected = false; 595 for (unsigned n = 0; n < m_conditions.size(); ++n) { 596 Condition& condition = m_conditions[n]; 597 if (condition.m_type == Condition::EventBase) { 598 ASSERT(!condition.m_syncbase); 599 if (!condition.m_eventListener) 600 continue; 601 // Note: It's a memory optimization to try to remove our condition 602 // event listener, but it's not guaranteed to work, since we have 603 // no guarantee that eventBaseFor() will be able to find our condition's 604 // original eventBase. So, we also have to disconnect ourselves from 605 // our condition event listener, in case it later fires. 606 Element* eventBase = eventBaseFor(condition); 607 if (eventBase) 608 eventBase->removeEventListener(condition.m_name, condition.m_eventListener.get(), false); 609 condition.m_eventListener->disconnectAnimation(); 610 condition.m_eventListener = 0; 611 } else if (condition.m_type == Condition::Syncbase) { 612 if (condition.m_syncbase) 613 toSVGSMILElement(condition.m_syncbase.get())->removeTimeDependent(this); 614 } 615 condition.m_syncbase = 0; 616 } 617 } 618 619 void SVGSMILElement::setAttributeName(const QualifiedName& attributeName) 620 { 621 if (m_timeContainer && m_targetElement && m_attributeName != attributeName) { 622 if (hasValidAttributeName()) 623 m_timeContainer->unschedule(this, m_targetElement, m_attributeName); 624 m_attributeName = attributeName; 625 if (hasValidAttributeName()) 626 m_timeContainer->schedule(this, m_targetElement, m_attributeName); 627 } else 628 m_attributeName = attributeName; 629 630 // Only clear the animated type, if we had a target before. 631 if (m_targetElement) 632 clearAnimatedType(m_targetElement); 633 } 634 635 void SVGSMILElement::setTargetElement(SVGElement* target) 636 { 637 if (m_timeContainer && hasValidAttributeName()) { 638 if (m_targetElement) 639 m_timeContainer->unschedule(this, m_targetElement, m_attributeName); 640 if (target) 641 m_timeContainer->schedule(this, target, m_attributeName); 642 } 643 644 if (m_targetElement) { 645 // Clear values that may depend on the previous target. 646 clearAnimatedType(m_targetElement); 647 disconnectConditions(); 648 } 649 650 // If the animation state is not Inactive, always reset to a clear state before leaving the old target element. 651 if (m_activeState != Inactive) 652 endedActiveInterval(); 653 654 m_targetElement = target; 655 } 656 657 SMILTime SVGSMILElement::elapsed() const 658 { 659 return m_timeContainer ? m_timeContainer->elapsed() : 0; 660 } 661 662 bool SVGSMILElement::isInactive() const 663 { 664 return m_activeState == Inactive; 665 } 666 667 bool SVGSMILElement::isFrozen() const 668 { 669 return m_activeState == Frozen; 670 } 671 672 SVGSMILElement::Restart SVGSMILElement::restart() const 673 { 674 DEFINE_STATIC_LOCAL(const AtomicString, never, ("never", AtomicString::ConstructFromLiteral)); 675 DEFINE_STATIC_LOCAL(const AtomicString, whenNotActive, ("whenNotActive", AtomicString::ConstructFromLiteral)); 676 const AtomicString& value = fastGetAttribute(SVGNames::restartAttr); 677 if (value == never) 678 return RestartNever; 679 if (value == whenNotActive) 680 return RestartWhenNotActive; 681 return RestartAlways; 682 } 683 684 SVGSMILElement::FillMode SVGSMILElement::fill() const 685 { 686 DEFINE_STATIC_LOCAL(const AtomicString, freeze, ("freeze", AtomicString::ConstructFromLiteral)); 687 const AtomicString& value = fastGetAttribute(SVGNames::fillAttr); 688 return value == freeze ? FillFreeze : FillRemove; 689 } 690 691 SMILTime SVGSMILElement::dur() const 692 { 693 if (m_cachedDur != invalidCachedTime) 694 return m_cachedDur; 695 const AtomicString& value = fastGetAttribute(SVGNames::durAttr); 696 SMILTime clockValue = parseClockValue(value); 697 return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue; 698 } 699 700 SMILTime SVGSMILElement::repeatDur() const 701 { 702 if (m_cachedRepeatDur != invalidCachedTime) 703 return m_cachedRepeatDur; 704 const AtomicString& value = fastGetAttribute(SVGNames::repeatDurAttr); 705 SMILTime clockValue = parseClockValue(value); 706 m_cachedRepeatDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue; 707 return m_cachedRepeatDur; 708 } 709 710 // So a count is not really a time but let just all pretend we did not notice. 711 SMILTime SVGSMILElement::repeatCount() const 712 { 713 if (m_cachedRepeatCount != invalidCachedTime) 714 return m_cachedRepeatCount; 715 const AtomicString& value = fastGetAttribute(SVGNames::repeatCountAttr); 716 if (value.isNull()) 717 return SMILTime::unresolved(); 718 719 DEFINE_STATIC_LOCAL(const AtomicString, indefiniteValue, ("indefinite", AtomicString::ConstructFromLiteral)); 720 if (value == indefiniteValue) 721 return SMILTime::indefinite(); 722 bool ok; 723 double result = value.string().toDouble(&ok); 724 return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved(); 725 } 726 727 SMILTime SVGSMILElement::maxValue() const 728 { 729 if (m_cachedMax != invalidCachedTime) 730 return m_cachedMax; 731 const AtomicString& value = fastGetAttribute(SVGNames::maxAttr); 732 SMILTime result = parseClockValue(value); 733 return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result; 734 } 735 736 SMILTime SVGSMILElement::minValue() const 737 { 738 if (m_cachedMin != invalidCachedTime) 739 return m_cachedMin; 740 const AtomicString& value = fastGetAttribute(SVGNames::minAttr); 741 SMILTime result = parseClockValue(value); 742 return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result; 743 } 744 745 SMILTime SVGSMILElement::simpleDuration() const 746 { 747 return min(dur(), SMILTime::indefinite()); 748 } 749 750 void SVGSMILElement::addBeginTime(SMILTime eventTime, SMILTime beginTime, SMILTimeWithOrigin::Origin origin) 751 { 752 ASSERT(!std::isnan(beginTime.value())); 753 m_beginTimes.append(SMILTimeWithOrigin(beginTime, origin)); 754 sortTimeList(m_beginTimes); 755 beginListChanged(eventTime); 756 } 757 758 void SVGSMILElement::addEndTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin origin) 759 { 760 ASSERT(!std::isnan(endTime.value())); 761 m_endTimes.append(SMILTimeWithOrigin(endTime, origin)); 762 sortTimeList(m_endTimes); 763 endListChanged(eventTime); 764 } 765 766 inline SMILTime extractTimeFromVector(const SMILTimeWithOrigin* position) 767 { 768 return position->time(); 769 } 770 771 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const 772 { 773 const Vector<SMILTimeWithOrigin>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes; 774 int sizeOfList = list.size(); 775 776 if (!sizeOfList) 777 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite(); 778 779 const SMILTimeWithOrigin* result = approximateBinarySearch<const SMILTimeWithOrigin, SMILTime>(list, sizeOfList, minimumTime, extractTimeFromVector); 780 int indexOfResult = result - list.begin(); 781 ASSERT_WITH_SECURITY_IMPLICATION(indexOfResult < sizeOfList); 782 783 if (list[indexOfResult].time() < minimumTime && indexOfResult < sizeOfList - 1) 784 ++indexOfResult; 785 786 const SMILTime& currentTime = list[indexOfResult].time(); 787 788 // The special value "indefinite" does not yield an instance time in the begin list. 789 if (currentTime.isIndefinite() && beginOrEnd == Begin) 790 return SMILTime::unresolved(); 791 792 if (currentTime < minimumTime) 793 return SMILTime::unresolved(); 794 if (currentTime > minimumTime) 795 return currentTime; 796 797 ASSERT(currentTime == minimumTime); 798 if (equalsMinimumOK) 799 return currentTime; 800 801 // If the equals is not accepted, return the next bigger item in the list. 802 SMILTime nextTime = currentTime; 803 while (indexOfResult < sizeOfList - 1) { 804 nextTime = list[indexOfResult + 1].time(); 805 if (nextTime > minimumTime) 806 return nextTime; 807 ++indexOfResult; 808 } 809 810 return beginOrEnd == Begin ? SMILTime::unresolved() : SMILTime::indefinite(); 811 } 812 813 SMILTime SVGSMILElement::repeatingDuration() const 814 { 815 // Computing the active duration 816 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur 817 SMILTime repeatCount = this->repeatCount(); 818 SMILTime repeatDur = this->repeatDur(); 819 SMILTime simpleDuration = this->simpleDuration(); 820 if (!simpleDuration || (repeatDur.isUnresolved() && repeatCount.isUnresolved())) 821 return simpleDuration; 822 SMILTime repeatCountDuration = simpleDuration * repeatCount; 823 return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite())); 824 } 825 826 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const 827 { 828 // Computing the active duration 829 // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur 830 SMILTime preliminaryActiveDuration; 831 if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved()) 832 preliminaryActiveDuration = resolvedEnd - resolvedBegin; 833 else if (!resolvedEnd.isFinite()) 834 preliminaryActiveDuration = repeatingDuration(); 835 else 836 preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin); 837 838 SMILTime minValue = this->minValue(); 839 SMILTime maxValue = this->maxValue(); 840 if (minValue > maxValue) { 841 // Ignore both. 842 // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax 843 minValue = 0; 844 maxValue = SMILTime::indefinite(); 845 } 846 return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration)); 847 } 848 849 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const 850 { 851 // See the pseudocode in http://www.w3.org/TR/SMIL3/smil-timing.html#q90. 852 SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd; 853 SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity(); 854 while (true) { 855 bool equalsMinimumOK = !first || m_intervalEnd > m_intervalBegin; 856 SMILTime tempBegin = findInstanceTime(Begin, beginAfter, equalsMinimumOK); 857 if (tempBegin.isUnresolved()) 858 break; 859 SMILTime tempEnd; 860 if (m_endTimes.isEmpty()) 861 tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite()); 862 else { 863 tempEnd = findInstanceTime(End, tempBegin, true); 864 if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd)) 865 tempEnd = findInstanceTime(End, tempBegin, false); 866 if (tempEnd.isUnresolved()) { 867 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions) 868 break; 869 } 870 tempEnd = resolveActiveEnd(tempBegin, tempEnd); 871 } 872 if (!first || (tempEnd > 0 || (!tempBegin.value() && !tempEnd.value()))) { 873 beginResult = tempBegin; 874 endResult = tempEnd; 875 return; 876 } 877 878 beginAfter = tempEnd; 879 lastIntervalTempEnd = tempEnd; 880 } 881 beginResult = SMILTime::unresolved(); 882 endResult = SMILTime::unresolved(); 883 } 884 885 void SVGSMILElement::resolveFirstInterval() 886 { 887 SMILTime begin; 888 SMILTime end; 889 resolveInterval(true, begin, end); 890 ASSERT(!begin.isIndefinite()); 891 892 if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) { 893 m_intervalBegin = begin; 894 m_intervalEnd = end; 895 notifyDependentsIntervalChanged(); 896 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin); 897 898 if (m_timeContainer) 899 m_timeContainer->notifyIntervalsChanged(); 900 } 901 } 902 903 bool SVGSMILElement::resolveNextInterval() 904 { 905 SMILTime begin; 906 SMILTime end; 907 resolveInterval(false, begin, end); 908 ASSERT(!begin.isIndefinite()); 909 910 if (!begin.isUnresolved() && begin != m_intervalBegin) { 911 m_intervalBegin = begin; 912 m_intervalEnd = end; 913 notifyDependentsIntervalChanged(); 914 m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin); 915 return true; 916 } 917 918 return false; 919 } 920 921 SMILTime SVGSMILElement::nextProgressTime() const 922 { 923 return m_nextProgressTime; 924 } 925 926 void SVGSMILElement::beginListChanged(SMILTime eventTime) 927 { 928 if (m_isWaitingForFirstInterval) 929 resolveFirstInterval(); 930 else { 931 SMILTime newBegin = findInstanceTime(Begin, eventTime, true); 932 if (newBegin.isFinite() && (m_intervalEnd <= eventTime || newBegin < m_intervalBegin)) { 933 // Begin time changed, re-resolve the interval. 934 SMILTime oldBegin = m_intervalBegin; 935 m_intervalEnd = eventTime; 936 resolveInterval(false, m_intervalBegin, m_intervalEnd); 937 ASSERT(!m_intervalBegin.isUnresolved()); 938 if (m_intervalBegin != oldBegin) { 939 if (m_activeState == Active && m_intervalBegin > eventTime) { 940 m_activeState = determineActiveState(eventTime); 941 if (m_activeState != Active) 942 endedActiveInterval(); 943 } 944 notifyDependentsIntervalChanged(); 945 } 946 } 947 } 948 m_nextProgressTime = elapsed(); 949 950 if (m_timeContainer) 951 m_timeContainer->notifyIntervalsChanged(); 952 } 953 954 void SVGSMILElement::endListChanged(SMILTime) 955 { 956 SMILTime elapsed = this->elapsed(); 957 if (m_isWaitingForFirstInterval) 958 resolveFirstInterval(); 959 else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) { 960 SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false); 961 if (newEnd < m_intervalEnd) { 962 newEnd = resolveActiveEnd(m_intervalBegin, newEnd); 963 if (newEnd != m_intervalEnd) { 964 m_intervalEnd = newEnd; 965 notifyDependentsIntervalChanged(); 966 } 967 } 968 } 969 m_nextProgressTime = elapsed; 970 971 if (m_timeContainer) 972 m_timeContainer->notifyIntervalsChanged(); 973 } 974 975 void SVGSMILElement::checkRestart(SMILTime elapsed) 976 { 977 ASSERT(!m_isWaitingForFirstInterval); 978 ASSERT(elapsed >= m_intervalBegin); 979 980 Restart restart = this->restart(); 981 if (restart == RestartNever) 982 return; 983 984 if (elapsed < m_intervalEnd) { 985 if (restart != RestartAlways) 986 return; 987 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false); 988 if (nextBegin < m_intervalEnd) { 989 m_intervalEnd = nextBegin; 990 notifyDependentsIntervalChanged(); 991 } 992 } 993 994 if (elapsed >= m_intervalEnd) 995 resolveNextInterval(); 996 } 997 998 void SVGSMILElement::seekToIntervalCorrespondingToTime(SMILTime elapsed) 999 { 1000 ASSERT(!m_isWaitingForFirstInterval); 1001 ASSERT(elapsed >= m_intervalBegin); 1002 1003 // Manually seek from interval to interval, just as if the animation would run regulary. 1004 while (true) { 1005 // Figure out the next value in the begin time list after the current interval begin. 1006 SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false); 1007 1008 // If the 'nextBegin' time is unresolved (eg. just one defined interval), we're done seeking. 1009 if (nextBegin.isUnresolved()) 1010 return; 1011 1012 // If the 'nextBegin' time is larger than or equal to the current interval end time, we're done seeking. 1013 // If the 'elapsed' time is smaller than the next begin interval time, we're done seeking. 1014 if (nextBegin < m_intervalEnd && elapsed >= nextBegin) { 1015 // End current interval, and start a new interval from the 'nextBegin' time. 1016 m_intervalEnd = nextBegin; 1017 if (!resolveNextInterval()) 1018 break; 1019 continue; 1020 } 1021 1022 // If the desired 'elapsed' time is past the current interval, advance to the next. 1023 if (elapsed >= m_intervalEnd) { 1024 if (!resolveNextInterval()) 1025 break; 1026 continue; 1027 } 1028 1029 return; 1030 } 1031 } 1032 1033 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const 1034 { 1035 SMILTime simpleDuration = this->simpleDuration(); 1036 repeat = 0; 1037 if (simpleDuration.isIndefinite()) { 1038 repeat = 0; 1039 return 0.f; 1040 } 1041 if (!simpleDuration) { 1042 repeat = 0; 1043 return 1.f; 1044 } 1045 ASSERT(m_intervalBegin.isFinite()); 1046 ASSERT(simpleDuration.isFinite()); 1047 SMILTime activeTime = elapsed - m_intervalBegin; 1048 SMILTime repeatingDuration = this->repeatingDuration(); 1049 if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) { 1050 repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value()); 1051 if (!fmod(repeatingDuration.value(), simpleDuration.value())) 1052 repeat--; 1053 1054 double percent = (m_intervalEnd.value() - m_intervalBegin.value()) / simpleDuration.value(); 1055 percent = percent - floor(percent); 1056 if (percent < numeric_limits<float>::epsilon() || 1 - percent < numeric_limits<float>::epsilon()) 1057 return 1.0f; 1058 return narrowPrecisionToFloat(percent); 1059 } 1060 repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value()); 1061 SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value()); 1062 return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value()); 1063 } 1064 1065 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const 1066 { 1067 if (m_activeState == Active) { 1068 // If duration is indefinite the value does not actually change over time. Same is true for <set>. 1069 SMILTime simpleDuration = this->simpleDuration(); 1070 if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) { 1071 SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration(); 1072 // We are supposed to do freeze semantics when repeating ends, even if the element is still active. 1073 // Take care that we get a timer callback at that point. 1074 if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite()) 1075 return repeatingDurationEnd; 1076 return m_intervalEnd; 1077 } 1078 return elapsed + 0.025; 1079 } 1080 return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved(); 1081 } 1082 1083 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const 1084 { 1085 if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd) 1086 return Active; 1087 1088 return fill() == FillFreeze ? Frozen : Inactive; 1089 } 1090 1091 bool SVGSMILElement::isContributing(SMILTime elapsed) const 1092 { 1093 // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove. 1094 return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen; 1095 } 1096 1097 bool SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement, bool seekToTime) 1098 { 1099 ASSERT(resultElement); 1100 ASSERT(m_timeContainer); 1101 ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite()); 1102 1103 if (!m_conditionsConnected) 1104 connectConditions(); 1105 1106 if (!m_intervalBegin.isFinite()) { 1107 ASSERT(m_activeState == Inactive); 1108 m_nextProgressTime = SMILTime::unresolved(); 1109 return false; 1110 } 1111 1112 if (elapsed < m_intervalBegin) { 1113 ASSERT(m_activeState != Active); 1114 if (m_activeState == Frozen) { 1115 if (this == resultElement) 1116 resetAnimatedType(); 1117 updateAnimation(m_lastPercent, m_lastRepeat, resultElement); 1118 } 1119 m_nextProgressTime = m_intervalBegin; 1120 return false; 1121 } 1122 1123 m_previousIntervalBegin = m_intervalBegin; 1124 1125 if (m_isWaitingForFirstInterval) { 1126 m_isWaitingForFirstInterval = false; 1127 resolveFirstInterval(); 1128 } 1129 1130 // This call may obtain a new interval -- never call calculateAnimationPercentAndRepeat() before! 1131 if (seekToTime) { 1132 seekToIntervalCorrespondingToTime(elapsed); 1133 if (elapsed < m_intervalBegin) { 1134 // elapsed is not within an interval. 1135 m_nextProgressTime = m_intervalBegin; 1136 return false; 1137 } 1138 } 1139 1140 unsigned repeat = 0; 1141 float percent = calculateAnimationPercentAndRepeat(elapsed, repeat); 1142 checkRestart(elapsed); 1143 1144 ActiveState oldActiveState = m_activeState; 1145 m_activeState = determineActiveState(elapsed); 1146 bool animationIsContributing = isContributing(elapsed); 1147 1148 // Only reset the animated type to the base value once for the lowest priority animation that animates and contributes to a particular element/attribute pair. 1149 if (this == resultElement && animationIsContributing) 1150 resetAnimatedType(); 1151 1152 if (animationIsContributing) { 1153 if (oldActiveState == Inactive) { 1154 smilBeginEventSender().dispatchEventSoon(this); 1155 startedActiveInterval(); 1156 } 1157 1158 if (repeat && repeat != m_lastRepeat) 1159 dispatchRepeatEvents(repeat); 1160 1161 updateAnimation(percent, repeat, resultElement); 1162 m_lastPercent = percent; 1163 m_lastRepeat = repeat; 1164 } 1165 1166 if (oldActiveState == Active && m_activeState != Active) { 1167 smilEndEventSender().dispatchEventSoon(this); 1168 endedActiveInterval(); 1169 if (m_activeState != Frozen && this == resultElement) 1170 clearAnimatedType(m_targetElement); 1171 } 1172 1173 // Triggering all the pending events if the animation timeline is changed. 1174 if (seekToTime) { 1175 if (m_activeState == Inactive) 1176 smilBeginEventSender().dispatchEventSoon(this); 1177 1178 if (repeat) { 1179 for (unsigned repeatEventCount = 1; repeatEventCount < repeat; repeatEventCount++) 1180 dispatchRepeatEvents(repeatEventCount); 1181 if (m_activeState == Inactive) 1182 dispatchRepeatEvents(repeat); 1183 } 1184 1185 if (m_activeState == Inactive || m_activeState == Frozen) 1186 smilEndEventSender().dispatchEventSoon(this); 1187 } 1188 1189 m_nextProgressTime = calculateNextProgressTime(elapsed); 1190 return animationIsContributing; 1191 } 1192 1193 void SVGSMILElement::notifyDependentsIntervalChanged() 1194 { 1195 ASSERT(m_intervalBegin.isFinite()); 1196 DEFINE_STATIC_LOCAL(HashSet<SVGSMILElement*>, loopBreaker, ()); 1197 if (!loopBreaker.add(this).isNewEntry) 1198 return; 1199 1200 TimeDependentSet::iterator end = m_timeDependents.end(); 1201 for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) { 1202 SVGSMILElement* dependent = *it; 1203 dependent->createInstanceTimesFromSyncbase(this); 1204 } 1205 1206 loopBreaker.remove(this); 1207 } 1208 1209 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase) 1210 { 1211 // FIXME: To be really correct, this should handle updating exising interval by changing 1212 // the associated times instead of creating new ones. 1213 for (unsigned n = 0; n < m_conditions.size(); ++n) { 1214 Condition& condition = m_conditions[n]; 1215 if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) { 1216 ASSERT(condition.m_name == "begin" || condition.m_name == "end"); 1217 // No nested time containers in SVG, no need for crazy time space conversions. Phew! 1218 SMILTime time = 0; 1219 if (condition.m_name == "begin") 1220 time = syncbase->m_intervalBegin + condition.m_offset; 1221 else 1222 time = syncbase->m_intervalEnd + condition.m_offset; 1223 ASSERT(time.isFinite()); 1224 if (condition.m_beginOrEnd == Begin) 1225 addBeginTime(elapsed(), time); 1226 else 1227 addEndTime(elapsed(), time); 1228 } 1229 } 1230 } 1231 1232 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation) 1233 { 1234 m_timeDependents.add(animation); 1235 if (m_intervalBegin.isFinite()) 1236 animation->createInstanceTimesFromSyncbase(this); 1237 } 1238 1239 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation) 1240 { 1241 m_timeDependents.remove(animation); 1242 } 1243 1244 void SVGSMILElement::handleConditionEvent(Event* event, Condition* condition) 1245 { 1246 if (event->type() == "repeatn" && toRepeatEvent(event)->repeat() != condition->m_repeat) 1247 return; 1248 1249 SMILTime elapsed = this->elapsed(); 1250 if (condition->m_beginOrEnd == Begin) 1251 addBeginTime(elapsed, elapsed + condition->m_offset); 1252 else 1253 addEndTime(elapsed, elapsed + condition->m_offset); 1254 } 1255 1256 void SVGSMILElement::beginByLinkActivation() 1257 { 1258 SMILTime elapsed = this->elapsed(); 1259 addBeginTime(elapsed, elapsed); 1260 } 1261 1262 void SVGSMILElement::endedActiveInterval() 1263 { 1264 clearTimesWithDynamicOrigins(m_beginTimes); 1265 clearTimesWithDynamicOrigins(m_endTimes); 1266 } 1267 1268 void SVGSMILElement::dispatchRepeatEvents(unsigned count) 1269 { 1270 m_repeatEventCountList.append(count); 1271 smilRepeatEventSender().dispatchEventSoon(this); 1272 smilRepeatNEventSender().dispatchEventSoon(this); 1273 } 1274 1275 void SVGSMILElement::dispatchPendingEvent(SMILEventSender* eventSender) 1276 { 1277 ASSERT(eventSender == &smilEndEventSender() || eventSender == &smilBeginEventSender() || eventSender == &smilRepeatEventSender() || eventSender == &smilRepeatNEventSender()); 1278 const AtomicString& eventType = eventSender->eventType(); 1279 if (eventType == "repeatn") { 1280 unsigned repeatEventCount = m_repeatEventCountList.first(); 1281 m_repeatEventCountList.remove(0); 1282 dispatchEvent(RepeatEvent::create(eventType, repeatEventCount)); 1283 } else { 1284 dispatchEvent(Event::create(eventType)); 1285 } 1286 } 1287 1288 } 1289