PageRenderTime 54ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/third_party/WebKit/Source/core/svg/animation/SMILTimeContainer.cpp

https://gitlab.com/jonnialva90/iridium-browser
C++ | 565 lines | 419 code | 87 blank | 59 comment | 92 complexity | d69416013d4a1a669f3f741222b9e7bf MD5 | raw file
  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. #include "config.h"
  26. #include "core/svg/animation/SMILTimeContainer.h"
  27. #include "core/animation/AnimationClock.h"
  28. #include "core/animation/AnimationTimeline.h"
  29. #include "core/dom/ElementTraversal.h"
  30. #include "core/frame/FrameView.h"
  31. #include "core/frame/Settings.h"
  32. #include "core/svg/SVGSVGElement.h"
  33. #include "core/svg/animation/SVGSMILElement.h"
  34. namespace blink {
  35. static const double initialFrameDelay = 0.025;
  36. static const double animationPolicyOnceDuration = 3.000;
  37. #if !ENABLE(OILPAN)
  38. // Every entry-point that calls updateAnimations() should instantiate a
  39. // DiscardScope to prevent deletion of the ownerElement (and hence itself.)
  40. class DiscardScope {
  41. public:
  42. explicit DiscardScope(SVGSVGElement& timeContainerOwner) : m_discardScopeElement(&timeContainerOwner) { }
  43. private:
  44. RefPtr<SVGSVGElement> m_discardScopeElement;
  45. };
  46. #endif
  47. SMILTimeContainer::SMILTimeContainer(SVGSVGElement& owner)
  48. : m_beginTime(0)
  49. , m_pauseTime(0)
  50. , m_resumeTime(0)
  51. , m_accumulatedActiveTime(0)
  52. , m_presetStartTime(0)
  53. , m_frameSchedulingState(Idle)
  54. , m_documentOrderIndexesDirty(false)
  55. , m_wakeupTimer(this, &SMILTimeContainer::wakeupTimerFired)
  56. , m_animationPolicyOnceTimer(this, &SMILTimeContainer::animationPolicyTimerFired)
  57. , m_ownerSVGElement(&owner)
  58. #if ENABLE(ASSERT)
  59. , m_preventScheduledAnimationsChanges(false)
  60. #endif
  61. {
  62. }
  63. SMILTimeContainer::~SMILTimeContainer()
  64. {
  65. cancelAnimationFrame();
  66. cancelAnimationPolicyTimer();
  67. ASSERT(!m_wakeupTimer.isActive());
  68. #if ENABLE(ASSERT)
  69. ASSERT(!m_preventScheduledAnimationsChanges);
  70. #endif
  71. }
  72. void SMILTimeContainer::schedule(SVGSMILElement* animation, SVGElement* target, const QualifiedName& attributeName)
  73. {
  74. ASSERT(animation->timeContainer() == this);
  75. ASSERT(target);
  76. ASSERT(animation->hasValidAttributeName());
  77. ASSERT(animation->hasValidAttributeType());
  78. ASSERT(animation->inActiveDocument());
  79. #if ENABLE(ASSERT)
  80. ASSERT(!m_preventScheduledAnimationsChanges);
  81. #endif
  82. ElementAttributePair key(target, attributeName);
  83. OwnPtrWillBeMember<AnimationsLinkedHashSet>& scheduled = m_scheduledAnimations.add(key, nullptr).storedValue->value;
  84. if (!scheduled)
  85. scheduled = adoptPtrWillBeNoop(new AnimationsLinkedHashSet);
  86. ASSERT(!scheduled->contains(animation));
  87. scheduled->add(animation);
  88. SMILTime nextFireTime = animation->nextProgressTime();
  89. if (nextFireTime.isFinite())
  90. notifyIntervalsChanged();
  91. }
  92. void SMILTimeContainer::unschedule(SVGSMILElement* animation, SVGElement* target, const QualifiedName& attributeName)
  93. {
  94. ASSERT(animation->timeContainer() == this);
  95. #if ENABLE(ASSERT)
  96. ASSERT(!m_preventScheduledAnimationsChanges);
  97. #endif
  98. ElementAttributePair key(target, attributeName);
  99. GroupedAnimationsMap::iterator it = m_scheduledAnimations.find(key);
  100. ASSERT(it != m_scheduledAnimations.end());
  101. AnimationsLinkedHashSet* scheduled = it->value.get();
  102. ASSERT(scheduled);
  103. AnimationsLinkedHashSet::iterator itAnimation = scheduled->find(animation);
  104. ASSERT(itAnimation != scheduled->end());
  105. scheduled->remove(itAnimation);
  106. if (scheduled->isEmpty())
  107. m_scheduledAnimations.remove(it);
  108. }
  109. bool SMILTimeContainer::hasAnimations() const
  110. {
  111. return !m_scheduledAnimations.isEmpty();
  112. }
  113. bool SMILTimeContainer::hasPendingSynchronization() const
  114. {
  115. return m_frameSchedulingState == SynchronizeAnimations && m_wakeupTimer.isActive() && !m_wakeupTimer.nextFireInterval();
  116. }
  117. void SMILTimeContainer::notifyIntervalsChanged()
  118. {
  119. if (!isStarted())
  120. return;
  121. // Schedule updateAnimations() to be called asynchronously so multiple intervals
  122. // can change with updateAnimations() only called once at the end.
  123. if (hasPendingSynchronization())
  124. return;
  125. cancelAnimationFrame();
  126. scheduleWakeUp(0, SynchronizeAnimations);
  127. }
  128. SMILTime SMILTimeContainer::elapsed() const
  129. {
  130. if (!m_beginTime)
  131. return 0;
  132. if (isPaused())
  133. return m_accumulatedActiveTime;
  134. return currentTime() + m_accumulatedActiveTime - lastResumeTime();
  135. }
  136. bool SMILTimeContainer::isPaused() const
  137. {
  138. // If animation policy is "none", it is always paused.
  139. return m_pauseTime || animationPolicy() == ImageAnimationPolicyNoAnimation;
  140. }
  141. bool SMILTimeContainer::isStarted() const
  142. {
  143. return m_beginTime;
  144. }
  145. void SMILTimeContainer::begin()
  146. {
  147. RELEASE_ASSERT(!m_beginTime);
  148. if (!document().isActive())
  149. return;
  150. if (!handleAnimationPolicy(RestartOnceTimerIfNotPaused))
  151. return;
  152. double now = currentTime();
  153. // If 'm_presetStartTime' is set, the timeline was modified via setElapsed() before the document began.
  154. // In this case pass on 'seekToTime=true' to updateAnimations().
  155. m_beginTime = now - m_presetStartTime;
  156. #if !ENABLE(OILPAN)
  157. DiscardScope discardScope(ownerSVGElement());
  158. #endif
  159. SMILTime earliestFireTime = updateAnimations(SMILTime(m_presetStartTime), m_presetStartTime ? true : false);
  160. m_presetStartTime = 0;
  161. if (m_pauseTime) {
  162. m_pauseTime = now;
  163. // If updateAnimations() caused new syncbase instance to be generated,
  164. // we don't want to cancel those. Excepting that, no frame should've
  165. // been scheduled at this point.
  166. ASSERT(m_frameSchedulingState == Idle || m_frameSchedulingState == SynchronizeAnimations);
  167. } else if (!hasPendingSynchronization()) {
  168. ASSERT(isTimelineRunning());
  169. // If the timeline is running, and there's pending animation updates,
  170. // always perform the first update after the timeline was started using
  171. // the wake-up mechanism.
  172. if (earliestFireTime.isFinite()) {
  173. SMILTime delay = earliestFireTime - elapsed();
  174. scheduleWakeUp(std::max(initialFrameDelay, delay.value()), SynchronizeAnimations);
  175. }
  176. }
  177. }
  178. void SMILTimeContainer::pause()
  179. {
  180. if (!handleAnimationPolicy(CancelOnceTimer))
  181. return;
  182. ASSERT(!isPaused());
  183. m_pauseTime = currentTime();
  184. if (m_beginTime) {
  185. m_accumulatedActiveTime += m_pauseTime - lastResumeTime();
  186. cancelAnimationFrame();
  187. }
  188. m_resumeTime = 0;
  189. }
  190. void SMILTimeContainer::resume()
  191. {
  192. if (!handleAnimationPolicy(RestartOnceTimer))
  193. return;
  194. ASSERT(isPaused());
  195. m_resumeTime = currentTime();
  196. m_pauseTime = 0;
  197. scheduleWakeUp(0, SynchronizeAnimations);
  198. }
  199. void SMILTimeContainer::setElapsed(SMILTime time)
  200. {
  201. // If the documment didn't begin yet, record a new start time, we'll seek to once its possible.
  202. if (!m_beginTime) {
  203. m_presetStartTime = time.value();
  204. return;
  205. }
  206. if (!handleAnimationPolicy(RestartOnceTimerIfNotPaused))
  207. return;
  208. cancelAnimationFrame();
  209. double now = currentTime();
  210. m_beginTime = now - time.value();
  211. m_resumeTime = 0;
  212. if (m_pauseTime) {
  213. m_pauseTime = now;
  214. m_accumulatedActiveTime = time.value();
  215. } else {
  216. m_accumulatedActiveTime = 0;
  217. }
  218. #if ENABLE(ASSERT)
  219. m_preventScheduledAnimationsChanges = true;
  220. #endif
  221. for (const auto& entry : m_scheduledAnimations) {
  222. if (!entry.key.first)
  223. continue;
  224. AnimationsLinkedHashSet* scheduled = entry.value.get();
  225. for (SVGSMILElement* element : *scheduled)
  226. element->reset();
  227. }
  228. #if ENABLE(ASSERT)
  229. m_preventScheduledAnimationsChanges = false;
  230. #endif
  231. updateAnimationsAndScheduleFrameIfNeeded(time, true);
  232. }
  233. bool SMILTimeContainer::isTimelineRunning() const
  234. {
  235. return m_beginTime && !isPaused();
  236. }
  237. void SMILTimeContainer::scheduleAnimationFrame(SMILTime fireTime)
  238. {
  239. ASSERT(isTimelineRunning() && fireTime.isFinite());
  240. ASSERT(!m_wakeupTimer.isActive());
  241. SMILTime delay = fireTime - elapsed();
  242. if (delay.value() < AnimationTimeline::s_minimumDelay) {
  243. serviceOnNextFrame();
  244. } else {
  245. scheduleWakeUp(delay.value() - AnimationTimeline::s_minimumDelay, FutureAnimationFrame);
  246. }
  247. }
  248. void SMILTimeContainer::cancelAnimationFrame()
  249. {
  250. m_frameSchedulingState = Idle;
  251. m_wakeupTimer.stop();
  252. }
  253. void SMILTimeContainer::scheduleWakeUp(double delayTime, FrameSchedulingState frameSchedulingState)
  254. {
  255. ASSERT(frameSchedulingState == SynchronizeAnimations || frameSchedulingState == FutureAnimationFrame);
  256. m_wakeupTimer.startOneShot(delayTime, FROM_HERE);
  257. m_frameSchedulingState = frameSchedulingState;
  258. }
  259. void SMILTimeContainer::wakeupTimerFired(Timer<SMILTimeContainer>*)
  260. {
  261. ASSERT(m_frameSchedulingState == SynchronizeAnimations || m_frameSchedulingState == FutureAnimationFrame);
  262. if (m_frameSchedulingState == FutureAnimationFrame) {
  263. ASSERT(isTimelineRunning());
  264. m_frameSchedulingState = Idle;
  265. serviceOnNextFrame();
  266. } else {
  267. m_frameSchedulingState = Idle;
  268. updateAnimationsAndScheduleFrameIfNeeded(elapsed());
  269. }
  270. }
  271. void SMILTimeContainer::scheduleAnimationPolicyTimer()
  272. {
  273. m_animationPolicyOnceTimer.startOneShot(animationPolicyOnceDuration, FROM_HERE);
  274. }
  275. void SMILTimeContainer::cancelAnimationPolicyTimer()
  276. {
  277. if (m_animationPolicyOnceTimer.isActive())
  278. m_animationPolicyOnceTimer.stop();
  279. }
  280. void SMILTimeContainer::animationPolicyTimerFired(Timer<SMILTimeContainer>*)
  281. {
  282. pause();
  283. }
  284. ImageAnimationPolicy SMILTimeContainer::animationPolicy() const
  285. {
  286. Settings* settings = document().settings();
  287. if (!settings)
  288. return ImageAnimationPolicyAllowed;
  289. return settings->imageAnimationPolicy();
  290. }
  291. bool SMILTimeContainer::handleAnimationPolicy(AnimationPolicyOnceAction onceAction)
  292. {
  293. ImageAnimationPolicy policy = animationPolicy();
  294. // If the animation policy is "none", control is not allowed.
  295. // returns false to exit flow.
  296. if (policy == ImageAnimationPolicyNoAnimation)
  297. return false;
  298. // If the animation policy is "once",
  299. if (policy == ImageAnimationPolicyAnimateOnce) {
  300. switch (onceAction) {
  301. case RestartOnceTimerIfNotPaused:
  302. if (isPaused())
  303. break;
  304. /* fall through */
  305. case RestartOnceTimer:
  306. scheduleAnimationPolicyTimer();
  307. break;
  308. case CancelOnceTimer:
  309. cancelAnimationPolicyTimer();
  310. break;
  311. }
  312. }
  313. if (policy == ImageAnimationPolicyAllowed) {
  314. // When the SVG owner element becomes detached from its document,
  315. // the policy defaults to ImageAnimationPolicyAllowed; there's
  316. // no way back. If the policy had been "once" prior to that,
  317. // ensure cancellation of its timer.
  318. if (onceAction == CancelOnceTimer)
  319. cancelAnimationPolicyTimer();
  320. }
  321. return true;
  322. }
  323. void SMILTimeContainer::updateDocumentOrderIndexes()
  324. {
  325. unsigned timingElementCount = 0;
  326. for (SVGSMILElement& element : Traversal<SVGSMILElement>::descendantsOf(ownerSVGElement()))
  327. element.setDocumentOrderIndex(timingElementCount++);
  328. m_documentOrderIndexesDirty = false;
  329. }
  330. struct PriorityCompare {
  331. PriorityCompare(SMILTime elapsed) : m_elapsed(elapsed) {}
  332. bool operator()(const RefPtrWillBeMember<SVGSMILElement>& a, const RefPtrWillBeMember<SVGSMILElement>& b)
  333. {
  334. // FIXME: This should also consider possible timing relations between the elements.
  335. SMILTime aBegin = a->intervalBegin();
  336. SMILTime bBegin = b->intervalBegin();
  337. // Frozen elements need to be prioritized based on their previous interval.
  338. aBegin = a->isFrozen() && m_elapsed < aBegin ? a->previousIntervalBegin() : aBegin;
  339. bBegin = b->isFrozen() && m_elapsed < bBegin ? b->previousIntervalBegin() : bBegin;
  340. if (aBegin == bBegin)
  341. return a->documentOrderIndex() < b->documentOrderIndex();
  342. return aBegin < bBegin;
  343. }
  344. SMILTime m_elapsed;
  345. };
  346. SVGSVGElement& SMILTimeContainer::ownerSVGElement() const
  347. {
  348. return *m_ownerSVGElement;
  349. }
  350. Document& SMILTimeContainer::document() const
  351. {
  352. return ownerSVGElement().document();
  353. }
  354. double SMILTimeContainer::currentTime() const
  355. {
  356. return document().animationClock().currentTime();
  357. }
  358. void SMILTimeContainer::serviceOnNextFrame()
  359. {
  360. if (document().view()) {
  361. document().view()->scheduleAnimation();
  362. m_frameSchedulingState = AnimationFrame;
  363. }
  364. }
  365. void SMILTimeContainer::serviceAnimations(double monotonicAnimationStartTime)
  366. {
  367. if (m_frameSchedulingState != AnimationFrame)
  368. return;
  369. m_frameSchedulingState = Idle;
  370. updateAnimationsAndScheduleFrameIfNeeded(elapsed());
  371. }
  372. void SMILTimeContainer::updateAnimationsAndScheduleFrameIfNeeded(SMILTime elapsed, bool seekToTime)
  373. {
  374. if (!document().isActive())
  375. return;
  376. #if !ENABLE(OILPAN)
  377. DiscardScope discardScope(ownerSVGElement());
  378. #endif
  379. SMILTime earliestFireTime = updateAnimations(elapsed, seekToTime);
  380. // If updateAnimations() ended up triggering a synchronization (most likely
  381. // via syncbases), then give that priority.
  382. if (hasPendingSynchronization())
  383. return;
  384. if (!isTimelineRunning())
  385. return;
  386. if (!earliestFireTime.isFinite())
  387. return;
  388. scheduleAnimationFrame(earliestFireTime);
  389. }
  390. SMILTime SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime)
  391. {
  392. ASSERT(document().isActive());
  393. SMILTime earliestFireTime = SMILTime::unresolved();
  394. #if ENABLE(ASSERT)
  395. // This boolean will catch any attempts to schedule/unschedule scheduledAnimations during this critical section.
  396. // Similarly, any elements removed will unschedule themselves, so this will catch modification of animationsToApply.
  397. m_preventScheduledAnimationsChanges = true;
  398. #endif
  399. if (m_documentOrderIndexesDirty)
  400. updateDocumentOrderIndexes();
  401. WillBeHeapHashSet<ElementAttributePair> invalidKeys;
  402. using AnimationsVector = WillBeHeapVector<RefPtrWillBeMember<SVGSMILElement>>;
  403. AnimationsVector animationsToApply;
  404. AnimationsVector scheduledAnimationsInSameGroup;
  405. for (const auto& entry : m_scheduledAnimations) {
  406. if (!entry.key.first || entry.value->isEmpty()) {
  407. invalidKeys.add(entry.key);
  408. continue;
  409. }
  410. // Sort according to priority. Elements with later begin time have higher priority.
  411. // In case of a tie, document order decides.
  412. // FIXME: This should also consider timing relationships between the elements. Dependents
  413. // have higher priority.
  414. copyToVector(*entry.value, scheduledAnimationsInSameGroup);
  415. std::sort(scheduledAnimationsInSameGroup.begin(), scheduledAnimationsInSameGroup.end(), PriorityCompare(elapsed));
  416. SVGSMILElement* resultElement = nullptr;
  417. for (const auto& itAnimation : scheduledAnimationsInSameGroup) {
  418. SVGSMILElement* animation = itAnimation.get();
  419. ASSERT(animation->timeContainer() == this);
  420. ASSERT(animation->targetElement());
  421. ASSERT(animation->hasValidAttributeName());
  422. ASSERT(animation->hasValidAttributeType());
  423. // Results are accumulated to the first animation that animates and contributes to a particular element/attribute pair.
  424. if (!resultElement)
  425. resultElement = animation;
  426. // This will calculate the contribution from the animation and add it to the resultsElement.
  427. if (!animation->progress(elapsed, resultElement, seekToTime) && resultElement == animation)
  428. resultElement = nullptr;
  429. SMILTime nextFireTime = animation->nextProgressTime();
  430. if (nextFireTime.isFinite())
  431. earliestFireTime = std::min(nextFireTime, earliestFireTime);
  432. }
  433. if (resultElement)
  434. animationsToApply.append(resultElement);
  435. }
  436. m_scheduledAnimations.removeAll(invalidKeys);
  437. std::sort(animationsToApply.begin(), animationsToApply.end(), PriorityCompare(elapsed));
  438. unsigned animationsToApplySize = animationsToApply.size();
  439. if (!animationsToApplySize) {
  440. #if ENABLE(ASSERT)
  441. m_preventScheduledAnimationsChanges = false;
  442. #endif
  443. return earliestFireTime;
  444. }
  445. // Apply results to target elements.
  446. for (unsigned i = 0; i < animationsToApplySize; ++i)
  447. animationsToApply[i]->applyResultsToTarget();
  448. #if ENABLE(ASSERT)
  449. m_preventScheduledAnimationsChanges = false;
  450. #endif
  451. for (unsigned i = 0; i < animationsToApplySize; ++i) {
  452. if (animationsToApply[i]->inDocument() && animationsToApply[i]->isSVGDiscardElement()) {
  453. RefPtrWillBeRawPtr<SVGSMILElement> animDiscard = animationsToApply[i];
  454. RefPtrWillBeRawPtr<SVGElement> targetElement = animDiscard->targetElement();
  455. if (targetElement && targetElement->inDocument()) {
  456. targetElement->remove(IGNORE_EXCEPTION);
  457. ASSERT(!targetElement->inDocument());
  458. }
  459. if (animDiscard->inDocument()) {
  460. animDiscard->remove(IGNORE_EXCEPTION);
  461. ASSERT(!animDiscard->inDocument());
  462. }
  463. }
  464. }
  465. return earliestFireTime;
  466. }
  467. void SMILTimeContainer::advanceFrameForTesting()
  468. {
  469. setElapsed(elapsed().value() + initialFrameDelay);
  470. }
  471. DEFINE_TRACE(SMILTimeContainer)
  472. {
  473. #if ENABLE(OILPAN)
  474. visitor->trace(m_scheduledAnimations);
  475. #endif
  476. visitor->trace(m_ownerSVGElement);
  477. }
  478. }