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 25 namespace keys = extension_idle_api_constants; 26 27 namespace { 28 29 const int kIdlePollInterval = 15; // Number of seconds between status checks 30 // when polling for active. 31 const int kMinThreshold = 15; // In seconds. Set >1 sec for security concerns. 32 const int kMaxThreshold = 60*60; // One hours, in seconds. Not set arbitrarily 33 // high for security concerns. 34 35 struct ExtensionIdlePollingData { 36 IdleState state; 37 double timestamp; 38 }; 39 40 // Static variables shared between instances of polling. 41 static ExtensionIdlePollingData polling_data; 42 43 // Forward declaration of utility methods. 44 static const char* IdleStateToDescription(IdleState state); 45 static StringValue* CreateIdleValue(IdleState idle_state); 46 static int CheckThresholdBounds(int timeout); 47 static IdleState CalculateIdleStateAndUpdateTimestamp(int threshold); 48 static void CreateNewPollTask(Profile* profile); 49 static IdleState ThrottledCalculateIdleState(int threshold, Profile* profile); 50 51 // Internal object which watches for changes in the system idle state. 52 class 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 66 const 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. 76 StringValue* CreateIdleValue(IdleState idle_state) { 77 StringValue* result = new StringValue(IdleStateToDescription(idle_state)); 78 return result; 79 } 80 81 int CheckThresholdBounds(int timeout) { 82 if (timeout < kMinThreshold) return kMinThreshold; 83 if (timeout > kMaxThreshold) return kMaxThreshold; 84 return timeout; 85 } 86 87 IdleState CalculateIdleStateAndUpdateTimestamp(int threshold) { 88 polling_data.timestamp = base::Time::Now().ToDoubleT(); 89 return CalculateIdleState(threshold); 90 } 91 92 void CreateNewPollTask(Profile* profile) { 93 MessageLoop::current()->PostDelayedTask( 94 FROM_HERE, 95 new ExtensionIdlePollingTask(profile), 96 kIdlePollInterval * 1000); 97 } 98 99 IdleState 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 119 void 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 137 bool 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 146 void 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