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 "extensions/browser/api/idle/idle_manager.h"
9 #include "base/stl_util.h"
10 #include "content/public/browser/browser_context.h"
11 #include "extensions/browser/api/idle/idle_api_constants.h"
12 #include "extensions/browser/event_router.h"
13 #include "extensions/browser/extension_registry.h"
14 #include "extensions/common/api/idle.h"
15 #include "extensions/common/extension.h"
17 namespace keys
= extensions::idle_api_constants
;
18 namespace idle
= extensions::api::idle
;
20 namespace extensions
{
24 const int kDefaultIdleThreshold
= 60;
25 const int kPollInterval
= 1;
27 class DefaultEventDelegate
: public IdleManager::EventDelegate
{
29 explicit DefaultEventDelegate(content::BrowserContext
* context
);
30 ~DefaultEventDelegate() override
;
32 void OnStateChanged(const std::string
& extension_id
,
33 ui::IdleState new_state
) override
;
34 void RegisterObserver(EventRouter::Observer
* observer
) override
;
35 void UnregisterObserver(EventRouter::Observer
* observer
) override
;
38 content::BrowserContext
* const context_
;
41 DefaultEventDelegate::DefaultEventDelegate(content::BrowserContext
* context
)
45 DefaultEventDelegate::~DefaultEventDelegate() {
48 void DefaultEventDelegate::OnStateChanged(const std::string
& extension_id
,
49 ui::IdleState new_state
) {
50 scoped_ptr
<base::ListValue
> args(new base::ListValue());
51 args
->Append(IdleManager::CreateIdleValue(new_state
));
52 scoped_ptr
<Event
> event(new Event(events::IDLE_ON_STATE_CHANGED
,
53 idle::OnStateChanged::kEventName
,
55 event
->restrict_to_browser_context
= context_
;
56 EventRouter::Get(context_
)
57 ->DispatchEventToExtension(extension_id
, event
.Pass());
60 void DefaultEventDelegate::RegisterObserver(EventRouter::Observer
* observer
) {
61 EventRouter::Get(context_
)
62 ->RegisterObserver(observer
, idle::OnStateChanged::kEventName
);
65 void DefaultEventDelegate::UnregisterObserver(EventRouter::Observer
* observer
) {
66 EventRouter::Get(context_
)->UnregisterObserver(observer
);
69 class DefaultIdleProvider
: public IdleManager::IdleTimeProvider
{
71 DefaultIdleProvider();
72 ~DefaultIdleProvider() override
;
74 void CalculateIdleState(int idle_threshold
, ui::IdleCallback notify
) override
;
75 void CalculateIdleTime(ui::IdleTimeCallback notify
) override
;
76 bool CheckIdleStateIsLocked() override
;
79 DefaultIdleProvider::DefaultIdleProvider() {
82 DefaultIdleProvider::~DefaultIdleProvider() {
85 void DefaultIdleProvider::CalculateIdleState(int idle_threshold
,
86 ui::IdleCallback notify
) {
87 ui::CalculateIdleState(idle_threshold
, notify
);
90 void DefaultIdleProvider::CalculateIdleTime(ui::IdleTimeCallback notify
) {
91 ui::CalculateIdleTime(notify
);
94 bool DefaultIdleProvider::CheckIdleStateIsLocked() {
95 return ui::CheckIdleStateIsLocked();
98 ui::IdleState
IdleTimeToIdleState(bool locked
,
100 int idle_threshold
) {
104 state
= ui::IDLE_STATE_LOCKED
;
105 } else if (idle_time
>= idle_threshold
) {
106 state
= ui::IDLE_STATE_IDLE
;
108 state
= ui::IDLE_STATE_ACTIVE
;
115 IdleMonitor::IdleMonitor(ui::IdleState initial_state
)
116 : last_state(initial_state
),
118 threshold(kDefaultIdleThreshold
) {
121 IdleManager::IdleManager(content::BrowserContext
* context
)
123 last_state_(ui::IDLE_STATE_ACTIVE
),
124 idle_time_provider_(new DefaultIdleProvider()),
125 event_delegate_(new DefaultEventDelegate(context
)),
126 extension_registry_observer_(this),
127 weak_factory_(this) {
130 IdleManager::~IdleManager() {
133 void IdleManager::Init() {
134 extension_registry_observer_
.Add(ExtensionRegistry::Get(context_
));
135 event_delegate_
->RegisterObserver(this);
138 void IdleManager::Shutdown() {
139 DCHECK(thread_checker_
.CalledOnValidThread());
140 event_delegate_
->UnregisterObserver(this);
143 void IdleManager::OnExtensionUnloaded(content::BrowserContext
* browser_context
,
144 const Extension
* extension
,
145 UnloadedExtensionInfo::Reason reason
) {
146 DCHECK(thread_checker_
.CalledOnValidThread());
147 monitors_
.erase(extension
->id());
150 void IdleManager::OnListenerAdded(const EventListenerInfo
& details
) {
151 DCHECK(thread_checker_
.CalledOnValidThread());
153 ++GetMonitor(details
.extension_id
)->listeners
;
157 void IdleManager::OnListenerRemoved(const EventListenerInfo
& details
) {
158 DCHECK(thread_checker_
.CalledOnValidThread());
160 // During unload the monitor could already have been deleted. No need to do
161 // anything in that case.
162 MonitorMap::iterator it
= monitors_
.find(details
.extension_id
);
163 if (it
!= monitors_
.end()) {
164 DCHECK_GT(it
->second
.listeners
, 0);
165 // Note: Deliberately leave the listener count as 0 rather than erase()ing
166 // this record so that the threshold doesn't get reset when all listeners
168 --it
->second
.listeners
;
172 void IdleManager::QueryState(int threshold
, QueryStateCallback notify
) {
173 DCHECK(thread_checker_
.CalledOnValidThread());
174 idle_time_provider_
->CalculateIdleState(threshold
, notify
);
177 void IdleManager::SetThreshold(const std::string
& extension_id
, int threshold
) {
178 DCHECK(thread_checker_
.CalledOnValidThread());
179 GetMonitor(extension_id
)->threshold
= threshold
;
183 base::StringValue
* IdleManager::CreateIdleValue(ui::IdleState idle_state
) {
184 const char* description
;
186 if (idle_state
== ui::IDLE_STATE_ACTIVE
) {
187 description
= keys::kStateActive
;
188 } else if (idle_state
== ui::IDLE_STATE_IDLE
) {
189 description
= keys::kStateIdle
;
191 description
= keys::kStateLocked
;
194 return new base::StringValue(description
);
197 void IdleManager::SetEventDelegateForTest(
198 scoped_ptr
<EventDelegate
> event_delegate
) {
199 DCHECK(thread_checker_
.CalledOnValidThread());
200 event_delegate_
= event_delegate
.Pass();
203 void IdleManager::SetIdleTimeProviderForTest(
204 scoped_ptr
<IdleTimeProvider
> idle_time_provider
) {
205 DCHECK(thread_checker_
.CalledOnValidThread());
206 idle_time_provider_
= idle_time_provider
.Pass();
209 IdleMonitor
* IdleManager::GetMonitor(const std::string
& extension_id
) {
210 DCHECK(thread_checker_
.CalledOnValidThread());
211 MonitorMap::iterator it
= monitors_
.find(extension_id
);
213 if (it
== monitors_
.end()) {
214 it
= monitors_
.insert(std::make_pair(extension_id
,
215 IdleMonitor(last_state_
))).first
;
220 void IdleManager::StartPolling() {
221 DCHECK(thread_checker_
.CalledOnValidThread());
222 if (!poll_timer_
.IsRunning()) {
223 poll_timer_
.Start(FROM_HERE
, base::TimeDelta::FromSeconds(kPollInterval
),
224 this, &IdleManager::UpdateIdleState
);
228 void IdleManager::StopPolling() {
229 DCHECK(thread_checker_
.CalledOnValidThread());
233 void IdleManager::UpdateIdleState() {
234 DCHECK(thread_checker_
.CalledOnValidThread());
235 idle_time_provider_
->CalculateIdleTime(base::Bind(
236 &IdleManager::UpdateIdleStateCallback
, weak_factory_
.GetWeakPtr()));
239 void IdleManager::UpdateIdleStateCallback(int idle_time
) {
240 DCHECK(thread_checker_
.CalledOnValidThread());
241 bool locked
= idle_time_provider_
->CheckIdleStateIsLocked();
242 int listener_count
= 0;
244 // Remember this state for initializing new event listeners.
245 last_state_
= IdleTimeToIdleState(locked
, idle_time
, kDefaultIdleThreshold
);
247 for (MonitorMap::iterator it
= monitors_
.begin(); it
!= monitors_
.end();
249 IdleMonitor
& monitor
= it
->second
;
250 ui::IdleState new_state
=
251 IdleTimeToIdleState(locked
, idle_time
, monitor
.threshold
);
252 // TODO(kalman): Use EventRouter::HasListeners for these sorts of checks.
253 if (monitor
.listeners
> 0 && monitor
.last_state
!= new_state
)
254 event_delegate_
->OnStateChanged(it
->first
, new_state
);
255 monitor
.last_state
= new_state
;
256 listener_count
+= monitor
.listeners
;
259 if (listener_count
== 0)
263 } // namespace extensions