summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/common/extensions/api/extension_api.json6
-rw-r--r--chrome/common/extensions/docs/management.html68
-rw-r--r--chrome/test/data/extensions/api_test/management/test/basics.js6
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");