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