diff options
20 files changed, 514 insertions, 93 deletions
diff --git a/chrome/browser/extensions/extension_function.cc b/chrome/browser/extensions/extension_function.cc index 72728aa..c4a502f 100644 --- a/chrome/browser/extensions/extension_function.cc +++ b/chrome/browser/extensions/extension_function.cc @@ -187,7 +187,14 @@ void IOThreadExtensionFunction::SendResponse(bool success) { ipc_sender(), routing_id_, success); } -AsyncExtensionFunction::AsyncExtensionFunction() { +AsyncExtensionFunction::AsyncExtensionFunction() : delegate_(NULL) { +} + +void AsyncExtensionFunction::SendResponse(bool success) { + if (delegate_) + delegate_->OnSendResponse(this, success); + else + UIThreadExtensionFunction::SendResponse(success); } AsyncExtensionFunction::~AsyncExtensionFunction() { diff --git a/chrome/browser/extensions/extension_function.h b/chrome/browser/extensions/extension_function.h index 21fd897..4b1fb5f 100644 --- a/chrome/browser/extensions/extension_function.h +++ b/chrome/browser/extensions/extension_function.h @@ -142,7 +142,7 @@ class ExtensionFunction // Sends the result back to the extension. virtual void SendResponse(bool success) = 0; - // Common implementation for SenderResponse. + // Common implementation for SendResponse. void SendResponseImpl(base::ProcessHandle process, IPC::Message::Sender* ipc_sender, int routing_id, @@ -340,10 +340,24 @@ class IOThreadExtensionFunction : public ExtensionFunction { // the browser's UI thread*. class AsyncExtensionFunction : public UIThreadExtensionFunction { public: + // A delegate for use in testing, to intercept the call to SendResponse. + class DelegateForTests { + public: + virtual void OnSendResponse(AsyncExtensionFunction* function, + bool success) = 0; + }; + AsyncExtensionFunction(); + virtual void SendResponse(bool success) OVERRIDE; + + void set_test_delegate(DelegateForTests* delegate) { + delegate_ = delegate; + } protected: virtual ~AsyncExtensionFunction(); + + DelegateForTests* delegate_; }; // A SyncExtensionFunction is an ExtensionFunction that runs synchronously diff --git a/chrome/browser/extensions/extension_function_test_utils.cc b/chrome/browser/extensions/extension_function_test_utils.cc index c6990e8..e68bf5e 100644 --- a/chrome/browser/extensions/extension_function_test_utils.cc +++ b/chrome/browser/extensions/extension_function_test_utils.cc @@ -13,6 +13,7 @@ #include "chrome/browser/extensions/extension_function_dispatcher.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/extensions/extension.h" +#include "chrome/test/base/ui_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -160,4 +161,58 @@ void RunFunction(UIThreadExtensionFunction* function, function->Run(); } +// This helps us be able to wait until an AsyncExtensionFunction calls +// SendResponse. +class SendResponseDelegate : public AsyncExtensionFunction::DelegateForTests { + public: + SendResponseDelegate() : should_post_quit_(false) {} + + virtual ~SendResponseDelegate() {} + + void set_should_post_quit(bool should_quit) { + should_post_quit_ = should_quit; + } + + bool HasResponse() { + return response_.get() != NULL; + } + + bool GetResponse() { + EXPECT_TRUE(HasResponse()); + return *response_.get(); + } + + virtual void OnSendResponse(AsyncExtensionFunction* function, bool success) { + ASSERT_FALSE(HasResponse()); + response_.reset(new bool); + *response_ = success; + if (should_post_quit_) { + MessageLoopForUI::current()->Quit(); + } + } + + private: + scoped_ptr<bool> response_; + bool should_post_quit_; +}; + +bool RunAsyncFunction(AsyncExtensionFunction* function, + const std::string& args, + Browser* browser, + RunFunctionFlags flags) { + SendResponseDelegate response_delegate; + function->set_test_delegate(&response_delegate); + RunFunction(function, args, browser, flags); + + // If the RunImpl of |function| didn't already call SendResponse, run the + // message loop until they do. + if (!response_delegate.HasResponse()) { + response_delegate.set_should_post_quit(true); + ui_test_utils::RunMessageLoop(); + } + + EXPECT_TRUE(response_delegate.HasResponse()); + return response_delegate.GetResponse(); +} + } // namespace extension_function_test_utils diff --git a/chrome/browser/extensions/extension_function_test_utils.h b/chrome/browser/extensions/extension_function_test_utils.h index 3b24b5d..ef005a4 100644 --- a/chrome/browser/extensions/extension_function_test_utils.h +++ b/chrome/browser/extensions/extension_function_test_utils.h @@ -10,6 +10,7 @@ #include "base/memory/ref_counted.h" +class AsyncExtensionFunction; class Browser; class Extension; class UIThreadExtensionFunction; @@ -84,6 +85,13 @@ void RunFunction(UIThreadExtensionFunction* function, Browser* browser, RunFunctionFlags flags); +// Similar to RunFunction, but doesn't return until |function| calls +// SendResponse. Returns the value |function| passed to SendResponse. +bool RunAsyncFunction(AsyncExtensionFunction* function, + const std::string& args, + Browser* browser, + RunFunctionFlags flags); + } // namespace extension_function_test_utils #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_FUNCTION_TEST_UTILS_H_ diff --git a/chrome/browser/extensions/extension_install_dialog.cc b/chrome/browser/extensions/extension_install_dialog.cc index 150200d..dad6d26 100644 --- a/chrome/browser/extensions/extension_install_dialog.cc +++ b/chrome/browser/extensions/extension_install_dialog.cc @@ -4,12 +4,16 @@ #include "chrome/browser/extensions/extension_install_dialog.h" +#include "base/bind.h" #include "base/file_path.h" +#include "base/message_loop.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" #include "chrome/common/extensions/extension.h" -// A flag used for SetExtensionInstallDialogForManifestAutoConfirmForTests +namespace { + +// A flag used for SetExtensionInstallDialogAutoConfirmForTests enum AutoConfirmForTest { DO_NOT_SKIP = 0, PROCEED, @@ -17,6 +21,37 @@ enum AutoConfirmForTest { }; AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP; +void AutoConfirmTask(ExtensionInstallUI::Delegate* delegate, bool proceed) { + if (proceed) + delegate->InstallUIProceed(); + else + delegate->InstallUIAbort(true); +} + +void DoAutoConfirm(ExtensionInstallUI::Delegate* delegate) { + bool proceed = (auto_confirm_for_tests == PROCEED); + // We use PostTask instead of calling the delegate directly here, because in + // the real implementations it's highly likely the message loop will be + // pumping a few times before the user clicks accept or cancel. + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&AutoConfirmTask, delegate, proceed)); +} + +} // namespace + +void ShowExtensionInstallDialog(Profile* profile, + ExtensionInstallUI::Delegate* delegate, + const Extension* extension, + SkBitmap* icon, + const ExtensionInstallUI::Prompt& prompt) { + if (auto_confirm_for_tests != DO_NOT_SKIP) { + DoAutoConfirm(delegate); + return; + } + ShowExtensionInstallDialogImpl(profile, delegate, extension, icon, prompt); +} + bool ShowExtensionInstallDialogForManifest( Profile *profile, ExtensionInstallUI::Delegate* delegate, @@ -59,10 +94,7 @@ bool ShowExtensionInstallDialogForManifest( // In tests, we may have setup to proceed or abort without putting up the real // confirmation dialog. if (auto_confirm_for_tests != DO_NOT_SKIP) { - if (auto_confirm_for_tests == PROCEED) - delegate->InstallUIProceed(); - else - delegate->InstallUIAbort(true); + DoAutoConfirm(delegate); return true; } @@ -78,7 +110,7 @@ bool ShowExtensionInstallDialogForManifest( return true; } -void SetExtensionInstallDialogForManifestAutoConfirmForTests( +void SetExtensionInstallDialogAutoConfirmForTests( bool should_proceed) { auto_confirm_for_tests = should_proceed ? PROCEED : ABORT; } diff --git a/chrome/browser/extensions/extension_install_dialog.h b/chrome/browser/extensions/extension_install_dialog.h index 8f0ada4..4124635 100644 --- a/chrome/browser/extensions/extension_install_dialog.h +++ b/chrome/browser/extensions/extension_install_dialog.h @@ -20,13 +20,19 @@ namespace base { class DictionaryValue; } -// The implementations of this function are platform-specific. void ShowExtensionInstallDialog(Profile* profile, ExtensionInstallUI::Delegate* delegate, const Extension* extension, SkBitmap* icon, const ExtensionInstallUI::Prompt& prompt); +// The implementations of this function are platform-specific. +void ShowExtensionInstallDialogImpl(Profile* profile, + ExtensionInstallUI::Delegate* delegate, + const Extension* extension, + SkBitmap* icon, + const ExtensionInstallUI::Prompt& prompt); + // Wrapper around ShowExtensionInstallDialog that shows the install dialog for // a given manifest (that corresponds to an extension about to be installed with // ID |id|). If the name or description in the manifest is a localized @@ -49,9 +55,9 @@ bool ShowExtensionInstallDialogForManifest( scoped_refptr<Extension>* dummy_extension); // For use only in tests - sets a flag that makes invocations of -// ShowExtensionInstallDialogForManifest skip putting up a real dialog, and +// ShowExtensionInstallDialog* skip putting up a real dialog, and // instead act as if the dialog choice was to proceed or abort. -void SetExtensionInstallDialogForManifestAutoConfirmForTests( +void SetExtensionInstallDialogAutoConfirmForTests( bool should_proceed); #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_INSTALL_DIALOG_H_ diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc index 53906fa..361a4ae 100644 --- a/chrome/browser/extensions/extension_management_api.cc +++ b/chrome/browser/extensions/extension_management_api.cc @@ -7,14 +7,15 @@ #include <map> #include <string> -#include "base/bind.h" #include "base/basictypes.h" +#include "base/bind.h" #include "base/json/json_writer.h" #include "base/metrics/histogram.h" #include "base/string_number_conversions.h" #include "base/string_util.h" #include "chrome/browser/extensions/extension_event_names.h" #include "chrome/browser/extensions/extension_event_router.h" +#include "chrome/browser/extensions/extension_management_api_constants.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/profiles/profile.h" @@ -32,61 +33,45 @@ using base::IntToString; using content::BrowserThread; -namespace events = extension_event_names; -namespace { +namespace events = extension_event_names; +namespace keys = extension_management_api_constants; -const char kAppLaunchUrlKey[] = "appLaunchUrl"; -const char kDescriptionKey[] = "description"; -const char kEnabledKey[] = "enabled"; -const char kHomepageUrlKey[] = "homepageUrl"; -const char kIconsKey[] = "icons"; -const char kIdKey[] = "id"; -const char kIsAppKey[] = "isApp"; -const char kNameKey[] = "name"; -const char kOfflineEnabledKey[] = "offlineEnabled"; -const char kOptionsUrlKey[] = "optionsUrl"; -const char kPermissionsKey[] = "permissions"; -const char kMayDisableKey[] = "mayDisable"; -const char kSizeKey[] = "size"; -const char kUpdateUrlKey[] = "updateUrl"; -const char kUrlKey[] = "url"; -const char kVersionKey[] = "version"; - -const char kExtensionCreateError[] = - "Failed to create extension from manifest."; -const char kManifestParseError[] = "Failed to parse manifest."; -const char kNoExtensionError[] = "Failed to find extension with id *"; -const char kNotAnAppError[] = "Extension * is not an App"; -const char kUserCantDisableError[] = "Extension * can not be disabled by user"; +ExtensionService* ExtensionManagementFunction::service() { + return profile()->GetExtensionService(); } -ExtensionService* ExtensionManagementFunction::service() { +ExtensionService* AsyncExtensionManagementFunction::service() { return profile()->GetExtensionService(); } static DictionaryValue* CreateExtensionInfo(const Extension& extension, - bool enabled) { + bool enabled, + bool permissions_escalated) { DictionaryValue* info = new DictionaryValue(); - info->SetString(kIdKey, extension.id()); - info->SetBoolean(kIsAppKey, extension.is_app()); - info->SetString(kNameKey, extension.name()); - info->SetBoolean(kEnabledKey, enabled); - info->SetBoolean(kMayDisableKey, + info->SetString(keys::kIdKey, extension.id()); + info->SetBoolean(keys::kIsAppKey, extension.is_app()); + info->SetString(keys::kNameKey, extension.name()); + info->SetBoolean(keys::kEnabledKey, enabled); + if (!enabled) { + const char* reason = permissions_escalated ? + keys::kDisabledReasonPermissionsIncrease : keys::kDisabledReasonUnknown; + info->SetString(keys::kDisabledReasonKey, reason); + } + info->SetBoolean(keys::kMayDisableKey, Extension::UserMayDisable(extension.location())); - info->SetBoolean(kOfflineEnabledKey, extension.offline_enabled()); - info->SetString(kVersionKey, extension.VersionString()); - info->SetString(kDescriptionKey, extension.description()); - info->SetString(kOptionsUrlKey, + info->SetBoolean(keys::kOfflineEnabledKey, extension.offline_enabled()); + info->SetString(keys::kVersionKey, extension.VersionString()); + info->SetString(keys::kDescriptionKey, extension.description()); + info->SetString(keys::kOptionsUrlKey, extension.options_url().possibly_invalid_spec()); - info->SetString(kHomepageUrlKey, + info->SetString(keys::kHomepageUrlKey, extension.GetHomepageURL().possibly_invalid_spec()); - if (!extension.update_url().is_empty()) { - info->SetString(kUpdateUrlKey, + if (!extension.update_url().is_empty()) + info->SetString(keys::kUpdateUrlKey, extension.update_url().possibly_invalid_spec()); - } if (extension.is_app()) - info->SetString(kAppLaunchUrlKey, + info->SetString(keys::kAppLaunchUrlKey, extension.GetFullLaunchURL().possibly_invalid_spec()); const ExtensionIconSet::IconMap& icons = extension.icons().map(); @@ -98,8 +83,8 @@ static DictionaryValue* CreateExtensionInfo(const Extension& extension, Extension::Icons size = static_cast<Extension::Icons>(icon_iter->first); GURL url = ExtensionIconSource::GetIconURL( &extension, size, ExtensionIconSet::MATCH_EXACTLY, false, NULL); - icon_info->SetInteger(kSizeKey, icon_iter->first); - icon_info->SetString(kUrlKey, url.spec()); + icon_info->SetInteger(keys::kSizeKey, icon_iter->first); + icon_info->SetString(keys::kUrlKey, url.spec()); icon_list->Append(icon_info); } info->Set("icons", icon_list); @@ -139,7 +124,8 @@ static DictionaryValue* CreateExtensionInfo(const Extension& extension, static void AddExtensionInfo(ListValue* list, const ExtensionList& extensions, - bool enabled) { + bool enabled, + ExtensionPrefs* prefs) { for (ExtensionList::const_iterator i = extensions.begin(); i != extensions.end(); ++i) { const Extension& extension = **i; @@ -147,7 +133,9 @@ static void AddExtensionInfo(ListValue* list, if (extension.location() == Extension::COMPONENT) continue; // Skip built-in extensions. - list->Append(CreateExtensionInfo(extension, enabled)); + bool escalated = + prefs->DidExtensionEscalatePermissions(extension.id()); + list->Append(CreateExtensionInfo(extension, enabled, escalated)); } } @@ -155,8 +143,10 @@ bool GetAllExtensionsFunction::RunImpl() { ListValue* result = new ListValue(); result_.reset(result); - AddExtensionInfo(result, *service()->extensions(), true); - AddExtensionInfo(result, *service()->disabled_extensions(), false); + ExtensionPrefs* prefs = service()->extension_prefs(); + AddExtensionInfo(result, *service()->extensions(), true, prefs); + AddExtensionInfo( + result, *service()->disabled_extensions(), false, prefs); return true; } @@ -166,12 +156,14 @@ bool GetExtensionByIdFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); const Extension* extension = service()->GetExtensionById(extension_id, true); if (!extension) { - error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError, + error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError, extension_id); return false; } bool enabled = service()->IsExtensionEnabled(extension_id); - DictionaryValue* result = CreateExtensionInfo(*extension, enabled); + ExtensionPrefs* prefs = service()->extension_prefs(); + bool escalated = prefs->DidExtensionEscalatePermissions(extension_id); + DictionaryValue* result = CreateExtensionInfo(*extension, enabled, escalated); result_.reset(result); return true; @@ -183,7 +175,7 @@ bool GetPermissionWarningsByIdFunction::RunImpl() { const Extension* extension = service()->GetExtensionById(ext_id, true); if (!extension) { - error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError, + error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError, ext_id); return false; } @@ -242,7 +234,7 @@ class SafeManifestJSONParser : public UtilityProcessHost::Client { if (value->IsType(Value::TYPE_DICTIONARY)) parsed_manifest_.reset(static_cast<DictionaryValue*>(value)->DeepCopy()); else - error_ = kManifestParseError; + error_ = keys::kManifestParseError; utility_host_ = NULL; // has already deleted itself BrowserThread::PostTask( @@ -310,7 +302,7 @@ void GetPermissionWarningsByManifestFunction::OnParseSuccess( FilePath(), Extension::INVALID, *parsed_manifest, Extension::STRICT_ERROR_CHECKS, &error_); if (!extension.get()) { - OnParseFailure(kExtensionCreateError); + OnParseFailure(keys::kExtensionCreateError); return; } @@ -340,12 +332,12 @@ bool LaunchAppFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); const Extension* extension = service()->GetExtensionById(extension_id, true); if (!extension) { - error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError, + error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError, extension_id); return false; } if (!extension->is_app()) { - error_ = ExtensionErrorUtils::FormatErrorMessage(kNotAnAppError, + error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNotAnAppError, extension_id); return false; } @@ -365,40 +357,74 @@ bool LaunchAppFunction::RunImpl() { return true; } +SetEnabledFunction::SetEnabledFunction() {} + +SetEnabledFunction::~SetEnabledFunction() {} + bool SetEnabledFunction::RunImpl() { - std::string extension_id; bool enable; - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); + EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id_)); EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &enable)); - const Extension* extension = service()->GetExtensionById(extension_id, true); + const Extension* extension = service()->GetExtensionById(extension_id_, true); if (!extension) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kNoExtensionError, extension_id); + keys::kNoExtensionError, extension_id_); return false; } if (!Extension::UserMayDisable(extension->location())) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kUserCantDisableError, extension_id); + keys::kUserCantDisableError, extension_id_); return false; } - if (!service()->IsExtensionEnabled(extension_id) && enable) - service()->EnableExtension(extension_id); - else if (service()->IsExtensionEnabled(extension_id) && !enable) - service()->DisableExtension(extension_id); + bool currently_enabled = service()->IsExtensionEnabled(extension_id_); + + if (!currently_enabled && enable) { + ExtensionPrefs* prefs = service()->extension_prefs(); + if (prefs->DidExtensionEscalatePermissions(extension_id_)) { + if (!user_gesture()) { + error_ = keys::kGestureNeededForEscalationError; + return false; + } + AddRef(); // Matched in InstallUIProceed/InstallUIAbort + install_ui_.reset(new ExtensionInstallUI(profile_)); + install_ui_->ConfirmReEnable(this, extension); + return true; + } + service()->EnableExtension(extension_id_); + } else if (currently_enabled && !enable) { + service()->DisableExtension(extension_id_); + } + + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&SetEnabledFunction::SendResponse, this, true)); return true; } +void SetEnabledFunction::InstallUIProceed() { + service()->EnableExtension(extension_id_); + SendResponse(true); + Release(); +} + +void SetEnabledFunction::InstallUIAbort(bool user_initiated) { + error_ = keys::kUserDidNotReEnableError; + SendResponse(false); + Release(); +} + bool UninstallFunction::RunImpl() { std::string extension_id; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); if (!service()->GetExtensionById(extension_id, true)) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kNoExtensionError, extension_id); + keys::kNoExtensionError, extension_id); return false; } @@ -407,7 +433,7 @@ bool UninstallFunction::RunImpl() { if (!Extension::UserMayDisable( prefs->GetInstalledExtensionInfo(extension_id)->extension_location)) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kUserCantDisableError, extension_id); + keys::kUserCantDisableError, extension_id); return false; } @@ -477,8 +503,10 @@ void ExtensionManagementEventRouter::Observe( } CHECK(extension); ExtensionService* service = profile->GetExtensionService(); + ExtensionPrefs* prefs = service->extension_prefs(); bool enabled = service->GetExtensionById(extension->id(), false) != NULL; - args.Append(CreateExtensionInfo(*extension, enabled)); + bool escalated = prefs ->DidExtensionEscalatePermissions(extension->id()); + args.Append(CreateExtensionInfo(*extension, enabled, escalated)); } std::string args_json; diff --git a/chrome/browser/extensions/extension_management_api.h b/chrome/browser/extensions/extension_management_api.h index d73b407..b7e6b80 100644 --- a/chrome/browser/extensions/extension_management_api.h +++ b/chrome/browser/extensions/extension_management_api.h @@ -8,6 +8,7 @@ #include "base/compiler_specific.h" #include "chrome/browser/extensions/extension_function.h" +#include "chrome/browser/extensions/extension_install_ui.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" @@ -18,6 +19,11 @@ class ExtensionManagementFunction : public SyncExtensionFunction { ExtensionService* service(); }; +class AsyncExtensionManagementFunction : public AsyncExtensionFunction { + protected: + ExtensionService* service(); +}; + class GetAllExtensionsFunction : public ExtensionManagementFunction { virtual ~GetAllExtensionsFunction() {} virtual bool RunImpl() OVERRIDE; @@ -54,9 +60,24 @@ class LaunchAppFunction : public ExtensionManagementFunction { DECLARE_EXTENSION_FUNCTION_NAME("management.launchApp"); }; -class SetEnabledFunction : public ExtensionManagementFunction { - virtual ~SetEnabledFunction() {} +class SetEnabledFunction : public AsyncExtensionManagementFunction, + public ExtensionInstallUI::Delegate { + public: + SetEnabledFunction(); + virtual ~SetEnabledFunction(); virtual bool RunImpl() OVERRIDE; + + protected: + // ExtensionInstalUI::Delegate. + virtual void InstallUIProceed() OVERRIDE; + virtual void InstallUIAbort(bool user_initiated) OVERRIDE; + + private: + std::string extension_id_; + + // Used for prompting to re-enable items with permissions escalation updates. + scoped_ptr<ExtensionInstallUI> install_ui_; + DECLARE_EXTENSION_FUNCTION_NAME("management.setEnabled"); }; diff --git a/chrome/browser/extensions/extension_management_api_browsertest.cc b/chrome/browser/extensions/extension_management_api_browsertest.cc index e6a7c11..808b137 100644 --- a/chrome/browser/extensions/extension_management_api_browsertest.cc +++ b/chrome/browser/extensions/extension_management_api_browsertest.cc @@ -1,9 +1,21 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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/stringprintf.h" #include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/browser/extensions/extension_install_dialog.h" +#include "chrome/browser/extensions/extension_management_api.h" +#include "chrome/browser/extensions/extension_management_api_constants.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_notification_types.h" + +namespace keys = extension_management_api_constants; +namespace util = extension_function_test_utils; class ExtensionManagementApiBrowserTest : public ExtensionBrowserTest {}; @@ -43,3 +55,76 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, test_data_dir_.AppendASCII("management/launch_app_from_background"))); ASSERT_TRUE(listener1.WaitUntilSatisfied()); } + +class ExtensionManagementApiEscalationTest : public ExtensionBrowserTest { + protected: + // The id of the permissions escalation test extension we use. + static const char kId[]; + + virtual void SetUpOnMainThread() OVERRIDE { + ExtensionService* service = browser()->profile()->GetExtensionService(); + + // Install low-permission version of the extension. + ASSERT_TRUE(InstallExtension( + test_data_dir_.AppendASCII("permissions-low-v1.crx"), 1)); + EXPECT_TRUE(service->GetExtensionById(kId, false) != NULL); + + // Update to a high-permission version - it should get disabled. + EXPECT_TRUE(UpdateExtension( + kId, test_data_dir_.AppendASCII("permissions-high-v2.crx"), -1)); + EXPECT_TRUE(service->GetExtensionById(kId, false) == NULL); + EXPECT_TRUE(service->GetExtensionById(kId, true) != NULL); + EXPECT_TRUE( + service->extension_prefs()->DidExtensionEscalatePermissions(kId)); + } + + void ReEnable(bool user_gesture, const std::string& expected_error) { + scoped_refptr<SetEnabledFunction> function(new SetEnabledFunction); + if (user_gesture) + function->set_user_gesture(true); + bool response = util::RunAsyncFunction( + function.get(), + base::StringPrintf("[\"%s\", true]", kId), + browser(), + util::NONE); + if (expected_error.empty()) { + EXPECT_EQ(true, response); + } else { + EXPECT_EQ(false, response); + EXPECT_EQ(expected_error, function->GetError()); + } + } + +}; + +const char ExtensionManagementApiEscalationTest::kId[] = + "pgdpcfcocojkjfbgpiianjngphoopgmo"; + +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiEscalationTest, + DisabledReason) { + scoped_ptr<base::Value> result( + util::RunFunctionAndReturnResult(new GetExtensionByIdFunction(), + base::StringPrintf("[\"%s\"]", kId), + browser())); + ASSERT_TRUE(result.get() != NULL); + ASSERT_TRUE(result->IsType(base::Value::TYPE_DICTIONARY)); + base::DictionaryValue* dict = + static_cast<base::DictionaryValue*>(result.get()); + std::string reason; + EXPECT_TRUE(dict->GetStringASCII(keys::kDisabledReasonKey, &reason)); + EXPECT_EQ(reason, std::string(keys::kDisabledReasonPermissionsIncrease)); +} + +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiEscalationTest, + ReEnable) { + // Expect an error about no gesture. + ReEnable(false, keys::kGestureNeededForEscalationError); + + // Expect an error that user cancelled the dialog. + SetExtensionInstallDialogAutoConfirmForTests(false); + ReEnable(true, keys::kUserDidNotReEnableError); + + // This should succeed when user accepts dialog. + SetExtensionInstallDialogAutoConfirmForTests(true); + ReEnable(true, ""); +} diff --git a/chrome/browser/extensions/extension_management_api_constants.cc b/chrome/browser/extensions/extension_management_api_constants.cc new file mode 100644 index 0000000..42ec69e --- /dev/null +++ b/chrome/browser/extensions/extension_management_api_constants.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2011 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_management_api_constants.h" + +namespace extension_management_api_constants { + +const char kAppLaunchUrlKey[] = "appLaunchUrl"; +const char kDescriptionKey[] = "description"; +const char kEnabledKey[] = "enabled"; +const char kDisabledReasonKey[] = "disabledReason"; +const char kHomepageUrlKey[] = "homepageUrl"; +const char kIconsKey[] = "icons"; +const char kIdKey[] = "id"; +const char kIsAppKey[] = "isApp"; +const char kNameKey[] = "name"; +const char kOfflineEnabledKey[] = "offlineEnabled"; +const char kOptionsUrlKey[] = "optionsUrl"; +const char kPermissionsKey[] = "permissions"; +const char kMayDisableKey[] = "mayDisable"; +const char kSizeKey[] = "size"; +const char kUpdateUrlKey[] = "updateUrl"; +const char kUrlKey[] = "url"; +const char kVersionKey[] = "version"; + +const char kDisabledReasonPermissionsIncrease[] = "permissions_increase"; +const char kDisabledReasonUnknown[] = "unknown"; + +const char kExtensionCreateError[] = + "Failed to create extension from manifest."; +const char kGestureNeededForEscalationError[] = + "Re-enabling an extension disabled due to permissions increase " + "requires a user gesture"; +const char kManifestParseError[] = "Failed to parse manifest."; +const char kNoExtensionError[] = "Failed to find extension with id *"; +const char kNotAnAppError[] = "Extension * is not an App"; +const char kUserCantDisableError[] = "Extension * can not be disabled by user"; +const char kUserDidNotReEnableError[] = + "The user did not accept the re-enable dialog"; + +} // namespace extension_management_api_constants diff --git a/chrome/browser/extensions/extension_management_api_constants.h b/chrome/browser/extensions/extension_management_api_constants.h new file mode 100644 index 0000000..81c0077 --- /dev/null +++ b/chrome/browser/extensions/extension_management_api_constants.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011 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_MANAGEMENT_API_CONSTANTS_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_API_CONSTANTS_H_ +#pragma once + +namespace extension_management_api_constants { + +// Keys used for incoming arguments and outgoing JSON data. +extern const char kAppLaunchUrlKey[]; +extern const char kDescriptionKey[]; +extern const char kEnabledKey[]; +extern const char kDisabledReasonKey[]; +extern const char kHomepageUrlKey[]; +extern const char kIconsKey[]; +extern const char kIdKey[]; +extern const char kIsAppKey[]; +extern const char kNameKey[]; +extern const char kOfflineEnabledKey[]; +extern const char kOptionsUrlKey[]; +extern const char kPermissionsKey[]; +extern const char kMayDisableKey[]; +extern const char kSizeKey[]; +extern const char kUpdateUrlKey[]; +extern const char kUrlKey[]; +extern const char kVersionKey[]; + +// Values for outgoing JSON data. +extern const char kDisabledReasonPermissionsIncrease[]; +extern const char kDisabledReasonUnknown[]; + +// Error messages. +extern const char kExtensionCreateError[]; +extern const char kGestureNeededForEscalationError[]; +extern const char kManifestParseError[]; +extern const char kNoExtensionError[]; +extern const char kNotAnAppError[]; +extern const char kUserCantDisableError[]; +extern const char kUserDidNotReEnableError[]; + +} // namespace extension_management_api_constants + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_API_CONSTANTS_H_ diff --git a/chrome/browser/extensions/extension_webstore_private_apitest.cc b/chrome/browser/extensions/extension_webstore_private_apitest.cc index 18d49e4..f5d015b 100644 --- a/chrome/browser/extensions/extension_webstore_private_apitest.cc +++ b/chrome/browser/extensions/extension_webstore_private_apitest.cc @@ -93,7 +93,7 @@ class ExtensionWebstorePrivateApiTest : public ExtensionApiTest { // API functions. host_resolver()->AddRule("www.example.com", "127.0.0.1"); ASSERT_TRUE(test_server()->Start()); - SetExtensionInstallDialogForManifestAutoConfirmForTests(true); + SetExtensionInstallDialogAutoConfirmForTests(true); ExtensionInstallUI::DisableFailureUIForTests(); } @@ -182,7 +182,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallLocalized) { // Now test the case where the user cancels the confirmation dialog. IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallCancelled) { - SetExtensionInstallDialogForManifestAutoConfirmForTests(false); + SetExtensionInstallDialogAutoConfirmForTests(false); ASSERT_TRUE(RunInstallTest("cancelled.html", "extension.crx")); } diff --git a/chrome/browser/extensions/webstore_inline_install_browsertest.cc b/chrome/browser/extensions/webstore_inline_install_browsertest.cc index af1c80f..037fec5 100644 --- a/chrome/browser/extensions/webstore_inline_install_browsertest.cc +++ b/chrome/browser/extensions/webstore_inline_install_browsertest.cc @@ -80,7 +80,7 @@ class WebstoreInlineInstallTest : public InProcessBrowserTest { }; IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, Install) { - SetExtensionInstallDialogForManifestAutoConfirmForTests(true); + SetExtensionInstallDialogAutoConfirmForTests(true); ui_test_utils::NavigateToURL( browser(), GenerateTestServerUrl(kAppDomain, "install.html")); @@ -94,7 +94,7 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, Install) { IN_PROC_BROWSER_TEST_F( WebstoreInlineInstallTest, InstallNotAllowedFromNonVerifiedDomains) { - SetExtensionInstallDialogForManifestAutoConfirmForTests(false); + SetExtensionInstallDialogAutoConfirmForTests(false); ui_test_utils::NavigateToURL( browser(), GenerateTestServerUrl(kNonAppDomain, "install_non_verified_domain.html")); @@ -111,7 +111,7 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, FindLink) { } IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, ArgumentValidation) { - SetExtensionInstallDialogForManifestAutoConfirmForTests(false); + SetExtensionInstallDialogAutoConfirmForTests(false); ui_test_utils::NavigateToURL( browser(), GenerateTestServerUrl(kAppDomain, "argument_validation.html")); @@ -119,7 +119,7 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, ArgumentValidation) { } IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, InstallNotSupported) { - SetExtensionInstallDialogForManifestAutoConfirmForTests(false); + SetExtensionInstallDialogAutoConfirmForTests(false); ui_test_utils::NavigateToURL( browser(), GenerateTestServerUrl(kAppDomain, "install_not_supported.html")); @@ -156,7 +156,7 @@ class WebstoreInlineInstallUnpackFailureTest }; IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallUnpackFailureTest, Test) { - SetExtensionInstallDialogForManifestAutoConfirmForTests(true); + SetExtensionInstallDialogAutoConfirmForTests(true); ui_test_utils::NavigateToURL(browser(), GenerateTestServerUrl(kAppDomain, "install_unpack_failure.html")); diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm index c58e3b8..c67e774 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm @@ -288,7 +288,7 @@ void AppendRatingStarsShim(const SkBitmap* skiaImage, void* data) { @end // ExtensionInstallDialogController -void ShowExtensionInstallDialog( +void ShowExtensionInstallDialogImpl( Profile* profile, ExtensionInstallUI::Delegate* delegate, const Extension* extension, diff --git a/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc b/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc index af4694f..ea5ada3 100644 --- a/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc +++ b/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc @@ -234,7 +234,7 @@ void ExtensionInstallDialog::OnStoreLinkClick(GtkWidget* sender) { } // namespace -void ShowExtensionInstallDialog( +void ShowExtensionInstallDialogImpl( Profile* profile, ExtensionInstallUI::Delegate* delegate, const Extension* extension, diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc index 273a8ed..97f4517 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc @@ -303,7 +303,7 @@ void ExtensionInstallDialogView::LinkClicked(views::Link* source, GetWidget()->Close(); } -void ShowExtensionInstallDialog( +void ShowExtensionInstallDialogImpl( Profile* profile, ExtensionInstallUI::Delegate* delegate, const Extension* extension, diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 9bff73f..798c0dc 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1040,6 +1040,8 @@ 'browser/extensions/extension_install_ui.h', 'browser/extensions/extension_management_api.cc', 'browser/extensions/extension_management_api.h', + 'browser/extensions/extension_management_api_constants.cc', + 'browser/extensions/extension_management_api_constants.h', 'browser/extensions/extension_mediaplayer_private_api.cc', 'browser/extensions/extension_mediaplayer_private_api.h', 'browser/extensions/extension_menu_manager.cc', diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index c187dbe..08ddcd8 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -6895,6 +6895,12 @@ "description": "Whether it is currently enabled or disabled.", "type": "boolean" }, + "disabledReason": { + "description": "A reason the item is disabled.", + "type": "string", + "enum": ["unknown", "permissions_increase"], + "optional": true + }, "isApp": { "description": "True if this is an app.", "type": "boolean" diff --git a/chrome/common/extensions/docs/management.html b/chrome/common/extensions/docs/management.html index 0d6d195..c6fb5fe 100644 --- a/chrome/common/extensions/docs/management.html +++ b/chrome/common/extensions/docs/management.html @@ -3299,6 +3299,74 @@ The one method that doesn't require the "manifest" permission is </div><div> <div> <dt> + <var>disabledReason</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span>["unknown", "permissions_increase"]</span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>A reason the item is disabled.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> <var>isApp</var> <em> diff --git a/chrome/test/data/extensions/api_test/management/test/basics.js b/chrome/test/data/extensions/api_test/management/test/basics.js index 1223362..5ad7ff7 100644 --- a/chrome/test/data/extensions/api_test/management/test/basics.js +++ b/chrome/test/data/extensions/api_test/management/test/basics.js @@ -54,11 +54,13 @@ var tests = [ { "appLaunchUrl": "http://www.google.com/", "offlineEnabled": true, "updateUrl": "http://example.com/update.xml" }); - checkItemInList(items, "disabled_app", false, true); + checkItemInList(items, "disabled_app", false, true, + { "disabledReason": "unknown" }); checkItemInList(items, "enabled_extension", true, false, { "homepageUrl": "http://example.com/" }); checkItemInList(items, "disabled_extension", false, false, - { "optionsUrl": "chrome-extension://<ID>/pages/options.html" }); + { "optionsUrl": "chrome-extension://<ID>/pages/options.html", + "disabledReason": "unknown" }); // Check that we got the icons correctly var extension = getItemNamed(items, "enabled_extension"); |