| // 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 <string> |
| |
| #include "base/json/json_writer.h" |
| #include "base/message_loop.h" |
| #include "base/stl_util-inl.h" |
| #include "base/task.h" |
| #include "base/time.h" |
| #include "chrome/browser/extensions/extension_event_router.h" |
| #include "chrome/browser/extensions/extension_host.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/browser/ui/browser.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 char* 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 char* 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_->GetInteger(0, &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->GetExtensionEventRouter()->DispatchEventToRenderers( |
| keys::kOnStateChanged, json_args, profile, GURL()); |
| } |