1 /* 2 * Copyright (C) 2013 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/animation/AnimationTimeline.h" 33 34 #include "core/animation/ActiveAnimations.h" 35 #include "core/animation/AnimationClock.h" 36 #include "core/dom/Document.h" 37 #include "core/frame/FrameView.h" 38 #include "core/loader/DocumentLoader.h" 39 #include "core/page/Page.h" 40 #include "platform/TraceEvent.h" 41 42 namespace blink { 43 44 namespace { 45 46 bool compareAnimationPlayers(const RefPtrWillBeMember<blink::AnimationPlayer>& left, const RefPtrWillBeMember<blink::AnimationPlayer>& right) 47 { 48 return AnimationPlayer::hasLowerPriority(left.get(), right.get()); 49 } 50 51 } 52 53 // This value represents 1 frame at 30Hz plus a little bit of wiggle room. 54 // TODO: Plumb a nominal framerate through and derive this value from that. 55 const double AnimationTimeline::s_minimumDelay = 0.04; 56 57 58 PassRefPtrWillBeRawPtr<AnimationTimeline> AnimationTimeline::create(Document* document, PassOwnPtrWillBeRawPtr<PlatformTiming> timing) 59 { 60 return adoptRefWillBeNoop(new AnimationTimeline(document, timing)); 61 } 62 63 AnimationTimeline::AnimationTimeline(Document* document, PassOwnPtrWillBeRawPtr<PlatformTiming> timing) 64 : m_document(document) 65 , m_zeroTime(0) 66 { 67 if (!timing) 68 m_timing = adoptPtrWillBeNoop(new AnimationTimelineTiming(this)); 69 else 70 m_timing = timing; 71 72 ASSERT(document); 73 } 74 75 AnimationTimeline::~AnimationTimeline() 76 { 77 #if !ENABLE(OILPAN) 78 for (WillBeHeapHashSet<RawPtrWillBeWeakMember<AnimationPlayer> >::iterator it = m_players.begin(); it != m_players.end(); ++it) 79 (*it)->timelineDestroyed(); 80 #endif 81 } 82 83 AnimationPlayer* AnimationTimeline::createAnimationPlayer(AnimationNode* child) 84 { 85 RefPtrWillBeRawPtr<AnimationPlayer> player = AnimationPlayer::create(m_document->contextDocument().get(), *this, child); 86 AnimationPlayer* result = player.get(); 87 m_players.add(result); 88 setOutdatedAnimationPlayer(result); 89 return result; 90 } 91 92 AnimationPlayer* AnimationTimeline::play(AnimationNode* child) 93 { 94 if (!m_document) 95 return 0; 96 AnimationPlayer* player = createAnimationPlayer(child); 97 return player; 98 } 99 100 WillBeHeapVector<RefPtrWillBeMember<AnimationPlayer> > AnimationTimeline::getAnimationPlayers() 101 { 102 WillBeHeapVector<RefPtrWillBeMember<AnimationPlayer> > animationPlayers; 103 for (WillBeHeapHashSet<RawPtrWillBeWeakMember<AnimationPlayer> >::iterator it = m_players.begin(); it != m_players.end(); ++it) { 104 if ((*it)->source() && (*it)->source()->isCurrent()) { 105 animationPlayers.append(*it); 106 } 107 } 108 std::sort(animationPlayers.begin(), animationPlayers.end(), compareAnimationPlayers); 109 return animationPlayers; 110 } 111 112 void AnimationTimeline::wake() 113 { 114 m_timing->serviceOnNextFrame(); 115 } 116 117 void AnimationTimeline::serviceAnimations(TimingUpdateReason reason) 118 { 119 TRACE_EVENT0("blink", "AnimationTimeline::serviceAnimations"); 120 121 m_timing->cancelWake(); 122 123 double timeToNextEffect = std::numeric_limits<double>::infinity(); 124 125 WillBeHeapVector<RawPtrWillBeMember<AnimationPlayer> > players; 126 players.reserveInitialCapacity(m_playersNeedingUpdate.size()); 127 for (WillBeHeapHashSet<RefPtrWillBeMember<AnimationPlayer> >::iterator it = m_playersNeedingUpdate.begin(); it != m_playersNeedingUpdate.end(); ++it) 128 players.append(it->get()); 129 130 std::sort(players.begin(), players.end(), AnimationPlayer::hasLowerPriority); 131 132 for (size_t i = 0; i < players.size(); ++i) { 133 AnimationPlayer* player = players[i]; 134 if (player->update(reason)) 135 timeToNextEffect = std::min(timeToNextEffect, player->timeToEffectChange()); 136 else 137 m_playersNeedingUpdate.remove(player); 138 } 139 140 if (timeToNextEffect < s_minimumDelay) 141 m_timing->serviceOnNextFrame(); 142 else if (timeToNextEffect != std::numeric_limits<double>::infinity()) 143 m_timing->wakeAfter(timeToNextEffect - s_minimumDelay); 144 145 ASSERT(!hasOutdatedAnimationPlayer()); 146 } 147 148 void AnimationTimeline::AnimationTimelineTiming::wakeAfter(double duration) 149 { 150 m_timer.startOneShot(duration, FROM_HERE); 151 } 152 153 void AnimationTimeline::AnimationTimelineTiming::cancelWake() 154 { 155 m_timer.stop(); 156 } 157 158 void AnimationTimeline::AnimationTimelineTiming::serviceOnNextFrame() 159 { 160 if (m_timeline->m_document && m_timeline->m_document->view()) 161 m_timeline->m_document->view()->scheduleAnimation(); 162 } 163 164 void AnimationTimeline::AnimationTimelineTiming::trace(Visitor* visitor) 165 { 166 visitor->trace(m_timeline); 167 AnimationTimeline::PlatformTiming::trace(visitor); 168 } 169 170 double AnimationTimeline::zeroTime() 171 { 172 if (!m_zeroTime && m_document && m_document->loader()) { 173 m_zeroTime = m_document->loader()->timing()->referenceMonotonicTime(); 174 } 175 return m_zeroTime; 176 } 177 178 double AnimationTimeline::currentTime(bool& isNull) 179 { 180 return currentTimeInternal(isNull) * 1000; 181 } 182 183 double AnimationTimeline::currentTimeInternal(bool& isNull) 184 { 185 if (!m_document) { 186 isNull = true; 187 return std::numeric_limits<double>::quiet_NaN(); 188 } 189 double result = m_document->animationClock().currentTime() - zeroTime(); 190 isNull = std::isnan(result); 191 return result; 192 } 193 194 double AnimationTimeline::currentTime() 195 { 196 return currentTimeInternal() * 1000; 197 } 198 199 double AnimationTimeline::currentTimeInternal() 200 { 201 bool isNull; 202 return currentTimeInternal(isNull); 203 } 204 205 double AnimationTimeline::effectiveTime() 206 { 207 double time = currentTimeInternal(); 208 return std::isnan(time) ? 0 : time; 209 } 210 211 void AnimationTimeline::pauseAnimationsForTesting(double pauseTime) 212 { 213 for (WillBeHeapHashSet<RefPtrWillBeMember<AnimationPlayer> >::iterator it = m_playersNeedingUpdate.begin(); it != m_playersNeedingUpdate.end(); ++it) 214 (*it)->pauseForTesting(pauseTime); 215 serviceAnimations(TimingUpdateOnDemand); 216 } 217 218 bool AnimationTimeline::hasOutdatedAnimationPlayer() const 219 { 220 for (WillBeHeapHashSet<RefPtrWillBeMember<AnimationPlayer> >::iterator it = m_playersNeedingUpdate.begin(); it != m_playersNeedingUpdate.end(); ++it) { 221 if ((*it)->outdated()) 222 return true; 223 } 224 return false; 225 } 226 227 void AnimationTimeline::setOutdatedAnimationPlayer(AnimationPlayer* player) 228 { 229 ASSERT(player->outdated()); 230 m_playersNeedingUpdate.add(player); 231 if (m_document && m_document->page() && !m_document->page()->animator().isServicingAnimations()) 232 m_timing->serviceOnNextFrame(); 233 } 234 235 #if !ENABLE(OILPAN) 236 void AnimationTimeline::detachFromDocument() 237 { 238 // FIXME: AnimationTimeline should keep Document alive. 239 m_document = nullptr; 240 } 241 #endif 242 243 void AnimationTimeline::trace(Visitor* visitor) 244 { 245 #if ENABLE(OILPAN) 246 visitor->trace(m_document); 247 visitor->trace(m_timing); 248 visitor->trace(m_playersNeedingUpdate); 249 visitor->trace(m_players); 250 #endif 251 } 252 253 } // namespace 254