summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/extension_idle_api.cc
blob: 949993fa5109f77e028cf994b5ebe4d49bc5b5a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This implementation supposes a single extension thread and synchronized
// method invokation.

#include "chrome/browser/extensions/extension_idle_api.h"

#include "base/stl_util-inl.h"
#include "base/json/json_writer.h"
#include "base/task.h"
#include "base/time.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_message_service.h"
#include "chrome/browser/extensions/extension_idle_api_constants.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_service.h"

namespace keys = extension_idle_api_constants;

namespace {

const int kIdlePollInterval = 15;  // Number of seconds between status checks
                                   // when polling for active.
const int kMinThreshold = 15;  // In seconds.  Set >1 sec for security concerns.
const int kMaxThreshold = 60*60;  // One hours, in seconds.  Not set arbitrarily
                                  // high for security concerns.

struct ExtensionIdlePollingData {
  IdleState state;
  double timestamp;
};

// Static variables shared between instances of polling.
static ExtensionIdlePollingData polling_data;

// Forward declaration of utility methods.
static const wchar_t* IdleStateToDescription(IdleState state);
static StringValue* CreateIdleValue(IdleState idle_state);
static int CheckThresholdBounds(int timeout);
static IdleState CalculateIdleStateAndUpdateTimestamp(int threshold);
static void CreateNewPollTask(Profile* profile);
static IdleState ThrottledCalculateIdleState(int threshold, Profile* profile);

// Internal object which watches for changes in the system idle state.
class ExtensionIdlePollingTask : public Task {
 public:
  explicit ExtensionIdlePollingTask(Profile* profile) : profile_(profile) {}
  virtual ~ExtensionIdlePollingTask() {}

  // Overridden from Task.
  virtual void Run();

 private:
  Profile* profile_;

  DISALLOW_COPY_AND_ASSIGN(ExtensionIdlePollingTask);
};

const wchar_t* IdleStateToDescription(IdleState state) {
  if (IDLE_STATE_ACTIVE == state)
    return keys::kStateActive;
  if (IDLE_STATE_IDLE == state)
    return keys::kStateIdle;
  return keys::kStateLocked;
};

// Helper function for reporting the idle state.  The lifetime of the object
// returned is controlled by the caller.
StringValue* CreateIdleValue(IdleState idle_state) {
  StringValue* result = new StringValue(IdleStateToDescription(idle_state));
  return result;
}

int CheckThresholdBounds(int timeout) {
  if (timeout < kMinThreshold) return kMinThreshold;
  if (timeout > kMaxThreshold) return kMaxThreshold;
  return timeout;
}

IdleState CalculateIdleStateAndUpdateTimestamp(int threshold) {
  polling_data.timestamp = base::Time::Now().ToDoubleT();
  return CalculateIdleState(threshold);
}

void CreateNewPollTask(Profile* profile) {
  MessageLoop::current()->PostDelayedTask(
      FROM_HERE,
      new ExtensionIdlePollingTask(profile),
      kIdlePollInterval * 1000);
}

IdleState ThrottledCalculateIdleState(int threshold, Profile* profile) {
  // If we are not active we should be polling.
  if (IDLE_STATE_ACTIVE != polling_data.state)
    return polling_data.state;

  // Only allow one check per threshold.
  double time_now = base::Time::Now().ToDoubleT();
  double delta = time_now - polling_data.timestamp;
  if (delta < threshold)
    return polling_data.state;

  // Update the new state with a poll.  Note this updates time of last check.
  polling_data.state = CalculateIdleStateAndUpdateTimestamp(threshold);

  if (IDLE_STATE_ACTIVE != polling_data.state)
    CreateNewPollTask(profile);

  return polling_data.state;
}

void ExtensionIdlePollingTask::Run() {
  IdleState state = CalculateIdleStateAndUpdateTimestamp(
      kIdlePollInterval);
  if (state != polling_data.state) {
    polling_data.state = state;

    // Inform of change if the current state is IDLE_STATE_ACTIVE.
    if (IDLE_STATE_ACTIVE == polling_data.state)
      ExtensionIdleEventRouter::OnIdleStateChange(profile_, state);
  }

  // Create a secondary polling task until an active state is reached.
  if (IDLE_STATE_ACTIVE != polling_data.state)
    CreateNewPollTask(profile_);
}

};  // namespace

bool ExtensionIdleQueryStateFunction::RunImpl() {
  int threshold;
  EXTENSION_FUNCTION_VALIDATE(args_->GetAsInteger(&threshold));
  threshold = CheckThresholdBounds(threshold);
  IdleState state = ThrottledCalculateIdleState(threshold, profile());
  result_.reset(CreateIdleValue(state));
  return true;
}

void ExtensionIdleEventRouter::OnIdleStateChange(Profile* profile,
                                                 IdleState state) {
  // Prepare the single argument of the current state.
  ListValue args;
  args.Append(CreateIdleValue(state));
  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);

  profile->GetExtensionMessageService()->DispatchEventToRenderers(
      keys::kOnStateChanged,
      json_args,
      profile->IsOffTheRecord(),
      GURL());
}