summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorasargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-16 22:31:32 +0000
committerasargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-16 22:31:32 +0000
commitd8c8749b93af8d5b8b2a761313673539234680ef (patch)
treec545342aa8c464585b6801ba53ec223096b2ff02 /chrome/browser
parentdebb04d07991d559cdd7d24107e3f4f862015d92 (diff)
downloadchromium_src-d8c8749b93af8d5b8b2a761313673539234680ef.zip
chromium_src-d8c8749b93af8d5b8b2a761313673539234680ef.tar.gz
chromium_src-d8c8749b93af8d5b8b2a761313673539234680ef.tar.bz2
Fix for management API related to escalated permissions disabled extensions
The general approach is to require user gesture and put up the confirmation UI if an extension wants to re-enable a disabled-due-to-permissions-escalation extension. Also added some new infrastructure for making this easier to test. (This is re-landing http://codereview.chromium.org/8423069/ which had a compile error on the cros_tegra2 bot) BUG=102579 TEST=See bug TBR=aa@chromium.org Review URL: http://codereview.chromium.org/8488012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110376 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/extensions/extension_function.cc9
-rw-r--r--chrome/browser/extensions/extension_function.h16
-rw-r--r--chrome/browser/extensions/extension_function_test_utils.cc55
-rw-r--r--chrome/browser/extensions/extension_function_test_utils.h8
-rw-r--r--chrome/browser/extensions/extension_install_dialog.cc44
-rw-r--r--chrome/browser/extensions/extension_install_dialog.h12
-rw-r--r--chrome/browser/extensions/extension_management_api.cc162
-rw-r--r--chrome/browser/extensions/extension_management_api.h25
-rw-r--r--chrome/browser/extensions/extension_management_api_browsertest.cc87
-rw-r--r--chrome/browser/extensions/extension_management_api_constants.cc42
-rw-r--r--chrome/browser/extensions/extension_management_api_constants.h45
-rw-r--r--chrome/browser/extensions/extension_webstore_private_apitest.cc4
-rw-r--r--chrome/browser/extensions/webstore_inline_install_browsertest.cc10
-rw-r--r--chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm2
-rw-r--r--chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc2
-rw-r--r--chrome/browser/ui/views/extensions/extension_install_dialog_view.cc2
16 files changed, 434 insertions, 91 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..d846778 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_TRUE(response == false);
+ 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,