diff options
author | brg@chromium.com <brg@chromium.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-16 00:19:34 +0000 |
---|---|---|
committer | brg@chromium.com <brg@chromium.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-16 00:19:34 +0000 |
commit | f5205419c04244f460891b8ff8e30d3793ffa507 (patch) | |
tree | 4becb4662d36d92050001eb65e624cffeec95c10 | |
parent | 5c7c98d536de7fb818b7f23ae52a92383072aa0d (diff) | |
download | chromium_src-f5205419c04244f460891b8ff8e30d3793ffa507.zip chromium_src-f5205419c04244f460891b8ff8e30d3793ffa507.tar.gz chromium_src-f5205419c04244f460891b8ff8e30d3793ffa507.tar.bz2 |
Idle API for the extension system
Tests=ExtensionApiTest.Idle
Bug=one
Review URL: http://codereview.chromium.org/845005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41661 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/extensions/extension_function_dispatcher.cc | 4 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_idle_api.cc | 156 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_idle_api.h | 28 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_idle_api_constants.cc | 18 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_idle_api_constants.h | 24 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_idle_apitest.cc | 15 | ||||
-rw-r--r-- | chrome/browser/idle.h | 18 | ||||
-rw-r--r-- | chrome/browser/idle_linux.cc | 15 | ||||
-rw-r--r-- | chrome/browser/idle_mac.cc | 16 | ||||
-rw-r--r-- | chrome/browser/idle_win.cc | 54 | ||||
-rwxr-xr-x | chrome/chrome_browser.gypi | 7 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rwxr-xr-x | chrome/common/extensions/api/extension_api.json | 43 | ||||
-rw-r--r-- | chrome/renderer/resources/renderer_extension_bindings.js | 1 | ||||
-rw-r--r-- | chrome/test/data/extensions/api_test/idle/manifest.json | 7 | ||||
-rw-r--r-- | chrome/test/data/extensions/api_test/idle/test.html | 1 | ||||
-rw-r--r-- | chrome/test/data/extensions/api_test/idle/test.js | 38 |
17 files changed, 446 insertions, 0 deletions
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 02facbd..6d47016 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -18,6 +18,7 @@ #include "chrome/browser/extensions/extension_dom_ui.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/extension_history_api.h" +#include "chrome/browser/extensions/extension_idle_api.h" #include "chrome/browser/extensions/extension_i18n_api.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extension_metrics_module.h" @@ -157,6 +158,9 @@ void FactoryRegistry::ResetFunctions() { RegisterFunction<GetVisitsHistoryFunction>(); RegisterFunction<SearchHistoryFunction>(); + // Idle + RegisterFunction<ExtensionIdleQueryStateFunction>(); + // Toolstrips. RegisterFunction<ToolstripExpandFunction>(); RegisterFunction<ToolstripCollapseFunction>(); diff --git a/chrome/browser/extensions/extension_idle_api.cc b/chrome/browser/extensions/extension_idle_api.cc new file mode 100644 index 0000000..688e4be --- /dev/null +++ b/chrome/browser/extensions/extension_idle_api.cc @@ -0,0 +1,156 @@ +// 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()); +} diff --git a/chrome/browser/extensions/extension_idle_api.h b/chrome/browser/extensions/extension_idle_api.h new file mode 100644 index 0000000..641628a --- /dev/null +++ b/chrome/browser/extensions/extension_idle_api.h @@ -0,0 +1,28 @@ +// 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_IDLE_API_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_IDLE_API_H_ + +#include "chrome/browser/idle.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/extensions/extension_function.h" + +// Event router class for events related to the idle API. +class ExtensionIdleEventRouter { + public: + static void OnIdleStateChange(Profile* profile, + IdleState idleState); + private: + DISALLOW_COPY_AND_ASSIGN(ExtensionIdleEventRouter); +}; + +// Implementation of the chrome.experimental.idle.queryState API. +class ExtensionIdleQueryStateFunction : public SyncExtensionFunction { + public: + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("experimental.idle.queryState") +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_IDLE_API_H_ diff --git a/chrome/browser/extensions/extension_idle_api_constants.cc b/chrome/browser/extensions/extension_idle_api_constants.cc new file mode 100644 index 0000000..7cabe37 --- /dev/null +++ b/chrome/browser/extensions/extension_idle_api_constants.cc @@ -0,0 +1,18 @@ +// 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. + +#include "chrome/browser/extensions/extension_idle_api_constants.h" + +namespace extension_idle_api_constants { + +const wchar_t kSecondsKey[] = L"seconds"; +const wchar_t kStateKey[] = L"state"; + +const char kOnStateChanged[] = "experimental.idle.onStateChanged"; + +const wchar_t kStateActive[] = L"active"; +const wchar_t kStateIdle[] = L"idle"; +const wchar_t kStateLocked[] = L"locked"; + +} // namespace extension_idle_api_constants diff --git a/chrome/browser/extensions/extension_idle_api_constants.h b/chrome/browser/extensions/extension_idle_api_constants.h new file mode 100644 index 0000000..5bdf696 --- /dev/null +++ b/chrome/browser/extensions/extension_idle_api_constants.h @@ -0,0 +1,24 @@ +// 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_IDLE_API_CONSTANTS_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_IDLE_API_CONSTANTS_H_ + +namespace extension_idle_api_constants { + +// Keys. +extern const wchar_t kSecondsKey[]; +extern const wchar_t kStateKey[]; + +// Events. +extern const char kOnStateChanged[]; + +// States +extern const wchar_t kStateActive[]; +extern const wchar_t kStateIdle[]; +extern const wchar_t kStateLocked[]; + +}; // namespace extension_idle_api_constants + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_IDLE_API_CONSTANTS_H_ diff --git a/chrome/browser/extensions/extension_idle_apitest.cc b/chrome/browser/extensions/extension_idle_apitest.cc new file mode 100644 index 0000000..89d7c3b --- /dev/null +++ b/chrome/browser/extensions/extension_idle_apitest.cc @@ -0,0 +1,15 @@ +// 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. + +#include "base/command_line.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/common/chrome_switches.h" +#include "net/base/mock_host_resolver.h" + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Idle) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + + ASSERT_TRUE(RunExtensionTest("idle")) << message_; +} diff --git a/chrome/browser/idle.h b/chrome/browser/idle.h new file mode 100644 index 0000000..e850dbe --- /dev/null +++ b/chrome/browser/idle.h @@ -0,0 +1,18 @@ +// 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. + +#ifndef CHROME_BROWSER_IDLE_H_ +#define CHROME_BROWSER_IDLE_H_ + +#include "base/task.h" + +enum IdleState { + IDLE_STATE_ACTIVE = 0, + IDLE_STATE_IDLE = 1, // No activity within threshold. + IDLE_STATE_LOCKED = 2 // Only available on supported systems. +}; + +IdleState CalculateIdleState(unsigned int idle_threshold); + +#endif // CHROME_BROWSER_IDLE_H_ diff --git a/chrome/browser/idle_linux.cc b/chrome/browser/idle_linux.cc new file mode 100644 index 0000000..0da7f72 --- /dev/null +++ b/chrome/browser/idle_linux.cc @@ -0,0 +1,15 @@ +// 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. + +#include "chrome/browser/idle.h" + +#include "chrome/browser/sync/engine/idle_query_linux.h" + +IdleState CalculateIdleState(unsigned int idle_threshold) { + browser_sync::IdleQueryLinux idle_query; + unsigned int idle_time = idle_query.IdleTime(); + if (idle_time >= idle_threshold) + return IDLE_STATE_IDLE; + return IDLE_STATE_ACTIVE; +} diff --git a/chrome/browser/idle_mac.cc b/chrome/browser/idle_mac.cc new file mode 100644 index 0000000..383e4c7 --- /dev/null +++ b/chrome/browser/idle_mac.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2009 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. + +#include "chrome/browser/idle.h" + +#include <CoreGraphics/CGEventSource.h> + +IdleState CalculateIdleState(unsigned int idle_threshold) { + unsigned int idle_time = CGEventSourceSecondsSinceLastEventType( + kCGEventSourceStateCombinedSessionState, + kCGAnyInputEventType); + if (idle_time >= idle_threshold) + return IDLE_STATE_IDLE; + return IDLE_STATE_ACTIVE; +} diff --git a/chrome/browser/idle_win.cc b/chrome/browser/idle_win.cc new file mode 100644 index 0000000..0b9ce72 --- /dev/null +++ b/chrome/browser/idle_win.cc @@ -0,0 +1,54 @@ +// 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. + +#include "chrome/browser/idle.h" + +static bool IsScreensaverRunning(); +static bool IsWorkstationLocked(); + +IdleState CalculateIdleState(unsigned int idle_threshold) { + if (IsScreensaverRunning() || IsWorkstationLocked()) + return IDLE_STATE_LOCKED; + + LASTINPUTINFO last_input_info = {0}; + last_input_info.cbSize = sizeof(LASTINPUTINFO); + DWORD current_idle_time = 0; + if (::GetLastInputInfo(&last_input_info)) { + current_idle_time = ::GetTickCount() - last_input_info.dwTime; + // Will go -ve if we have been idle for a long time (2gb seconds). + if (current_idle_time < 0) + current_idle_time = INT_MAX; + // Convert from ms to seconds. + current_idle_time /= 1000; + } + + if (current_idle_time >= idle_threshold) + return IDLE_STATE_IDLE; + return IDLE_STATE_ACTIVE; +} + +bool IsScreensaverRunning() { + DWORD result = 0; + if (::SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &result, 0)) + return result != FALSE; + return false; +} + +bool IsWorkstationLocked() { + bool is_locked = true; + HDESK input_desk = ::OpenInputDesktop(0, 0, GENERIC_READ); + if (input_desk) { + wchar_t name[256] = {0}; + DWORD needed = 0; + if (::GetUserObjectInformation(input_desk, + UOI_NAME, + name, + sizeof(name), + &needed)) { + is_locked = lstrcmpi(name, L"default") != 0; + } + ::CloseDesktop(input_desk); + } + return is_locked; +} diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 6dffe8b..22010e6 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -928,6 +928,10 @@ 'browser/extensions/extension_host.h', 'browser/extensions/extension_host_mac.h', 'browser/extensions/extension_host_mac.mm', + 'browser/extensions/extension_idle_api.cc', + 'browser/extensions/extension_idle_api.h', + 'browser/extensions/extension_idle_api_constants.cc', + 'browser/extensions/extension_idle_api_constants.h', 'browser/extensions/extension_i18n_api.cc', 'browser/extensions/extension_i18n_api.h', 'browser/extensions/extension_install_ui.cc', @@ -1349,6 +1353,9 @@ 'browser/icon_manager_linux.cc', 'browser/icon_manager_mac.mm', 'browser/icon_manager_win.cc', + 'browser/idle_linux.cc', + 'browser/idle_mac.cc', + 'browser/idle_win.cc', 'browser/ime_input.cc', 'browser/ime_input.h', 'browser/important_file_writer.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 10f3fd8..9cb92f7 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1205,6 +1205,7 @@ 'browser/extensions/extension_crash_recovery_browsertest.cc', 'browser/extensions/extension_geolocation_apitest.cc', 'browser/extensions/extension_history_apitest.cc', + 'browser/extensions/extension_idle_apitest.cc', 'browser/extensions/extension_i18n_apitest.cc', 'browser/extensions/extension_incognito_apitest.cc', 'browser/extensions/extension_javascript_url_apitest.cc', diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index c0042c0..a65aac8 100755 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -1825,6 +1825,49 @@ ] }, { + "namespace": "experimental.idle", + "nodoc": "true", + "types": [], + "functions": [ + { + "name": "queryState", + "type": "function", + "description": "Returns the current state of the browser.", + "parameters": [ + { + "name": "threshold", + "type": "integer", + "minimum": 15, + "description": "Threshold used to determine when a machine is in the idle state." + }, + { + "name": "callback", + "type": "function", + "parameters": [ + { + "type": "string", + "enum": ["active", "idle", "locked"] + } + ] + } + ] + } + ], + "events": [ + { + "name": "onStateChanged", + "type": "function", + "description": "Fired when the browser changes to an active state. Currently only reports the transition from idle to active.", + "parameters": [ + { + "type": "string", + "enum": ["active"] + } + ] + } + ] + }, + { "namespace": "toolstrip", "nodoc": true, "types": [], diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js index e34285b..ba6f314 100644 --- a/chrome/renderer/resources/renderer_extension_bindings.js +++ b/chrome/renderer/resources/renderer_extension_bindings.js @@ -247,6 +247,7 @@ var chrome = chrome || {}; "experimental.accessibility", "experimental.bookmarkManager", "experimental.extension", + "experimental.idle", "experimental.history", "experimental.metrics", "experimental.popup", diff --git a/chrome/test/data/extensions/api_test/idle/manifest.json b/chrome/test/data/extensions/api_test/idle/manifest.json new file mode 100644 index 0000000..5284e20 --- /dev/null +++ b/chrome/test/data/extensions/api_test/idle/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "chrome.idle", + "version": "0.1", + "description": "end-to-end browser test for chrome.idle API", + "background_page": "test.html", + "permissions": ["experimental"] +} diff --git a/chrome/test/data/extensions/api_test/idle/test.html b/chrome/test/data/extensions/api_test/idle/test.html new file mode 100644 index 0000000..46f4d74 --- /dev/null +++ b/chrome/test/data/extensions/api_test/idle/test.html @@ -0,0 +1 @@ +<script src="test.js"></script> diff --git a/chrome/test/data/extensions/api_test/idle/test.js b/chrome/test/data/extensions/api_test/idle/test.js new file mode 100644 index 0000000..68dcded --- /dev/null +++ b/chrome/test/data/extensions/api_test/idle/test.js @@ -0,0 +1,38 @@ +// 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. + +// Idle api test for Chrome. +// browser_tests.exe --gtest_filter=ExtensionApiTest.Idle + +// Due to the fact that browser tests are run in many different environments it +// is not simple to be able to set the user input value before testing. For +// these bvts we have chosen the minimal consistent tests. + +var pass = chrome.test.callbackPass; +var fail = chrome.test.callbackFail; +var assertEq = chrome.test.assertEq; + +chrome.test.runTests([ + // Exercise querying the state. Querying the state multiple times within + // the same threshold exercises different code. + function queryState() { + chrome.experimental.idle.queryState(60, pass(function(state) { + var previous_state = state; + chrome.experimental.idle.queryState(120, pass(function(state) { + assertEq(previous_state, state); + chrome.test.succeed(); + })); + })); + }, + + // Exercise the setting of the event listener. + function setCallback() { + chrome.experimental.idle.onStateChanged.addListener(function(state) { + window.console.log('current state: ' + state); + + // The test has succeeded. + chrome.test.succeed(); + }); + } +]); |