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