CTTE Timer and DeferrableOneShotTimer
[WebKit-https.git] / Source / WebCore / page / PageThrottler.cpp
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 "MainFrame.h"
32 #include "Page.h"
33 #include "PageActivityAssertionToken.h"
34 #include <wtf/StdLibExtras.h>
35
36 namespace WebCore {
37
38 static const double kThrottleHysteresisSeconds = 2.0;
39
40 PageThrottler::PageThrottler(Page& page)
41     : m_page(page)
42     , m_throttleState(PageNotThrottledState)
43     , m_throttleHysteresisTimer(this, &PageThrottler::throttleHysteresisTimerFired)
44     , m_visuallyNonIdle("Page is not visually idle.")
45 {
46     m_page.chrome().client().incrementActivePageCount();
47 }
48
49 PageThrottler::~PageThrottler()
50 {
51     setIsVisuallyIdle(false);
52
53     for (auto it = m_activityTokens.begin(), end = m_activityTokens.end(); it != end; ++it)
54         (*it)->invalidate();
55
56     if (m_throttleState != PageThrottledState)
57         m_page.chrome().client().decrementActivePageCount();
58 }
59
60 std::unique_ptr<PageActivityAssertionToken> PageThrottler::createActivityToken()
61 {
62     return std::make_unique<PageActivityAssertionToken>(*this);
63 }
64
65 void PageThrottler::throttlePage()
66 {
67     m_throttleState = PageThrottledState;
68
69     m_page.chrome().client().decrementActivePageCount();
70
71     for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
72         if (frame->document())
73             frame->document()->scriptedAnimationControllerSetThrottled(true);
74     }
75
76     m_page.throttleTimers();
77 }
78
79 void PageThrottler::unthrottlePage()
80 {
81     PageThrottleState oldState = m_throttleState;
82     m_throttleState = PageNotThrottledState;
83
84     if (oldState == PageNotThrottledState)
85         return;
86
87     if (oldState == PageThrottledState)
88         m_page.chrome().client().incrementActivePageCount();
89     
90     for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
91         if (frame->document())
92             frame->document()->scriptedAnimationControllerSetThrottled(false);
93     }
94
95     m_page.unthrottleTimers();
96 }
97
98 void PageThrottler::setIsVisuallyIdle(bool isVisuallyIdle)
99 {
100     if (isVisuallyIdle) {
101         m_throttleState = PageWaitingToThrottleState;
102         startThrottleHysteresisTimer();
103         if (m_visuallyNonIdle.isActive())
104             m_visuallyNonIdle.endActivity();
105     } else {
106         unthrottlePage();
107         stopThrottleHysteresisTimer();
108         if (!m_visuallyNonIdle.isActive())
109             m_visuallyNonIdle.beginActivity();
110     }
111 }
112
113 void PageThrottler::stopThrottleHysteresisTimer()
114 {
115     m_throttleHysteresisTimer.stop();
116 }
117
118 void PageThrottler::reportInterestingEvent()
119 {
120     if (m_throttleState == PageNotThrottledState)
121         return;
122     if (m_throttleState == PageThrottledState)
123         unthrottlePage();
124     m_throttleState = PageWaitingToThrottleState;
125     startThrottleHysteresisTimer();
126 }
127
128 void PageThrottler::startThrottleHysteresisTimer()
129 {
130     if (m_throttleHysteresisTimer.isActive())
131         m_throttleHysteresisTimer.stop();
132     if (!m_activityTokens.size())
133         m_throttleHysteresisTimer.startOneShot(kThrottleHysteresisSeconds);
134 }
135
136 void PageThrottler::throttleHysteresisTimerFired(Timer<PageThrottler>&)
137 {
138     ASSERT(!m_activityTokens.size());
139     throttlePage();
140 }
141
142 void PageThrottler::addActivityToken(PageActivityAssertionToken& token)
143 {
144     ASSERT(!m_activityTokens.contains(&token));
145
146     m_activityTokens.add(&token);
147
148     // If we've already got events that block throttling we can return early
149     if (m_activityTokens.size() > 1)
150         return;
151
152     if (m_throttleState == PageNotThrottledState)
153         return;
154
155     if (m_throttleState == PageThrottledState)
156         unthrottlePage();
157
158     m_throttleState = PageWaitingToThrottleState;
159     stopThrottleHysteresisTimer();
160 }
161
162 void PageThrottler::removeActivityToken(PageActivityAssertionToken& token)
163 {
164     ASSERT(m_activityTokens.contains(&token));
165
166     m_activityTokens.remove(&token);
167
168     if (m_activityTokens.size())
169         return;
170
171     if (m_throttleState == PageNotThrottledState)
172         return;
173
174     ASSERT(m_throttleState == PageWaitingToThrottleState);
175     startThrottleHysteresisTimer();
176 }
177
178 }