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