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