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