1/* 2 * Copyright (C) 2013 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. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "PageThrottler.h" 28 29#include "Chrome.h" 30#include "ChromeClient.h" 31#include "Frame.h" 32#include "Page.h" 33#include "PageActivityAssertionToken.h" 34 35namespace WebCore { 36 37static const double kThrottleHysteresisSeconds = 2.0; 38 39PageThrottler::PageThrottler(Page* page) 40 : m_page(page) 41 , m_activeThrottleBlockers(0) 42 , m_throttleState(PageNotThrottledState) 43 , m_throttleHysteresisTimer(this, &PageThrottler::throttleHysteresisTimerFired) 44{ 45 if (ChromeClient* chromeClient = m_page->chrome().client()) 46 chromeClient->incrementActivePageCount(); 47} 48 49PageThrottler::~PageThrottler() 50{ 51 for (HashSet<PageActivityAssertionToken*>::iterator i = m_activityTokens.begin(); i != m_activityTokens.end(); ++i) 52 (*i)->invalidate(); 53 54 if (m_throttleState != PageThrottledState && m_page) { 55 if (ChromeClient* chromeClient = m_page->chrome().client()) 56 chromeClient->decrementActivePageCount(); 57 } 58} 59 60void PageThrottler::clearPage() 61{ 62 setThrottled(false); 63 m_page = 0; 64} 65 66void PageThrottler::throttlePage() 67{ 68 m_throttleState = PageThrottledState; 69 70 if (!m_page) 71 return; 72 73 if (ChromeClient* chromeClient = m_page->chrome().client()) 74 chromeClient->decrementActivePageCount(); 75 76 for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { 77 if (frame->document()) 78 frame->document()->scriptedAnimationControllerSetThrottled(true); 79 } 80 81 m_page->throttleTimers(); 82} 83 84void PageThrottler::unthrottlePage() 85{ 86 PageThrottleState oldState = m_throttleState; 87 m_throttleState = PageNotThrottledState; 88 89 if (!m_page || oldState == PageNotThrottledState) 90 return; 91 92 if (oldState == PageThrottledState) { 93 if (ChromeClient* chromeClient = m_page->chrome().client()) 94 chromeClient->incrementActivePageCount(); 95 } 96 97 for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { 98 if (frame->document()) 99 frame->document()->scriptedAnimationControllerSetThrottled(false); 100 } 101 102 m_page->unthrottleTimers(); 103} 104 105void PageThrottler::setThrottled(bool isThrottled) 106{ 107 if (isThrottled) { 108 m_throttleState = PageWaitingToThrottleState; 109 startThrottleHysteresisTimer(); 110 } else { 111 unthrottlePage(); 112 stopThrottleHysteresisTimer(); 113 } 114} 115 116void PageThrottler::preventThrottling() 117{ 118 // If we've already got events that block throttling we can increment 119 // and return early 120 if (m_activeThrottleBlockers++) 121 return; 122 123 if (m_throttleState == PageNotThrottledState) 124 return; 125 126 if (m_throttleState == PageThrottledState) 127 unthrottlePage(); 128 129 m_throttleState = PageWaitingToThrottleState; 130 stopThrottleHysteresisTimer(); 131} 132 133void PageThrottler::allowThrottling() 134{ 135 ASSERT(m_activeThrottleBlockers > 0); 136 m_activeThrottleBlockers--; 137 if (m_activeThrottleBlockers) 138 return; 139 140 if (m_throttleState == PageNotThrottledState) 141 return; 142 143 ASSERT(m_throttleState == PageWaitingToThrottleState); 144 startThrottleHysteresisTimer(); 145} 146 147void PageThrottler::stopThrottleHysteresisTimer() 148{ 149 m_throttleHysteresisTimer.stop(); 150} 151 152void PageThrottler::reportInterestingEvent() 153{ 154 if (m_throttleState == PageNotThrottledState) 155 return; 156 if (m_throttleState == PageThrottledState) 157 unthrottlePage(); 158 m_throttleState = PageWaitingToThrottleState; 159 startThrottleHysteresisTimer(); 160} 161 162void PageThrottler::startThrottleHysteresisTimer() 163{ 164 if (m_throttleHysteresisTimer.isActive()) 165 m_throttleHysteresisTimer.stop(); 166 if (!m_activeThrottleBlockers) 167 m_throttleHysteresisTimer.startOneShot(kThrottleHysteresisSeconds); 168} 169 170void PageThrottler::throttleHysteresisTimerFired(Timer<PageThrottler>*) 171{ 172 ASSERT(!m_activeThrottleBlockers); 173 throttlePage(); 174} 175 176void PageThrottler::addActivityToken(PageActivityAssertionToken* token) 177{ 178 if (!token || m_activityTokens.contains(token)) 179 return; 180 181 m_activityTokens.add(token); 182 preventThrottling(); 183} 184 185void PageThrottler::removeActivityToken(PageActivityAssertionToken* token) 186{ 187 if (!token || !m_activityTokens.contains(token)) 188 return; 189 190 m_activityTokens.remove(token); 191 allowThrottling(); 192} 193 194} 195