• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/extensions/api/idle/idle_manager.h"
6 
7 #include <utility>
8 
9 #include "base/stl_util.h"
10 #include "chrome/browser/extensions/api/idle/idle_api_constants.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/common/extensions/api/idle.h"
13 #include "chrome/common/extensions/extension_constants.h"
14 #include "extensions/browser/event_router.h"
15 #include "extensions/browser/extension_registry.h"
16 #include "extensions/common/extension.h"
17 
18 namespace keys = extensions::idle_api_constants;
19 namespace idle = extensions::api::idle;
20 
21 namespace extensions {
22 
23 namespace {
24 
25 const int kDefaultIdleThreshold = 60;
26 const int kPollInterval = 1;
27 
28 class DefaultEventDelegate : public IdleManager::EventDelegate {
29  public:
30   explicit DefaultEventDelegate(Profile* profile);
31   virtual ~DefaultEventDelegate();
32 
33   virtual void OnStateChanged(const std::string& extension_id,
34                               IdleState new_state) OVERRIDE;
35   virtual void RegisterObserver(EventRouter::Observer* observer) OVERRIDE;
36   virtual void UnregisterObserver(EventRouter::Observer* observer) OVERRIDE;
37 
38  private:
39   Profile* profile_;
40 };
41 
DefaultEventDelegate(Profile * profile)42 DefaultEventDelegate::DefaultEventDelegate(Profile* profile)
43     : profile_(profile) {
44 }
45 
~DefaultEventDelegate()46 DefaultEventDelegate::~DefaultEventDelegate() {
47 }
48 
OnStateChanged(const std::string & extension_id,IdleState new_state)49 void DefaultEventDelegate::OnStateChanged(const std::string& extension_id,
50                                           IdleState new_state) {
51   scoped_ptr<base::ListValue> args(new base::ListValue());
52   args->Append(IdleManager::CreateIdleValue(new_state));
53   scoped_ptr<Event> event(new Event(idle::OnStateChanged::kEventName,
54                                     args.Pass()));
55   event->restrict_to_browser_context = profile_;
56   EventRouter::Get(profile_)
57       ->DispatchEventToExtension(extension_id, event.Pass());
58 }
59 
RegisterObserver(EventRouter::Observer * observer)60 void DefaultEventDelegate::RegisterObserver(
61     EventRouter::Observer* observer) {
62   EventRouter::Get(profile_)
63       ->RegisterObserver(observer, idle::OnStateChanged::kEventName);
64 }
65 
UnregisterObserver(EventRouter::Observer * observer)66 void DefaultEventDelegate::UnregisterObserver(EventRouter::Observer* observer) {
67   EventRouter::Get(profile_)->UnregisterObserver(observer);
68 }
69 
70 class DefaultIdleProvider : public IdleManager::IdleTimeProvider {
71  public:
72   DefaultIdleProvider();
73   virtual ~DefaultIdleProvider();
74 
75   virtual void CalculateIdleState(int idle_threshold,
76                                   IdleCallback notify) OVERRIDE;
77   virtual void CalculateIdleTime(IdleTimeCallback notify) OVERRIDE;
78   virtual bool CheckIdleStateIsLocked() OVERRIDE;
79 };
80 
DefaultIdleProvider()81 DefaultIdleProvider::DefaultIdleProvider() {
82 }
83 
~DefaultIdleProvider()84 DefaultIdleProvider::~DefaultIdleProvider() {
85 }
86 
CalculateIdleState(int idle_threshold,IdleCallback notify)87 void DefaultIdleProvider::CalculateIdleState(int idle_threshold,
88                                              IdleCallback notify) {
89   ::CalculateIdleState(idle_threshold, notify);
90 }
91 
CalculateIdleTime(IdleTimeCallback notify)92 void DefaultIdleProvider::CalculateIdleTime(IdleTimeCallback notify) {
93   ::CalculateIdleTime(notify);
94 }
95 
CheckIdleStateIsLocked()96 bool DefaultIdleProvider::CheckIdleStateIsLocked() {
97   return ::CheckIdleStateIsLocked();
98 }
99 
IdleTimeToIdleState(bool locked,int idle_time,int idle_threshold)100 IdleState IdleTimeToIdleState(bool locked, int idle_time, int idle_threshold) {
101   IdleState state;
102 
103   if (locked) {
104     state = IDLE_STATE_LOCKED;
105   } else if (idle_time >= idle_threshold) {
106     state = IDLE_STATE_IDLE;
107   } else {
108     state = IDLE_STATE_ACTIVE;
109   }
110   return state;
111 }
112 
113 }  // namespace
114 
IdleMonitor(IdleState initial_state)115 IdleMonitor::IdleMonitor(IdleState initial_state)
116     : last_state(initial_state),
117       listeners(0),
118       threshold(kDefaultIdleThreshold) {
119 }
120 
IdleManager(Profile * profile)121 IdleManager::IdleManager(Profile* profile)
122     : profile_(profile),
123       last_state_(IDLE_STATE_ACTIVE),
124       weak_factory_(this),
125       idle_time_provider_(new DefaultIdleProvider()),
126       event_delegate_(new DefaultEventDelegate(profile)),
127       extension_registry_observer_(this) {
128 }
129 
~IdleManager()130 IdleManager::~IdleManager() {
131 }
132 
Init()133 void IdleManager::Init() {
134   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
135   event_delegate_->RegisterObserver(this);
136 }
137 
Shutdown()138 void IdleManager::Shutdown() {
139   DCHECK(thread_checker_.CalledOnValidThread());
140   event_delegate_->UnregisterObserver(this);
141 }
142 
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionInfo::Reason reason)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());
148 }
149 
OnListenerAdded(const EventListenerInfo & details)150 void IdleManager::OnListenerAdded(const EventListenerInfo& details) {
151   DCHECK(thread_checker_.CalledOnValidThread());
152 
153   ++GetMonitor(details.extension_id)->listeners;
154   StartPolling();
155 }
156 
OnListenerRemoved(const EventListenerInfo & details)157 void IdleManager::OnListenerRemoved(const EventListenerInfo& details) {
158   DCHECK(thread_checker_.CalledOnValidThread());
159 
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
167     // are removed.
168     --it->second.listeners;
169   }
170 }
171 
QueryState(int threshold,QueryStateCallback notify)172 void IdleManager::QueryState(int threshold, QueryStateCallback notify) {
173   DCHECK(thread_checker_.CalledOnValidThread());
174   idle_time_provider_->CalculateIdleState(threshold, notify);
175 }
176 
SetThreshold(const std::string & extension_id,int threshold)177 void IdleManager::SetThreshold(const std::string& extension_id,
178                                int threshold) {
179   DCHECK(thread_checker_.CalledOnValidThread());
180   GetMonitor(extension_id)->threshold = threshold;
181 }
182 
183 // static
CreateIdleValue(IdleState idle_state)184 base::StringValue* IdleManager::CreateIdleValue(IdleState idle_state) {
185   const char* description;
186 
187   if (idle_state == IDLE_STATE_ACTIVE) {
188     description = keys::kStateActive;
189   } else if (idle_state == IDLE_STATE_IDLE) {
190     description = keys::kStateIdle;
191   } else {
192     description = keys::kStateLocked;
193   }
194 
195   return new base::StringValue(description);
196 }
197 
SetEventDelegateForTest(scoped_ptr<EventDelegate> event_delegate)198 void IdleManager::SetEventDelegateForTest(
199     scoped_ptr<EventDelegate> event_delegate) {
200   DCHECK(thread_checker_.CalledOnValidThread());
201   event_delegate_ = event_delegate.Pass();
202 }
203 
SetIdleTimeProviderForTest(scoped_ptr<IdleTimeProvider> idle_time_provider)204 void IdleManager::SetIdleTimeProviderForTest(
205     scoped_ptr<IdleTimeProvider> idle_time_provider) {
206   DCHECK(thread_checker_.CalledOnValidThread());
207   idle_time_provider_ = idle_time_provider.Pass();
208 }
209 
GetMonitor(const std::string & extension_id)210 IdleMonitor* IdleManager::GetMonitor(const std::string& extension_id) {
211   DCHECK(thread_checker_.CalledOnValidThread());
212   MonitorMap::iterator it = monitors_.find(extension_id);
213 
214   if (it == monitors_.end()) {
215     it = monitors_.insert(std::make_pair(extension_id,
216                                          IdleMonitor(last_state_))).first;
217   }
218   return &it->second;
219 }
220 
StartPolling()221 void IdleManager::StartPolling() {
222   DCHECK(thread_checker_.CalledOnValidThread());
223   if (!poll_timer_.IsRunning()) {
224     poll_timer_.Start(FROM_HERE,
225                       base::TimeDelta::FromSeconds(kPollInterval),
226                       this,
227                       &IdleManager::UpdateIdleState);
228   }
229 }
230 
StopPolling()231 void IdleManager::StopPolling() {
232   DCHECK(thread_checker_.CalledOnValidThread());
233   poll_timer_.Stop();
234 }
235 
UpdateIdleState()236 void IdleManager::UpdateIdleState() {
237   DCHECK(thread_checker_.CalledOnValidThread());
238   idle_time_provider_->CalculateIdleTime(
239       base::Bind(
240           &IdleManager::UpdateIdleStateCallback,
241           weak_factory_.GetWeakPtr()));
242 }
243 
UpdateIdleStateCallback(int idle_time)244 void IdleManager::UpdateIdleStateCallback(int idle_time) {
245   DCHECK(thread_checker_.CalledOnValidThread());
246   bool locked = idle_time_provider_->CheckIdleStateIsLocked();
247   int listener_count = 0;
248 
249   // Remember this state for initializing new event listeners.
250   last_state_ = IdleTimeToIdleState(locked,
251                                     idle_time,
252                                     kDefaultIdleThreshold);
253 
254   for (MonitorMap::iterator it = monitors_.begin();
255        it != monitors_.end(); ++it) {
256     IdleMonitor& monitor = it->second;
257     IdleState new_state =
258         IdleTimeToIdleState(locked, idle_time, monitor.threshold);
259     // TODO(kalman): Use EventRouter::HasListeners for these sorts of checks.
260     if (monitor.listeners > 0 && monitor.last_state != new_state)
261       event_delegate_->OnStateChanged(it->first, new_state);
262     monitor.last_state = new_state;
263     listener_count += monitor.listeners;
264   }
265 
266   if (listener_count == 0)
267     StopPolling();
268 }
269 
270 }  // namespace extensions
271