// 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/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/extension.h" #include "content/browser/renderer_host/render_view_host.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()); }