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