1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/idle/idle_manager.h"
9 #include "base/stl_util.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/api/idle/idle_api_constants.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/extensions/api/idle.h"
14 #include "chrome/common/extensions/extension_constants.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_source.h"
17 #include "extensions/browser/event_router.h"
18 #include "extensions/common/extension.h"
20 namespace keys
= extensions::idle_api_constants
;
21 namespace idle
= extensions::api::idle
;
23 namespace extensions
{
27 const int kDefaultIdleThreshold
= 60;
28 const int kPollInterval
= 1;
30 class DefaultEventDelegate
: public IdleManager::EventDelegate
{
32 explicit DefaultEventDelegate(Profile
* profile
);
33 virtual ~DefaultEventDelegate();
35 virtual void OnStateChanged(const std::string
& extension_id
,
36 IdleState new_state
) OVERRIDE
;
37 virtual void RegisterObserver(EventRouter::Observer
* observer
) OVERRIDE
;
38 virtual void UnregisterObserver(EventRouter::Observer
* observer
) OVERRIDE
;
44 DefaultEventDelegate::DefaultEventDelegate(Profile
* profile
)
48 DefaultEventDelegate::~DefaultEventDelegate() {
51 void DefaultEventDelegate::OnStateChanged(const std::string
& extension_id
,
52 IdleState new_state
) {
53 scoped_ptr
<base::ListValue
> args(new base::ListValue());
54 args
->Append(IdleManager::CreateIdleValue(new_state
));
55 scoped_ptr
<Event
> event(new Event(idle::OnStateChanged::kEventName
,
57 event
->restrict_to_browser_context
= profile_
;
58 EventRouter::Get(profile_
)
59 ->DispatchEventToExtension(extension_id
, event
.Pass());
62 void DefaultEventDelegate::RegisterObserver(
63 EventRouter::Observer
* observer
) {
64 EventRouter::Get(profile_
)
65 ->RegisterObserver(observer
, idle::OnStateChanged::kEventName
);
68 void DefaultEventDelegate::UnregisterObserver(EventRouter::Observer
* observer
) {
69 EventRouter::Get(profile_
)->UnregisterObserver(observer
);
72 class DefaultIdleProvider
: public IdleManager::IdleTimeProvider
{
74 DefaultIdleProvider();
75 virtual ~DefaultIdleProvider();
77 virtual void CalculateIdleState(int idle_threshold
,
78 IdleCallback notify
) OVERRIDE
;
79 virtual void CalculateIdleTime(IdleTimeCallback notify
) OVERRIDE
;
80 virtual bool CheckIdleStateIsLocked() OVERRIDE
;
83 DefaultIdleProvider::DefaultIdleProvider() {
86 DefaultIdleProvider::~DefaultIdleProvider() {
89 void DefaultIdleProvider::CalculateIdleState(int idle_threshold
,
90 IdleCallback notify
) {
91 ::CalculateIdleState(idle_threshold
, notify
);
94 void DefaultIdleProvider::CalculateIdleTime(IdleTimeCallback notify
) {
95 ::CalculateIdleTime(notify
);
98 bool DefaultIdleProvider::CheckIdleStateIsLocked() {
99 return ::CheckIdleStateIsLocked();
102 IdleState
IdleTimeToIdleState(bool locked
, int idle_time
, int idle_threshold
) {
106 state
= IDLE_STATE_LOCKED
;
107 } else if (idle_time
>= idle_threshold
) {
108 state
= IDLE_STATE_IDLE
;
110 state
= IDLE_STATE_ACTIVE
;
117 IdleMonitor::IdleMonitor(IdleState initial_state
)
118 : last_state(initial_state
),
120 threshold(kDefaultIdleThreshold
) {
123 IdleManager::IdleManager(Profile
* profile
)
125 last_state_(IDLE_STATE_ACTIVE
),
127 idle_time_provider_(new DefaultIdleProvider()),
128 event_delegate_(new DefaultEventDelegate(profile
)) {
131 IdleManager::~IdleManager() {
134 void IdleManager::Init() {
135 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
,
136 content::Source
<Profile
>(profile_
->GetOriginalProfile()));
137 event_delegate_
->RegisterObserver(this);
140 void IdleManager::Shutdown() {
141 DCHECK(thread_checker_
.CalledOnValidThread());
142 event_delegate_
->UnregisterObserver(this);
145 void IdleManager::Observe(int type
,
146 const content::NotificationSource
& source
,
147 const content::NotificationDetails
& details
) {
148 DCHECK(thread_checker_
.CalledOnValidThread());
150 if (type
== chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
) {
151 const Extension
* extension
=
152 content::Details
<extensions::UnloadedExtensionInfo
>(details
)->extension
;
153 monitors_
.erase(extension
->id());
159 void IdleManager::OnListenerAdded(const EventListenerInfo
& details
) {
160 DCHECK(thread_checker_
.CalledOnValidThread());
162 ++GetMonitor(details
.extension_id
)->listeners
;
166 void IdleManager::OnListenerRemoved(const EventListenerInfo
& details
) {
167 DCHECK(thread_checker_
.CalledOnValidThread());
169 // During unload the monitor could already have been deleted. No need to do
170 // anything in that case.
171 MonitorMap::iterator it
= monitors_
.find(details
.extension_id
);
172 if (it
!= monitors_
.end()) {
173 DCHECK_GT(it
->second
.listeners
, 0);
174 // Note: Deliberately leave the listener count as 0 rather than erase()ing
175 // this record so that the threshold doesn't get reset when all listeners
177 --it
->second
.listeners
;
181 void IdleManager::QueryState(int threshold
, QueryStateCallback notify
) {
182 DCHECK(thread_checker_
.CalledOnValidThread());
183 idle_time_provider_
->CalculateIdleState(threshold
, notify
);
186 void IdleManager::SetThreshold(const std::string
& extension_id
,
188 DCHECK(thread_checker_
.CalledOnValidThread());
189 GetMonitor(extension_id
)->threshold
= threshold
;
193 base::StringValue
* IdleManager::CreateIdleValue(IdleState idle_state
) {
194 const char* description
;
196 if (idle_state
== IDLE_STATE_ACTIVE
) {
197 description
= keys::kStateActive
;
198 } else if (idle_state
== IDLE_STATE_IDLE
) {
199 description
= keys::kStateIdle
;
201 description
= keys::kStateLocked
;
204 return new base::StringValue(description
);
207 void IdleManager::SetEventDelegateForTest(
208 scoped_ptr
<EventDelegate
> event_delegate
) {
209 DCHECK(thread_checker_
.CalledOnValidThread());
210 event_delegate_
= event_delegate
.Pass();
213 void IdleManager::SetIdleTimeProviderForTest(
214 scoped_ptr
<IdleTimeProvider
> idle_time_provider
) {
215 DCHECK(thread_checker_
.CalledOnValidThread());
216 idle_time_provider_
= idle_time_provider
.Pass();
219 IdleMonitor
* IdleManager::GetMonitor(const std::string
& extension_id
) {
220 DCHECK(thread_checker_
.CalledOnValidThread());
221 MonitorMap::iterator it
= monitors_
.find(extension_id
);
223 if (it
== monitors_
.end()) {
224 it
= monitors_
.insert(std::make_pair(extension_id
,
225 IdleMonitor(last_state_
))).first
;
230 void IdleManager::StartPolling() {
231 DCHECK(thread_checker_
.CalledOnValidThread());
232 if (!poll_timer_
.IsRunning()) {
233 poll_timer_
.Start(FROM_HERE
,
234 base::TimeDelta::FromSeconds(kPollInterval
),
236 &IdleManager::UpdateIdleState
);
240 void IdleManager::StopPolling() {
241 DCHECK(thread_checker_
.CalledOnValidThread());
245 void IdleManager::UpdateIdleState() {
246 DCHECK(thread_checker_
.CalledOnValidThread());
247 idle_time_provider_
->CalculateIdleTime(
249 &IdleManager::UpdateIdleStateCallback
,
250 weak_factory_
.GetWeakPtr()));
253 void IdleManager::UpdateIdleStateCallback(int idle_time
) {
254 DCHECK(thread_checker_
.CalledOnValidThread());
255 bool locked
= idle_time_provider_
->CheckIdleStateIsLocked();
256 int listener_count
= 0;
258 // Remember this state for initializing new event listeners.
259 last_state_
= IdleTimeToIdleState(locked
,
261 kDefaultIdleThreshold
);
263 for (MonitorMap::iterator it
= monitors_
.begin();
264 it
!= monitors_
.end(); ++it
) {
265 IdleMonitor
& monitor
= it
->second
;
266 IdleState new_state
=
267 IdleTimeToIdleState(locked
, idle_time
, monitor
.threshold
);
268 // TODO(kalman): Use EventRouter::HasListeners for these sorts of checks.
269 if (monitor
.listeners
> 0 && monitor
.last_state
!= new_state
)
270 event_delegate_
->OnStateChanged(it
->first
, new_state
);
271 monitor
.last_state
= new_state
;
272 listener_count
+= monitor
.listeners
;
275 if (listener_count
== 0)
279 } // namespace extensions