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:
ExtensionIdlePollingTask(Profile * profile)54 explicit ExtensionIdlePollingTask(Profile* profile) : profile_(profile) {}
~ExtensionIdlePollingTask()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
IdleStateToDescription(IdleState state)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.
CreateIdleValue(IdleState idle_state)76 StringValue* CreateIdleValue(IdleState idle_state) {
77 StringValue* result = new StringValue(IdleStateToDescription(idle_state));
78 return result;
79 }
80
CheckThresholdBounds(int timeout)81 int CheckThresholdBounds(int timeout) {
82 if (timeout < kMinThreshold) return kMinThreshold;
83 if (timeout > kMaxThreshold) return kMaxThreshold;
84 return timeout;
85 }
86
CalculateIdleStateAndUpdateTimestamp(int threshold)87 IdleState CalculateIdleStateAndUpdateTimestamp(int threshold) {
88 polling_data.timestamp = base::Time::Now().ToDoubleT();
89 return CalculateIdleState(threshold);
90 }
91
CreateNewPollTask(Profile * profile)92 void CreateNewPollTask(Profile* profile) {
93 MessageLoop::current()->PostDelayedTask(
94 FROM_HERE,
95 new ExtensionIdlePollingTask(profile),
96 kIdlePollInterval * 1000);
97 }
98
ThrottledCalculateIdleState(int threshold,Profile * profile)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
Run()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
RunImpl()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
OnIdleStateChange(Profile * profile,IdleState state)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