diff options
author | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-20 12:18:26 +0000 |
---|---|---|
committer | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-20 12:18:26 +0000 |
commit | ee3f482917d9cdd169faf921dfef6455c33713a7 (patch) | |
tree | 98c3ba25792cb5a179f9af7df93241203b6fb3f1 | |
parent | b4beba41bff7c2f1e9437c0931a577c8d0daf49d (diff) | |
download | chromium_src-ee3f482917d9cdd169faf921dfef6455c33713a7.zip chromium_src-ee3f482917d9cdd169faf921dfef6455c33713a7.tar.gz chromium_src-ee3f482917d9cdd169faf921dfef6455c33713a7.tar.bz2 |
Add chrome.power extension API.
This moves the chrome.experimental.power API to
chrome.power, makes minor changes to its public interface
(adding a "level" parameter to requestKeepAwake() and
removing the callbacks from both methods), adds unit tests
for it, and adds a sample "Keep Awake" extension meant to
replace the existing Chrome OS-specific extension at
http://goo.gl/CrUzi.
BUG=178944
TBR=sky@chromium.org
Review URL: https://chromiumcodereview.appspot.com/12576018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@189253 0039d316-1c4b-4281-b951-d872f2087c98
33 files changed, 704 insertions, 397 deletions
diff --git a/chrome/browser/chromeos/extensions/power/power_api.cc b/chrome/browser/chromeos/extensions/power/power_api.cc deleted file mode 100644 index ee062d9..0000000 --- a/chrome/browser/chromeos/extensions/power/power_api.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2012 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/chromeos/extensions/power/power_api.h" - -#include "chrome/browser/chromeos/extensions/power/power_api_manager.h" - -namespace extensions { - -bool PowerRequestKeepAwakeFunction::RunImpl() { - power::PowerApiManager* power_api_manager = - power::PowerApiManager::GetInstance(); - power_api_manager->AddExtensionLock(extension_id()); - - SetResult(base::Value::CreateBooleanValue(true)); - return true; -} - -bool PowerReleaseKeepAwakeFunction::RunImpl() { - power::PowerApiManager* power_api_manager = - power::PowerApiManager::GetInstance(); - power_api_manager->RemoveExtensionLock(extension_id()); - - SetResult(base::Value::CreateBooleanValue(true)); - return true; -} - -} // namespace extensions diff --git a/chrome/browser/chromeos/extensions/power/power_api_browsertest.cc b/chrome/browser/chromeos/extensions/power/power_api_browsertest.cc deleted file mode 100644 index de72c20..0000000 --- a/chrome/browser/chromeos/extensions/power/power_api_browsertest.cc +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2012 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/memory/scoped_ptr.h" -#include "chrome/browser/chromeos/extensions/power/power_api.h" -#include "chrome/browser/extensions/extension_function_test_utils.h" -#include "chrome/common/extensions/extension_test_util.h" -#include "chrome/common/extensions/manifest.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/ui_test_utils.h" -#include "chromeos/dbus/mock_dbus_thread_manager.h" -#include "chromeos/dbus/mock_power_manager_client.h" -#include "chromeos/dbus/mock_update_engine_client.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::Return; - -namespace extensions { - -class PowerApiTest : public InProcessBrowserTest { - public: - PowerApiTest() {} - virtual ~PowerApiTest() {} - - virtual void SetUp() OVERRIDE { - chromeos::MockDBusThreadManager* mock_dbus_thread_manager = - new chromeos::MockDBusThreadManager; - - EXPECT_CALL(*mock_dbus_thread_manager, GetSystemBus()) - .WillRepeatedly(Return(reinterpret_cast<dbus::Bus*>(NULL))); - EXPECT_CALL(*mock_dbus_thread_manager->mock_update_engine_client(), - GetLastStatus()) - .Times(1) - .WillOnce(Return(chromeos::MockUpdateEngineClient::Status())); - chromeos::DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); - power_client_ = mock_dbus_thread_manager->mock_power_manager_client(); - - InProcessBrowserTest::SetUp(); - } - - virtual void TearDown() OVERRIDE { - chromeos::DBusThreadManager::Shutdown(); - - InProcessBrowserTest::TearDown(); - } - - protected: - static const int kRequestId = 5; // arbitrary - - scoped_refptr<Extension> CreateExtensionWithId(const std::string& id) { - return extension_test_util::CreateExtensionWithID( - extension_test_util::MakeId(id)); - } - - void RunFunctionAndExpectPass(UIThreadExtensionFunction* function, - Extension* extension) { - function->set_extension(extension); - function->set_has_callback(true); - - scoped_ptr<base::Value> result( - extension_function_test_utils::RunFunctionAndReturnSingleResult( - function, "[]", browser())); - - ASSERT_TRUE(result.get() != NULL); - ASSERT_EQ(base::Value::TYPE_BOOLEAN, result->GetType()); - bool boolean_value; - result->GetAsBoolean(&boolean_value); - EXPECT_EQ(boolean_value, true); - - MessageLoop::current()->RunUntilIdle(); - } - - // Adds an expectation that RequestPowerStateOverrides() will be called once - // to create a new override. The callback is stored in - // |request_id_callback_|, which should be invoked with |kRequestId|. - void AddRequestPowerStateOverridesExpectation() { - EXPECT_CALL(*power_client_, RequestPowerStateOverrides(0, _, _, _)) - .WillOnce(testing::SaveArg<3>(&request_id_callback_)); - } - - // Adds an expectation that CancelPowerStateOverrides() will be called once to - // cancel an override previously created via - // AddRequestPowerStateOverridesExpectation(). - void AddCancelPowerStateOverridesExpectation() { - EXPECT_CALL(*power_client_, CancelPowerStateOverrides(kRequestId)).Times(1); - } - - void RequestKeepAwake(scoped_refptr<Extension> extension) { - scoped_refptr<PowerRequestKeepAwakeFunction> function( - new PowerRequestKeepAwakeFunction); - RunFunctionAndExpectPass(function.get(), extension); - } - - void ReleaseKeepAwake(scoped_refptr<Extension> extension) { - scoped_refptr<PowerReleaseKeepAwakeFunction> function( - new PowerReleaseKeepAwakeFunction); - RunFunctionAndExpectPass(function.get(), extension); - } - - chromeos::MockPowerManagerClient* power_client_; - - chromeos::PowerStateRequestIdCallback request_id_callback_; - - private: - DISALLOW_COPY_AND_ASSIGN(PowerApiTest); -}; - -IN_PROC_BROWSER_TEST_F(PowerApiTest, RequestAndRelease) { - scoped_refptr<Extension> extension(CreateExtensionWithId("0")); - AddRequestPowerStateOverridesExpectation(); - RequestKeepAwake(extension); - request_id_callback_.Run(kRequestId); - - AddCancelPowerStateOverridesExpectation(); - ReleaseKeepAwake(extension); -} - -IN_PROC_BROWSER_TEST_F(PowerApiTest, RequestMultipleAndReleaseOne) { - scoped_refptr<Extension> extension1(CreateExtensionWithId("1")); - scoped_refptr<Extension> extension2(CreateExtensionWithId("2")); - - AddRequestPowerStateOverridesExpectation(); - RequestKeepAwake(extension1); - RequestKeepAwake(extension2); - RequestKeepAwake(extension1); - request_id_callback_.Run(kRequestId); - - AddCancelPowerStateOverridesExpectation(); - ReleaseKeepAwake(extension1); -} - -IN_PROC_BROWSER_TEST_F(PowerApiTest, RequestOneAndReleaseMultiple) { - scoped_refptr<Extension> extension(CreateExtensionWithId("3")); - - AddRequestPowerStateOverridesExpectation(); - RequestKeepAwake(extension); - request_id_callback_.Run(kRequestId); - - AddCancelPowerStateOverridesExpectation(); - ReleaseKeepAwake(extension); - ReleaseKeepAwake(extension); - ReleaseKeepAwake(extension); -} - -IN_PROC_BROWSER_TEST_F(PowerApiTest, RequestMultipleAndReleaseAll) { - scoped_refptr<Extension> extension1(CreateExtensionWithId("4")); - scoped_refptr<Extension> extension2(CreateExtensionWithId("5")); - scoped_refptr<Extension> extension3(CreateExtensionWithId("6")); - - AddRequestPowerStateOverridesExpectation(); - RequestKeepAwake(extension1); - RequestKeepAwake(extension2); - RequestKeepAwake(extension3); - request_id_callback_.Run(kRequestId); - - AddCancelPowerStateOverridesExpectation(); - ReleaseKeepAwake(extension3); - ReleaseKeepAwake(extension1); - ReleaseKeepAwake(extension2); -} - -// TODO(rkc): Add another test to verify a Request->Release->Request scenario. - -} // namespace extensions diff --git a/chrome/browser/chromeos/extensions/power/power_api_manager.cc b/chrome/browser/chromeos/extensions/power/power_api_manager.cc deleted file mode 100644 index f5627ab..0000000 --- a/chrome/browser/chromeos/extensions/power/power_api_manager.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2012 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/chromeos/extensions/power/power_api_manager.h" - -#include "chrome/common/chrome_notification_types.h" -#include "chrome/common/extensions/extension.h" -#include "chromeos/power/power_state_override.h" -#include "content/public/browser/notification_service.h" - -namespace extensions { -namespace power { - -// static -PowerApiManager* PowerApiManager::GetInstance() { - return Singleton<PowerApiManager>::get(); -} - -void PowerApiManager::AddExtensionLock(const std::string& extension_id) { - extension_ids_set_.insert(extension_id); - UpdatePowerSettings(); -} - -void PowerApiManager::RemoveExtensionLock(const std::string& extension_id) { - extension_ids_set_.erase(extension_id); - UpdatePowerSettings(); -} - -void PowerApiManager::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) { - RemoveExtensionLock( - content::Details<extensions::UnloadedExtensionInfo>(details)-> - extension->id()); - UpdatePowerSettings(); - } else if (type == chrome::NOTIFICATION_APP_TERMINATING) { - // If the Chrome app is terminating, ensure we release our power overrides. - power_state_override_ = NULL; - } else { - NOTREACHED() << "Unexpected notification " << type; - } -} - -PowerApiManager::PowerApiManager() - : power_state_override_(NULL) { - registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, - content::NotificationService::AllSources()); - registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, - content::NotificationService::AllSources()); - UpdatePowerSettings(); -} - -PowerApiManager::~PowerApiManager() {} - -void PowerApiManager::UpdatePowerSettings() { - // If we have a wake lock and don't have the power state overriden. - if (extension_ids_set_.size() && !power_state_override_.get()) { - power_state_override_ = new chromeos::PowerStateOverride( - chromeos::PowerStateOverride::BLOCK_DISPLAY_SLEEP); - // else, if we don't have any wake locks and do have a power override. - } else if (extension_ids_set_.empty() && power_state_override_.get()) { - power_state_override_ = NULL; - } -} - -} // namespace power -} // namespace extensions diff --git a/chrome/browser/chromeos/extensions/power/power_api_manager.h b/chrome/browser/chromeos/extensions/power/power_api_manager.h deleted file mode 100644 index cfaf3b9..0000000 --- a/chrome/browser/chromeos/extensions/power/power_api_manager.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2012 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_CHROMEOS_EXTENSIONS_POWER_POWER_API_MANAGER_H_ -#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_POWER_POWER_API_MANAGER_H_ - -#include <set> -#include <string> - -#include "base/memory/singleton.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" - -namespace chromeos { -class PowerStateOverride; -} - -namespace extensions { -namespace power { - -class PowerApiManager : public content::NotificationObserver { - public: - static PowerApiManager* GetInstance(); - - // Add an extension lock for an extension. Calling this for an - // extension id already with a lock will do nothing. - void AddExtensionLock(const std::string& extension_id); - - // Remove an extension lock for an extension. Calling this for an - // extension id without a lock will do nothing. - void RemoveExtensionLock(const std::string& extension_id); - - // Overridden from content::NotificationObserver. - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; - - private: - friend struct DefaultSingletonTraits<PowerApiManager>; - - // Singleton class - use GetInstance instead. - PowerApiManager(); - virtual ~PowerApiManager(); - - // Check the state of the set of extension IDs with a lock. - // If we have any extensions with a lock, make sure that power management - // is overriden. If no extensions have a lock, ensure that our power - // management override is released. - void UpdatePowerSettings(); - - content::NotificationRegistrar registrar_; - - scoped_refptr<chromeos::PowerStateOverride> power_state_override_; - - // Set of extension IDs that have a keep awake lock. - std::set<std::string> extension_ids_set_; - - DISALLOW_COPY_AND_ASSIGN(PowerApiManager); -}; - -} // namespace power -} // namespace extensions - -#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_POWER_POWER_API_MANAGER_H_ diff --git a/chrome/browser/extensions/api/power/power_api.cc b/chrome/browser/extensions/api/power/power_api.cc new file mode 100644 index 0000000..e82e4b5 --- /dev/null +++ b/chrome/browser/extensions/api/power/power_api.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2013 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/api/power/power_api.h" + +#include "chrome/browser/extensions/api/power/power_api_manager.h" +#include "chrome/common/extensions/api/power.h" + +namespace extensions { + +bool PowerRequestKeepAwakeFunction::RunImpl() { + scoped_ptr<api::power::RequestKeepAwake::Params> params( + api::power::RequestKeepAwake::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + EXTENSION_FUNCTION_VALIDATE(params->level != api::power::LEVEL_NONE); + PowerApiManager::GetInstance()->AddRequest(extension_id(), params->level); + return true; +} + +bool PowerReleaseKeepAwakeFunction::RunImpl() { + PowerApiManager::GetInstance()->RemoveRequest(extension_id()); + return true; +} + +} // namespace extensions diff --git a/chrome/browser/chromeos/extensions/power/power_api.h b/chrome/browser/extensions/api/power/power_api.h index 1d79f8a..fcde9c5 100644 --- a/chrome/browser/chromeos/extensions/power/power_api.h +++ b/chrome/browser/extensions/api/power/power_api.h @@ -2,18 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_POWER_POWER_API_H_ -#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_POWER_POWER_API_H_ +#ifndef CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_H_ #include "chrome/browser/extensions/extension_function.h" namespace extensions { -// Implementation of the chrome.experimental.power.requestKeepAwake API. +// Implementation of the chrome.power.requestKeepAwake API. class PowerRequestKeepAwakeFunction : public SyncExtensionFunction { public: - DECLARE_EXTENSION_FUNCTION("experimental.power.requestKeepAwake", - EXPERIMENTAL_POWER_REQUESTKEEPAWAKE) + DECLARE_EXTENSION_FUNCTION("power.requestKeepAwake", POWER_REQUESTKEEPAWAKE) protected: virtual ~PowerRequestKeepAwakeFunction() {} @@ -22,11 +21,10 @@ class PowerRequestKeepAwakeFunction : public SyncExtensionFunction { virtual bool RunImpl() OVERRIDE; }; -// Implementation of the chrome.experimental.power.releaseKeepAwake API. +// Implementation of the chrome.power.releaseKeepAwake API. class PowerReleaseKeepAwakeFunction : public SyncExtensionFunction { public: - DECLARE_EXTENSION_FUNCTION("experimental.power.releaseKeepAwake", - EXPERIMENTAL_POWER_RELEASEKEEPAWAKE) + DECLARE_EXTENSION_FUNCTION("power.releaseKeepAwake", POWER_RELEASEKEEPAWAKE) protected: virtual ~PowerReleaseKeepAwakeFunction() {} @@ -37,4 +35,4 @@ class PowerReleaseKeepAwakeFunction : public SyncExtensionFunction { } // namespace extensions -#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_POWER_POWER_API_H_ +#endif // CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_H_ diff --git a/chrome/browser/extensions/api/power/power_api_manager.cc b/chrome/browser/extensions/api/power/power_api_manager.cc new file mode 100644 index 0000000..1a1c37b --- /dev/null +++ b/chrome/browser/extensions/api/power/power_api_manager.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2013 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/api/power/power_api_manager.h" + +#include "base/bind.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/extensions/extension.h" +#include "content/public/browser/notification_service.h" + +namespace extensions { + +namespace { + +const char kPowerSaveBlockerReason[] = "extension"; + +content::PowerSaveBlocker::PowerSaveBlockerType +LevelToPowerSaveBlockerType(api::power::Level level) { + switch (level) { + case api::power::LEVEL_SYSTEM: + return content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension; + case api::power::LEVEL_DISPLAY: // fallthrough + case api::power::LEVEL_NONE: + return content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep; + } + NOTREACHED() << "Unhandled level " << level; + return content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep; +} + +} // namespace + +// static +PowerApiManager* PowerApiManager::GetInstance() { + return Singleton<PowerApiManager>::get(); +} + +void PowerApiManager::AddRequest(const std::string& extension_id, + api::power::Level level) { + extension_levels_[extension_id] = level; + UpdatePowerSaveBlocker(); +} + +void PowerApiManager::RemoveRequest(const std::string& extension_id) { + extension_levels_.erase(extension_id); + UpdatePowerSaveBlocker(); +} + +void PowerApiManager::SetCreateBlockerFunctionForTesting( + CreateBlockerFunction function) { + create_blocker_function_ = !function.is_null() ? function : + base::Bind(&content::PowerSaveBlocker::Create); +} + +void PowerApiManager::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case chrome::NOTIFICATION_EXTENSION_UNLOADED: + RemoveRequest(content::Details<extensions::UnloadedExtensionInfo>( + details)->extension->id()); + UpdatePowerSaveBlocker(); + break; + case chrome::NOTIFICATION_APP_TERMINATING: + power_save_blocker_.reset(); + break; + default: + NOTREACHED() << "Unexpected notification " << type; + } +} + +PowerApiManager::PowerApiManager() + : create_blocker_function_(base::Bind(&content::PowerSaveBlocker::Create)), + current_level_(api::power::LEVEL_SYSTEM) { + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, + content::NotificationService::AllSources()); + registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, + content::NotificationService::AllSources()); +} + +PowerApiManager::~PowerApiManager() {} + +void PowerApiManager::UpdatePowerSaveBlocker() { + if (extension_levels_.empty()) { + power_save_blocker_.reset(); + return; + } + + api::power::Level new_level = api::power::LEVEL_SYSTEM; + for (ExtensionLevelMap::const_iterator it = extension_levels_.begin(); + it != extension_levels_.end(); ++it) { + if (it->second == api::power::LEVEL_DISPLAY) + new_level = it->second; + } + + // If the level changed and we need to create a new blocker, do a swap + // to ensure that there isn't a brief period where power management is + // unblocked. + if (!power_save_blocker_ || new_level != current_level_) { + content::PowerSaveBlocker::PowerSaveBlockerType type = + LevelToPowerSaveBlockerType(new_level); + scoped_ptr<content::PowerSaveBlocker> new_blocker( + create_blocker_function_.Run(type, kPowerSaveBlockerReason)); + power_save_blocker_.swap(new_blocker); + current_level_ = new_level; + } +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/power/power_api_manager.h b/chrome/browser/extensions/api/power/power_api_manager.h new file mode 100644 index 0000000..48ed9ee --- /dev/null +++ b/chrome/browser/extensions/api/power/power_api_manager.h @@ -0,0 +1,79 @@ +// Copyright (c) 2013 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_API_POWER_POWER_API_MANAGER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_MANAGER_H_ + +#include <map> +#include <string> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "chrome/common/extensions/api/power.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/power_save_blocker.h" + +namespace extensions { + +class PowerApiManager : public content::NotificationObserver { + public: + typedef base::Callback<scoped_ptr<content::PowerSaveBlocker>( + content::PowerSaveBlocker::PowerSaveBlockerType, + const std::string&)> CreateBlockerFunction; + + static PowerApiManager* GetInstance(); + + // Adds an extension lock at |level| for |extension_id|, replacing the + // extension's existing lock, if any. + void AddRequest(const std::string& extension_id, api::power::Level level); + + // Removes an extension lock for an extension. Calling this for an + // extension id without a lock will do nothing. + void RemoveRequest(const std::string& extension_id); + + // Replaces the function that will be called to create PowerSaveBlocker + // objects. Passing an empty callback will revert to the default. + void SetCreateBlockerFunctionForTesting(CreateBlockerFunction function); + + // Overridden from content::NotificationObserver. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + private: + friend struct DefaultSingletonTraits<PowerApiManager>; + + PowerApiManager(); + virtual ~PowerApiManager(); + + // Updates |power_save_blocker_| and |current_level_| after iterating + // over |extension_levels_|. + void UpdatePowerSaveBlocker(); + + content::NotificationRegistrar registrar_; + + // Function that should be called to create PowerSaveBlocker objects. + // Tests can change this to record what would've been done instead of + // actually changing the system power-saving settings. + CreateBlockerFunction create_blocker_function_; + + scoped_ptr<content::PowerSaveBlocker> power_save_blocker_; + + // Current level used by |power_save_blocker_|. Meaningless if + // |power_save_blocker_| is NULL. + api::power::Level current_level_; + + // Map from extension ID to the corresponding level for each extension + // that has an outstanding request. + typedef std::map<std::string, api::power::Level> ExtensionLevelMap; + ExtensionLevelMap extension_levels_; + + DISALLOW_COPY_AND_ASSIGN(PowerApiManager); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_MANAGER_H_ diff --git a/chrome/browser/extensions/api/power/power_api_unittest.cc b/chrome/browser/extensions/api/power/power_api_unittest.cc new file mode 100644 index 0000000..7c2ce9a --- /dev/null +++ b/chrome/browser/extensions/api/power/power_api_unittest.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2013 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/api/power/power_api.h" + +#include <deque> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/extensions/api/power/power_api_manager.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "content/public/browser/power_save_blocker.h" + +namespace utils = extension_function_test_utils; + +namespace extensions { + +namespace { + +// Args commonly passed to PowerSaveBlockerStubManager::CallFunction(). +const char kDisplayArgs[] = "[\"display\"]"; +const char kSystemArgs[] = "[\"system\"]"; +const char kEmptyArgs[] = "[]"; + +// Different actions that can be performed as a result of a +// PowerSaveBlocker being created or destroyed. +enum Request { + BLOCK_APP_SUSPENSION, + UNBLOCK_APP_SUSPENSION, + BLOCK_DISPLAY_SLEEP, + UNBLOCK_DISPLAY_SLEEP, + // Returned by PowerSaveBlockerStubManager::PopFirstRequest() when no + // requests are present. + NONE, +}; + +// Stub implementation of content::PowerSaveBlocker that just runs a +// callback on destruction. +class PowerSaveBlockerStub : public content::PowerSaveBlocker { + public: + explicit PowerSaveBlockerStub(base::Closure unblock_callback) + : unblock_callback_(unblock_callback) { + } + + virtual ~PowerSaveBlockerStub() { + unblock_callback_.Run(); + } + + private: + base::Closure unblock_callback_; + + DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStub); +}; + +// Manages PowerSaveBlockerStub objects. Tests can instantiate this class +// to make PowerApiManager's calls to create PowerSaveBlockers record the +// actions that would've been performed instead of actually blocking and +// unblocking power management. +class PowerSaveBlockerStubManager { + public: + PowerSaveBlockerStubManager() : weak_ptr_factory_(this) { + // Use base::Unretained since callbacks with return values can't use + // weak pointers. + PowerApiManager::GetInstance()->SetCreateBlockerFunctionForTesting( + base::Bind(&PowerSaveBlockerStubManager::CreateStub, + base::Unretained(this))); + } + + ~PowerSaveBlockerStubManager() { + PowerApiManager::GetInstance()->SetCreateBlockerFunctionForTesting( + PowerApiManager::CreateBlockerFunction()); + } + + // Removes and returns the first item from |requests_|. Returns NONE if + // |requests_| is empty. + Request PopFirstRequest() { + if (requests_.empty()) + return NONE; + + Request request = requests_.front(); + requests_.pop_front(); + return request; + } + + private: + // Creates a new PowerSaveBlockerStub of type |type|. + scoped_ptr<content::PowerSaveBlocker> CreateStub( + content::PowerSaveBlocker::PowerSaveBlockerType type, + const std::string& reason) { + Request unblock_request = NONE; + switch (type) { + case content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension: + requests_.push_back(BLOCK_APP_SUSPENSION); + unblock_request = UNBLOCK_APP_SUSPENSION; + break; + case content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep: + requests_.push_back(BLOCK_DISPLAY_SLEEP); + unblock_request = UNBLOCK_DISPLAY_SLEEP; + break; + } + return scoped_ptr<content::PowerSaveBlocker>( + new PowerSaveBlockerStub( + base::Bind(&PowerSaveBlockerStubManager::AppendRequest, + weak_ptr_factory_.GetWeakPtr(), + unblock_request))); + } + + void AppendRequest(Request request) { + requests_.push_back(request); + } + + // Requests in chronological order. + std::deque<Request> requests_; + + base::WeakPtrFactory<PowerSaveBlockerStubManager> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStubManager); +}; + +} // namespace + +class PowerApiTest : public BrowserWithTestWindowTest { + public: + virtual void SetUp() OVERRIDE { + BrowserWithTestWindowTest::SetUp(); + manager_.reset(new PowerSaveBlockerStubManager); + extension_ = utils::CreateEmptyExtensionWithLocation( + extensions::Manifest::UNPACKED); + } + + protected: + // Shorthand for PowerRequestKeepAwakeFunction and + // PowerReleaseKeepAwakeFunction. + enum FunctionType { + REQUEST, + RELEASE, + }; + + // Calls the function described by |type| with |args|, a JSON list of + // arguments, on behalf of |extension|. + bool CallFunction(FunctionType type, + const std::string& args, + extensions::Extension* extension) { + UIThreadExtensionFunction* function = + type == REQUEST ? + static_cast<UIThreadExtensionFunction*>( + new PowerRequestKeepAwakeFunction) : + static_cast<UIThreadExtensionFunction*>( + new PowerReleaseKeepAwakeFunction); + function->set_extension(extension); + return utils::RunFunction(function, args, browser(), utils::NONE); + } + + // Send a notification to PowerApiManager saying that |extension| has + // been unloaded. + void UnloadExtension(extensions::Extension* extension) { + UnloadedExtensionInfo details( + extension, extension_misc::UNLOAD_REASON_UNINSTALL); + PowerApiManager::GetInstance()->Observe( + chrome::NOTIFICATION_EXTENSION_UNLOADED, + content::Source<Profile>(browser()->profile()), + content::Details<UnloadedExtensionInfo>(&details)); + } + + scoped_ptr<PowerSaveBlockerStubManager> manager_; + scoped_refptr<extensions::Extension> extension_; +}; + +TEST_F(PowerApiTest, RequestAndRelease) { + // Simulate an extension making and releasing a "display" request and a + // "system" request. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get())); + EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, RequestWithoutRelease) { + // Simulate an extension calling requestKeepAwake() without calling + // releaseKeepAwake(). The override should be automatically removed when + // the extension is unloaded. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + UnloadExtension(extension_.get()); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, ReleaseWithoutRequest) { + // Simulate an extension calling releaseKeepAwake() without having + // calling requestKeepAwake() earlier. The call should be ignored. + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, UpgradeRequest) { + // Simulate an extension calling requestKeepAwake("system") and then + // requestKeepAwake("display"). When the second call is made, a + // display-sleep-blocking request should be made before the initial + // app-suspension-blocking request is released. + ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get())); + EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, DowngradeRequest) { + // Simulate an extension calling requestKeepAwake("display") and then + // requestKeepAwake("system"). When the second call is made, an + // app-suspension-blocking request should be made before the initial + // display-sleep-blocking request is released. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get())); + EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, MultipleExtensions) { + // Simulate an extension blocking the display from sleeping. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + // Create a second extension that blocks system suspend. No additional + // PowerSaveBlocker is needed; the blocker from the first extension + // already covers the behavior requested by the second extension. + scoped_ptr<base::DictionaryValue> extension_value( + utils::ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}")); + scoped_refptr<extensions::Extension> extension2( + utils::CreateExtension(extensions::Manifest::UNPACKED, + extension_value.get(), "second_extension")); + ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension2.get())); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + // When the first extension is unloaded, a new app-suspension blocker + // should be created before the display-sleep blocker is destroyed. + UnloadExtension(extension_.get()); + EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + // Make the first extension block display-sleep again. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/extension_function_histogram_value.h b/chrome/browser/extensions/extension_function_histogram_value.h index ab247a3..c2cb528 100644 --- a/chrome/browser/extensions/extension_function_histogram_value.h +++ b/chrome/browser/extensions/extension_function_histogram_value.h @@ -492,6 +492,8 @@ enum HistogramValue { SYNCFILESYSTEM_GETCONFLICTRESOLUTIONPOLICY, NETWORKINGPRIVATE_SETPROPERTIES, NETWORKINGPRIVATE_GETSTATE, + POWER_REQUESTKEEPAWAKE, + POWER_RELEASEKEEPAWAKE, ENUM_BOUNDARY // Last entry: Add new entries above. }; diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index 160aa27..49f78d0 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -298,10 +298,6 @@ 'browser/chromeos/extensions/install_limiter_factory.h', 'browser/chromeos/extensions/media_player_event_router.cc', 'browser/chromeos/extensions/media_player_event_router.h', - 'browser/chromeos/extensions/power/power_api.cc', - 'browser/chromeos/extensions/power/power_api.h', - 'browser/chromeos/extensions/power/power_api_manager.cc', - 'browser/chromeos/extensions/power/power_api_manager.h', 'browser/chromeos/external_metrics.cc', 'browser/chromeos/external_metrics.h', 'browser/chromeos/external_protocol_dialog.cc', diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index d7babda..0a9688d 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -282,6 +282,10 @@ 'browser/extensions/api/permissions/permissions_api_helpers.h', 'browser/extensions/api/plugins/plugins_api.cc', 'browser/extensions/api/plugins/plugins_api.h', + 'browser/extensions/api/power/power_api.cc', + 'browser/extensions/api/power/power_api.h', + 'browser/extensions/api/power/power_api_manager.cc', + 'browser/extensions/api/power/power_api_manager.h', 'browser/extensions/api/preference/preference_api.cc', 'browser/extensions/api/preference/preference_api.h', 'browser/extensions/api/preference/preference_api_constants.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index e4bb786..8dff977 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1131,7 +1131,6 @@ 'browser/chromeos/extensions/info_private_apitest.cc', 'browser/chromeos/extensions/input_method_apitest_chromeos.cc', 'browser/chromeos/extensions/networking_private_apitest.cc', - 'browser/chromeos/extensions/power/power_api_browsertest.cc', 'browser/chromeos/extensions/wallpaper_private_apitest.cc', 'browser/chromeos/kiosk_mode/mock_kiosk_mode_settings.cc', 'browser/chromeos/kiosk_mode/mock_kiosk_mode_settings.h', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 9787db7..1d4916a 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -713,6 +713,7 @@ 'browser/extensions/api/notifications/notifications_api_unittest.cc', 'browser/extensions/api/omnibox/omnibox_unittest.cc', 'browser/extensions/api/permissions/permissions_api_helpers_unittest.cc', + 'browser/extensions/api/power/power_api_unittest.cc', 'browser/extensions/api/proxy/proxy_api_helpers_unittest.cc', 'browser/extensions/api/push_messaging/obfuscated_gaia_id_fetcher_unittest.cc', 'browser/extensions/api/push_messaging/push_messaging_invalidation_handler_unittest.cc', diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json index 4ca397e..d2a7b39 100644 --- a/chrome/common/extensions/api/_permission_features.json +++ b/chrome/common/extensions/api/_permission_features.json @@ -314,6 +314,11 @@ "channel": "stable", "extension_types": ["extension", "packaged_app"] }, + "power": { + "channel": "dev", + "platform": "chromeos", + "extension_types": ["extension", "hosted_app", "packaged_app"] + }, "privacy": { "channel": "stable", "extension_types": ["extension", "packaged_app"] diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp index 90ed37e..a89e95c 100644 --- a/chrome/common/extensions/api/api.gyp +++ b/chrome/common/extensions/api/api.gyp @@ -47,7 +47,6 @@ 'experimental_idltest.idl', 'experimental_infobars.json', 'experimental_media_galleries.idl', - 'experimental_power.json', 'experimental_record.json', 'experimental_system_info_cpu.idl', 'experimental_system_info_memory.idl', @@ -71,6 +70,7 @@ 'page_capture.json', 'page_launcher.idl', 'permissions.json', + 'power.idl', 'push_messaging.idl', 'rtc_private.idl', 'serial.idl', diff --git a/chrome/common/extensions/api/experimental_power.json b/chrome/common/extensions/api/experimental_power.json deleted file mode 100644 index 1090942..0000000 --- a/chrome/common/extensions/api/experimental_power.json +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2012 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. -[ - { - "namespace": "experimental.power", - "compiler_options": { - "implemented_in": "chrome/browser/chromeos/extensions/power/power_api.h" - }, - "platforms": ["chromeos"], - "types": [], - "functions": [ - { - "name": "requestKeepAwake", - "type": "function", - "description": "Requests that the machine be kept awake. Requests can be canceled manually with releaseKeepAwake, and are automatically canceled when the machine is restarted, or when the extension is disabled or uninstalled. Calling this multiple times has the same effect as calling it once.", - "parameters": [ - { - "name": "callback", - "type": "function", - "optional": true, - "parameters": [ - { - "name": "success", - "type": "boolean", - "description": "True if the request was successful, false otherwise." - } - ] - } - ] - }, - { - "name": "releaseKeepAwake", - "type": "function", - "description": "Releases a keep awake request. Once there are no keep awake requests active on the system, normal power management will resume.", - "parameters": [ - { - "name": "callback", - "type": "function", - "optional": true, - "parameters": [ - { - "name": "success", - "type": "boolean", - "description": "True if the release was successful, false otherwise." - } - ] - } - ] - } - ] - } -] diff --git a/chrome/common/extensions/api/power.idl b/chrome/common/extensions/api/power.idl new file mode 100644 index 0000000..72cbcee --- /dev/null +++ b/chrome/common/extensions/api/power.idl @@ -0,0 +1,26 @@ +// Copyright (c) 2013 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. + +// An API that can be used to override the system's power management +// features. +[permissions=power] +namespace power { + enum Level { + // Prevent the system from sleeping in response to user inactivity. + system, + + // Prevent the display from being turned off or dimmed or the system + // from sleeping in response to user inactivity. + display + }; + + interface Functions { + // Requests that power management be temporarily disabled. |level| + // describes the degree to which power management should be disabled. + static void requestKeepAwake(Level level); + + // Releases a request previously made via requestKeepAwake(). + static void releaseKeepAwake(); + }; +}; diff --git a/chrome/common/extensions/docs/examples/api/power/_locales/en/messages.json b/chrome/common/extensions/docs/examples/api/power/_locales/en/messages.json new file mode 100644 index 0000000..ef50944 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/_locales/en/messages.json @@ -0,0 +1,22 @@ +{ + "extensionName": { + "message": "Keep Awake", + "description": "Extension name." + }, + "extensionDescription": { + "message": "Override system power-saving settings.", + "description": "Extension description." + }, + "disabledTitle": { + "message": "Default power-saving settings", + "description": "Browser action title when disabled." + }, + "displayTitle": { + "message": "Screen will be kept on", + "description": "Browser action title when preventing screen-off." + }, + "systemTitle": { + "message": "System will stay awake", + "description": "Browser action title when preventing system sleep." + } +} diff --git a/chrome/common/extensions/docs/examples/api/power/background.js b/chrome/common/extensions/docs/examples/api/power/background.js new file mode 100644 index 0000000..e190777 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/background.js @@ -0,0 +1,113 @@ +// Copyright (c) 2013 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. + +/** + * Available levels of power-saving-overriding. + */ +var LevelEnum = { + DISABLED: '', + DISPLAY: 'display', + SYSTEM: 'system' +}; + +/** + * Key used for storing the current level in {localStorage}. + */ +var LEVEL_KEY = 'level'; + +/** + * Current {LevelEnum}. + */ +var currentLevel = LevelEnum.DISABLED; + +/** + * Should the old {chrome.experimental.power} API be used rather than + * {chrome.power}? + */ +var useOldApi = !chrome.power; + +/** + * Returns the previously-used level. + * @return {string} Saved {LevelEnum} from local storage. + */ +function getInitialLevel() { + if (LEVEL_KEY in localStorage) { + var savedLevel = localStorage[LEVEL_KEY]; + for (var key in LevelEnum) { + if (savedLevel == LevelEnum[key]) { + return savedLevel; + } + } + } + return LevelEnum.DISABLED; +} + +/** + * Switches to a new power-saving-overriding level. + * @param {string} newLevel New {LevelEnum} to use. + */ +function setLevel(newLevel) { + var imagePrefix = 'night'; + var title = ''; + + // The old API doesn't support the "system" level. + if (useOldApi && newLevel == LevelEnum.SYSTEM) + newLevel = LevelEnum.DISPLAY; + + switch (newLevel) { + case LevelEnum.DISABLED: + (useOldApi ? chrome.experimental.power : chrome.power).releaseKeepAwake(); + imagePrefix = 'night'; + title = chrome.i18n.getMessage('disabledTitle'); + break; + case LevelEnum.DISPLAY: + if (useOldApi) + chrome.experimental.power.requestKeepAwake(function() {}); + else + chrome.power.requestKeepAwake('display'); + imagePrefix = 'day'; + title = chrome.i18n.getMessage('displayTitle'); + break; + case LevelEnum.SYSTEM: + chrome.power.requestKeepAwake('system'); + imagePrefix = 'sunset'; + title = chrome.i18n.getMessage('systemTitle'); + break; + default: + throw 'Invalid level "' + newLevel + '"'; + } + + currentLevel = newLevel; + localStorage[LEVEL_KEY] = currentLevel; + + chrome.browserAction.setIcon({ + path: { + '19': 'images/' + imagePrefix + '-19.png', + '38': 'images/' + imagePrefix + '-38.png' + } + }); + chrome.browserAction.setTitle({title: title}); +} + +/** + * Cycles levels in response to browser action icon clicks. + */ +function handleClicked() { + switch (currentLevel) { + case LevelEnum.DISABLED: + setLevel(LevelEnum.DISPLAY); + break; + case LevelEnum.DISPLAY: + setLevel(useOldApi ? LevelEnum.DISABLED : LevelEnum.SYSTEM); + break; + case LevelEnum.SYSTEM: + setLevel(LevelEnum.DISABLED); + break; + default: + throw 'Invalid level "' + currentLevel + '"'; + } +} + +setLevel(getInitialLevel()); +chrome.browserAction.onClicked.addListener(handleClicked); diff --git a/chrome/common/extensions/docs/examples/api/power/images/day-19.png b/chrome/common/extensions/docs/examples/api/power/images/day-19.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/images/day-19.png diff --git a/chrome/common/extensions/docs/examples/api/power/images/day-38.png b/chrome/common/extensions/docs/examples/api/power/images/day-38.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/images/day-38.png diff --git a/chrome/common/extensions/docs/examples/api/power/images/icon-128.png b/chrome/common/extensions/docs/examples/api/power/images/icon-128.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/images/icon-128.png diff --git a/chrome/common/extensions/docs/examples/api/power/images/icon-16.png b/chrome/common/extensions/docs/examples/api/power/images/icon-16.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/images/icon-16.png diff --git a/chrome/common/extensions/docs/examples/api/power/images/icon-48.png b/chrome/common/extensions/docs/examples/api/power/images/icon-48.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/images/icon-48.png diff --git a/chrome/common/extensions/docs/examples/api/power/images/night-19.png b/chrome/common/extensions/docs/examples/api/power/images/night-19.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/images/night-19.png diff --git a/chrome/common/extensions/docs/examples/api/power/images/night-38.png b/chrome/common/extensions/docs/examples/api/power/images/night-38.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/images/night-38.png diff --git a/chrome/common/extensions/docs/examples/api/power/images/sunset-19.png b/chrome/common/extensions/docs/examples/api/power/images/sunset-19.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/images/sunset-19.png diff --git a/chrome/common/extensions/docs/examples/api/power/images/sunset-38.png b/chrome/common/extensions/docs/examples/api/power/images/sunset-38.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/images/sunset-38.png diff --git a/chrome/common/extensions/docs/examples/api/power/manifest.json b/chrome/common/extensions/docs/examples/api/power/manifest.json new file mode 100644 index 0000000..ccba84e --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/power/manifest.json @@ -0,0 +1,24 @@ +{ + "manifest_version": 2, + + "name": "__MSG_extensionName__", + "description": "__MSG_extensionDescription__", + "version": "1.2", + "icons": { + "16": "images/icon-16.png", + "48": "images/icon-48.png", + "128": "images/icon-128.png" + }, + + "permissions": [ + "experimental", + "power" + ], + "browser_action": {}, + "background": { + "scripts": ["background.js"], + "persistent": false + }, + + "default_locale": "en" +} diff --git a/chrome/common/extensions/permissions/api_permission.cc b/chrome/common/extensions/permissions/api_permission.cc index 0a64152..1ec7743 100644 --- a/chrome/common/extensions/permissions/api_permission.cc +++ b/chrome/common/extensions/permissions/api_permission.cc @@ -216,6 +216,7 @@ void APIPermissionInfo::RegisterAllPermissions( IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT, PermissionMessage::kManagement }, { APIPermission::kNativeMessaging, "nativeMessaging" }, + { APIPermission::kPower, "power", }, { APIPermission::kPrivacy, "privacy", kFlagNone, IDS_EXTENSION_PROMPT_WARNING_PRIVACY, PermissionMessage::kPrivacy }, diff --git a/chrome/common/extensions/permissions/api_permission.h b/chrome/common/extensions/permissions/api_permission.h index 64f1f6e..98005fd 100644 --- a/chrome/common/extensions/permissions/api_permission.h +++ b/chrome/common/extensions/permissions/api_permission.h @@ -93,6 +93,7 @@ class APIPermission { kPageCapture, kPointerLock, kPlugin, + kPower, kPrivacy, kProxy, kPushMessaging, diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc index 63668cc..b673748 100644 --- a/chrome/common/extensions/permissions/permission_set_unittest.cc +++ b/chrome/common/extensions/permissions/permission_set_unittest.cc @@ -671,6 +671,7 @@ TEST_F(PermissionsTest, PermissionMessages) { skip.insert(APIPermission::kIdle); skip.insert(APIPermission::kNotification); skip.insert(APIPermission::kPointerLock); + skip.insert(APIPermission::kPower); skip.insert(APIPermission::kPushMessaging); skip.insert(APIPermission::kSessionRestore); skip.insert(APIPermission::kScreensaver); |