Home | History | Annotate | Download | only in idle
      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 
     42 DefaultEventDelegate::DefaultEventDelegate(Profile* profile)
     43     : profile_(profile) {
     44 }
     45 
     46 DefaultEventDelegate::~DefaultEventDelegate() {
     47 }
     48 
     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 
     60 void DefaultEventDelegate::RegisterObserver(
     61     EventRouter::Observer* observer) {
     62   EventRouter::Get(profile_)
     63       ->RegisterObserver(observer, idle::OnStateChanged::kEventName);
     64 }
     65 
     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 
     81 DefaultIdleProvider::DefaultIdleProvider() {
     82 }
     83 
     84 DefaultIdleProvider::~DefaultIdleProvider() {
     85 }
     86 
     87 void DefaultIdleProvider::CalculateIdleState(int idle_threshold,
     88                                              IdleCallback notify) {
     89   ::CalculateIdleState(idle_threshold, notify);
     90 }
     91 
     92 void DefaultIdleProvider::CalculateIdleTime(IdleTimeCallback notify) {
     93   ::CalculateIdleTime(notify);
     94 }
     95 
     96 bool DefaultIdleProvider::CheckIdleStateIsLocked() {
     97   return ::CheckIdleStateIsLocked();
     98 }
     99 
    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 
    115 IdleMonitor::IdleMonitor(IdleState initial_state)
    116     : last_state(initial_state),
    117       listeners(0),
    118       threshold(kDefaultIdleThreshold) {
    119 }
    120 
    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 
    130 IdleManager::~IdleManager() {
    131 }
    132 
    133 void IdleManager::Init() {
    134   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
    135   event_delegate_->RegisterObserver(this);
    136 }
    137 
    138 void IdleManager::Shutdown() {
    139   DCHECK(thread_checker_.CalledOnValidThread());
    140   event_delegate_->UnregisterObserver(this);
    141 }
    142 
    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 
    150 void IdleManager::OnListenerAdded(const EventListenerInfo& details) {
    151   DCHECK(thread_checker_.CalledOnValidThread());
    152 
    153   ++GetMonitor(details.extension_id)->listeners;
    154   StartPolling();
    155 }
    156 
    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 
    172 void IdleManager::QueryState(int threshold, QueryStateCallback notify) {
    173   DCHECK(thread_checker_.CalledOnValidThread());
    174   idle_time_provider_->CalculateIdleState(threshold, notify);
    175 }
    176 
    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
    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 
    198 void IdleManager::SetEventDelegateForTest(
    199     scoped_ptr<EventDelegate> event_delegate) {
    200   DCHECK(thread_checker_.CalledOnValidThread());
    201   event_delegate_ = event_delegate.Pass();
    202 }
    203 
    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 
    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 
    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 
    231 void IdleManager::StopPolling() {
    232   DCHECK(thread_checker_.CalledOnValidThread());
    233   poll_timer_.Stop();
    234 }
    235 
    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 
    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