1// Copyright (c) 2010 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// This implementation supposes a single extension thread and synchronized 6// method invokation. 7 8#include "chrome/browser/extensions/extension_idle_api.h" 9 10#include <string> 11 12#include "base/json/json_writer.h" 13#include "base/message_loop.h" 14#include "base/stl_util-inl.h" 15#include "base/task.h" 16#include "base/time.h" 17#include "chrome/browser/extensions/extension_event_router.h" 18#include "chrome/browser/extensions/extension_host.h" 19#include "chrome/browser/extensions/extension_idle_api_constants.h" 20#include "chrome/browser/extensions/extension_service.h" 21#include "chrome/browser/profiles/profile.h" 22#include "chrome/common/extensions/extension.h" 23#include "content/browser/renderer_host/render_view_host.h" 24 25namespace keys = extension_idle_api_constants; 26 27namespace { 28 29const int kIdlePollInterval = 15; // Number of seconds between status checks 30 // when polling for active. 31const int kMinThreshold = 15; // In seconds. Set >1 sec for security concerns. 32const int kMaxThreshold = 60*60; // One hours, in seconds. Not set arbitrarily 33 // high for security concerns. 34 35struct ExtensionIdlePollingData { 36 IdleState state; 37 double timestamp; 38}; 39 40// Static variables shared between instances of polling. 41static ExtensionIdlePollingData polling_data; 42 43// Forward declaration of utility methods. 44static const char* IdleStateToDescription(IdleState state); 45static StringValue* CreateIdleValue(IdleState idle_state); 46static int CheckThresholdBounds(int timeout); 47static IdleState CalculateIdleStateAndUpdateTimestamp(int threshold); 48static void CreateNewPollTask(Profile* profile); 49static IdleState ThrottledCalculateIdleState(int threshold, Profile* profile); 50 51// Internal object which watches for changes in the system idle state. 52class ExtensionIdlePollingTask : public Task { 53 public: 54 explicit ExtensionIdlePollingTask(Profile* profile) : profile_(profile) {} 55 virtual ~ExtensionIdlePollingTask() {} 56 57 // Overridden from Task. 58 virtual void Run(); 59 60 private: 61 Profile* profile_; 62 63 DISALLOW_COPY_AND_ASSIGN(ExtensionIdlePollingTask); 64}; 65 66const char* IdleStateToDescription(IdleState state) { 67 if (IDLE_STATE_ACTIVE == state) 68 return keys::kStateActive; 69 if (IDLE_STATE_IDLE == state) 70 return keys::kStateIdle; 71 return keys::kStateLocked; 72}; 73 74// Helper function for reporting the idle state. The lifetime of the object 75// returned is controlled by the caller. 76StringValue* CreateIdleValue(IdleState idle_state) { 77 StringValue* result = new StringValue(IdleStateToDescription(idle_state)); 78 return result; 79} 80 81int CheckThresholdBounds(int timeout) { 82 if (timeout < kMinThreshold) return kMinThreshold; 83 if (timeout > kMaxThreshold) return kMaxThreshold; 84 return timeout; 85} 86 87IdleState CalculateIdleStateAndUpdateTimestamp(int threshold) { 88 polling_data.timestamp = base::Time::Now().ToDoubleT(); 89 return CalculateIdleState(threshold); 90} 91 92void CreateNewPollTask(Profile* profile) { 93 MessageLoop::current()->PostDelayedTask( 94 FROM_HERE, 95 new ExtensionIdlePollingTask(profile), 96 kIdlePollInterval * 1000); 97} 98 99IdleState ThrottledCalculateIdleState(int threshold, Profile* profile) { 100 // If we are not active we should be polling. 101 if (IDLE_STATE_ACTIVE != polling_data.state) 102 return polling_data.state; 103 104 // Only allow one check per threshold. 105 double time_now = base::Time::Now().ToDoubleT(); 106 double delta = time_now - polling_data.timestamp; 107 if (delta < threshold) 108 return polling_data.state; 109 110 // Update the new state with a poll. Note this updates time of last check. 111 polling_data.state = CalculateIdleStateAndUpdateTimestamp(threshold); 112 113 if (IDLE_STATE_ACTIVE != polling_data.state) 114 CreateNewPollTask(profile); 115 116 return polling_data.state; 117} 118 119void ExtensionIdlePollingTask::Run() { 120 IdleState state = CalculateIdleStateAndUpdateTimestamp( 121 kIdlePollInterval); 122 if (state != polling_data.state) { 123 polling_data.state = state; 124 125 // Inform of change if the current state is IDLE_STATE_ACTIVE. 126 if (IDLE_STATE_ACTIVE == polling_data.state) 127 ExtensionIdleEventRouter::OnIdleStateChange(profile_, state); 128 } 129 130 // Create a secondary polling task until an active state is reached. 131 if (IDLE_STATE_ACTIVE != polling_data.state) 132 CreateNewPollTask(profile_); 133} 134 135}; // namespace 136 137bool ExtensionIdleQueryStateFunction::RunImpl() { 138 int threshold; 139 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &threshold)); 140 threshold = CheckThresholdBounds(threshold); 141 IdleState state = ThrottledCalculateIdleState(threshold, profile()); 142 result_.reset(CreateIdleValue(state)); 143 return true; 144} 145 146void ExtensionIdleEventRouter::OnIdleStateChange(Profile* profile, 147 IdleState state) { 148 // Prepare the single argument of the current state. 149 ListValue args; 150 args.Append(CreateIdleValue(state)); 151 std::string json_args; 152 base::JSONWriter::Write(&args, false, &json_args); 153 154 profile->GetExtensionEventRouter()->DispatchEventToRenderers( 155 keys::kOnStateChanged, json_args, profile, GURL()); 156} 157