diff options
author | jstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-27 18:42:31 +0000 |
---|---|---|
committer | jstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-27 18:42:31 +0000 |
commit | 902fd7bbba5683135ec25fe83479b13ab532891a (patch) | |
tree | a6f2a4eb6b7a4349bc6fbaae0e419c715046a069 /chrome/browser | |
parent | 3a91c5b51bc1890ecf3a558dbd51e3da68c418ca (diff) | |
download | chromium_src-902fd7bbba5683135ec25fe83479b13ab532891a.zip chromium_src-902fd7bbba5683135ec25fe83479b13ab532891a.tar.gz chromium_src-902fd7bbba5683135ec25fe83479b13ab532891a.tar.bz2 |
Re-land the experimental permissions API for extensions.
The permissions API lets extensions specify optional permissions in their manifest that they can request at run-time. It currently supports API permissions through a white-list. Host permissions will come later. This also fixes some clang errors from the previous attempt.
BUG=48119, 70466, 84507
TEST=*Extension*
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=94288
Review URL: http://codereview.chromium.org/7432006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@94326 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
23 files changed, 950 insertions, 128 deletions
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index 44a3b5b..d1a523f 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -4111,7 +4111,7 @@ ListValue* GetHostPermissions(const Extension* ext, bool effective_perm) { if (effective_perm) pattern_set = ext->GetEffectiveHostPermissions(); else - pattern_set = ext->permission_set()->explicit_hosts(); + pattern_set = ext->GetActivePermissions()->explicit_hosts(); ListValue* permissions = new ListValue; for (URLPatternSet::const_iterator perm = pattern_set.begin(); @@ -4124,7 +4124,8 @@ ListValue* GetHostPermissions(const Extension* ext, bool effective_perm) { ListValue* GetAPIPermissions(const Extension* ext) { ListValue* permissions = new ListValue; - std::set<std::string> perm_list = ext->permission_set()->GetAPIsAsStrings(); + std::set<std::string> perm_list = + ext->GetActivePermissions()->GetAPIsAsStrings(); for (std::set<std::string>::const_iterator perm = perm_list.begin(); perm != perm_list.end(); ++perm) { permissions->Append(new StringValue(perm->c_str())); diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc index c44362c..33eecf1 100644 --- a/chrome/browser/background/background_mode_manager.cc +++ b/chrome/browser/background/background_mode_manager.cc @@ -196,13 +196,15 @@ void BackgroundModeManager::RegisterProfile(Profile* profile) { profile, this)); background_mode_data_[profile] = bmd; - // Listen for when extensions are loaded/unloaded so we can track the - // number of background apps and modify our keep-alive and launch-on-startup - // state appropriately. + // Listen for when extensions are loaded/unloaded or add/remove the + // background permission so we can track the number of background apps and + // modify our keep-alive and launch-on-startup state appropriately. registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, Source<Profile>(profile)); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, Source<Profile>(profile)); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED, + Source<Profile>(profile)); // Check for the presence of background apps after all extensions have been // loaded, to handle the case where an extension has been manually removed @@ -274,6 +276,22 @@ void BackgroundModeManager::Observe(int type, OnBackgroundAppUninstalled(); } break; + case chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED: { + UpdatedExtensionPermissionsInfo* info = + Details<UpdatedExtensionPermissionsInfo>(details).ptr(); + if (!info->permissions->HasAPIPermission( + ExtensionAPIPermission::kBackground)) + break; + + if (info->reason == UpdatedExtensionPermissionsInfo::ADDED) { + OnBackgroundAppInstalled(info->extension); + OnBackgroundAppLoaded(); + } else { + OnBackgroundAppUnloaded(); + OnBackgroundAppUninstalled(); + } + } + break; case content::NOTIFICATION_APP_TERMINATING: // Make sure we aren't still keeping the app alive (only happens if we // don't receive an EXTENSIONS_READY notification for some reason). diff --git a/chrome/browser/extensions/convert_web_app_browsertest.cc b/chrome/browser/extensions/convert_web_app_browsertest.cc index a8405b1..7fe6f94 100644 --- a/chrome/browser/extensions/convert_web_app_browsertest.cc +++ b/chrome/browser/extensions/convert_web_app_browsertest.cc @@ -70,7 +70,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionFromWebAppTest, Basic) { EXPECT_EQ(extension_misc::LAUNCH_PANEL, installed_extension_->launch_container()); - ASSERT_EQ(2u, installed_extension_->permission_set()->apis().size()); + ASSERT_EQ(2u, installed_extension_->GetActivePermissions()->apis().size()); EXPECT_TRUE(installed_extension_->HasAPIPermission( ExtensionAPIPermission::kGeolocation)); EXPECT_TRUE(installed_extension_->HasAPIPermission( diff --git a/chrome/browser/extensions/convert_web_app_unittest.cc b/chrome/browser/extensions/convert_web_app_unittest.cc index 0f3b537..640b31b 100644 --- a/chrome/browser/extensions/convert_web_app_unittest.cc +++ b/chrome/browser/extensions/convert_web_app_unittest.cc @@ -123,7 +123,7 @@ TEST(ExtensionFromWebApp, Basic) { EXPECT_EQ(UTF16ToUTF8(web_app.title), extension->name()); EXPECT_EQ(UTF16ToUTF8(web_app.description), extension->description()); EXPECT_EQ(web_app.app_url, extension->GetFullLaunchURL()); - EXPECT_EQ(2u, extension->permission_set()->apis().size()); + EXPECT_EQ(2u, extension->GetActivePermissions()->apis().size()); EXPECT_TRUE(extension->HasAPIPermission("geolocation")); EXPECT_TRUE(extension->HasAPIPermission("notifications")); ASSERT_EQ(1u, extension->web_extent().patterns().size()); @@ -167,7 +167,7 @@ TEST(ExtensionFromWebApp, Minimal) { EXPECT_EQ("", extension->description()); EXPECT_EQ(web_app.app_url, extension->GetFullLaunchURL()); EXPECT_EQ(0u, extension->icons().map().size()); - EXPECT_EQ(0u, extension->permission_set()->apis().size()); + EXPECT_EQ(0u, extension->GetActivePermissions()->apis().size()); ASSERT_EQ(1u, extension->web_extent().patterns().size()); EXPECT_EQ("*://aaronboodman.com/*", extension->web_extent().patterns().begin()->GetAsString()); diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index c8cbf9e..15529b2 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -32,6 +32,7 @@ #include "chrome/browser/extensions/extension_module.h" #include "chrome/browser/extensions/extension_omnibox_api.h" #include "chrome/browser/extensions/extension_page_actions_module.h" +#include "chrome/browser/extensions/extension_permissions_api.h" #include "chrome/browser/extensions/extension_preference_api.h" #include "chrome/browser/extensions/extension_processes_api.h" #include "chrome/browser/extensions/extension_proxy_api.h" @@ -393,6 +394,12 @@ void FactoryRegistry::ResetFunctions() { // Experimental App API. RegisterFunction<AppNotifyFunction>(); RegisterFunction<AppClearAllNotificationsFunction>(); + + // Permissions + RegisterFunction<ContainsPermissionsFunction>(); + RegisterFunction<GetAllPermissionsFunction>(); + RegisterFunction<RemovePermissionsFunction>(); + RegisterFunction<RequestPermissionsFunction>(); } void FactoryRegistry::GetAllNames(std::vector<std::string>* names) { diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc index 62d904a..bdbd839 100644 --- a/chrome/browser/extensions/extension_install_ui.cc +++ b/chrome/browser/extensions/extension_install_ui.cc @@ -39,22 +39,32 @@ // static const int ExtensionInstallUI::kTitleIds[NUM_PROMPT_TYPES] = { IDS_EXTENSION_INSTALL_PROMPT_TITLE, - IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE + IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE, + IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE }; // static const int ExtensionInstallUI::kHeadingIds[NUM_PROMPT_TYPES] = { IDS_EXTENSION_INSTALL_PROMPT_HEADING, - IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING + IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING, + IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING }; // static const int ExtensionInstallUI::kButtonIds[NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_INSTALL_BUTTON, - IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON + IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON, + IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON +}; +// static +const int ExtensionInstallUI::kAbortButtonIds[NUM_PROMPT_TYPES] = { + 0, + 0, + IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON }; // static const int ExtensionInstallUI::kWarningIds[NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, - IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO + IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO, + IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO, }; namespace { @@ -112,6 +122,7 @@ void ExtensionInstallUI::ConfirmInstall(Delegate* delegate, const Extension* extension) { DCHECK(ui_loop_ == MessageLoop::current()); extension_ = extension; + permissions_ = extension->GetActivePermissions(); delegate_ = delegate; // We special-case themes to not show any confirm UI. Instead they are @@ -129,11 +140,24 @@ void ExtensionInstallUI::ConfirmReEnable(Delegate* delegate, const Extension* extension) { DCHECK(ui_loop_ == MessageLoop::current()); extension_ = extension; + permissions_ = extension->GetActivePermissions(); delegate_ = delegate; ShowConfirmation(RE_ENABLE_PROMPT); } +void ExtensionInstallUI::ConfirmPermissions( + Delegate* delegate, + const Extension* extension, + const ExtensionPermissionSet* permissions) { + DCHECK(ui_loop_ == MessageLoop::current()); + extension_ = extension; + permissions_ = permissions; + delegate_ = delegate; + + ShowConfirmation(PERMISSIONS_PROMPT); +} + void ExtensionInstallUI::OnInstallSuccess(const Extension* extension, SkBitmap* icon) { extension_ = extension; @@ -193,6 +217,7 @@ void ExtensionInstallUI::OnImageLoaded( SetIcon(image); switch (prompt_type_) { + case PERMISSIONS_PROMPT: case RE_ENABLE_PROMPT: case INSTALL_PROMPT: { // TODO(jcivelli): http://crbug.com/44771 We should not show an install @@ -203,7 +228,7 @@ void ExtensionInstallUI::OnImageLoaded( NotificationService::NoDetails()); std::vector<string16> warnings = - extension_->GetPermissionMessageStrings(); + permissions_->GetWarningMessages(); ShowExtensionInstallDialog( profile_, delegate_, extension_, &icon_, warnings, prompt_type_); break; diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h index 222f6a3..19965e5 100644 --- a/chrome/browser/extensions/extension_install_ui.h +++ b/chrome/browser/extensions/extension_install_ui.h @@ -16,6 +16,7 @@ #include "ui/gfx/native_widget_types.h" class Extension; +class ExtensionPermissionSet; class MessageLoop; class Profile; class InfoBarDelegate; @@ -28,6 +29,7 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { UNSET_PROMPT_TYPE = -1, INSTALL_PROMPT = 0, RE_ENABLE_PROMPT, + PERMISSIONS_PROMPT, NUM_PROMPT_TYPES }; @@ -36,6 +38,7 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { static const int kHeadingIds[NUM_PROMPT_TYPES]; static const int kButtonIds[NUM_PROMPT_TYPES]; static const int kWarningIds[NUM_PROMPT_TYPES]; + static const int kAbortButtonIds[NUM_PROMPT_TYPES]; class Delegate { public: @@ -65,6 +68,14 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { // We *MUST* eventually call either Proceed() or Abort() on |delegate|. virtual void ConfirmReEnable(Delegate* delegate, const Extension* extension); + // This is called by the extension permissions API to verify whether an + // extension may be granted additional permissions. + // + // We *MUST* eventually call either Proceed() or Abort() on |delegate|. + virtual void ConfirmPermissions(Delegate* delegate, + const Extension* extension, + const ExtensionPermissionSet* permissions); + // Installation was successful. This is declared virtual for testing. virtual void OnInstallSuccess(const Extension* extension, SkBitmap* icon); @@ -122,6 +133,9 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { // The extension we are showing the UI for. const Extension* extension_; + // The permissions being prompted for. + scoped_refptr<const ExtensionPermissionSet> permissions_; + // The delegate we will call Proceed/Abort on after confirmation UI. Delegate* delegate_; diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc index 3955361..8037f56 100644 --- a/chrome/browser/extensions/extension_management_api.cc +++ b/chrome/browser/extensions/extension_management_api.cc @@ -92,7 +92,7 @@ static DictionaryValue* CreateExtensionInfo(const Extension& extension, } const std::set<std::string> perms = - extension.permission_set()->GetAPIsAsStrings(); + extension.GetActivePermissions()->GetAPIsAsStrings(); ListValue* permission_list = new ListValue(); if (!perms.empty()) { std::set<std::string>::const_iterator perms_iter; @@ -107,7 +107,7 @@ static DictionaryValue* CreateExtensionInfo(const Extension& extension, if (!extension.is_hosted_app()) { // Skip host permissions for hosted apps. const URLPatternSet host_perms = - extension.permission_set()->explicit_hosts(); + extension.GetActivePermissions()->explicit_hosts(); if (!host_perms.is_empty()) { URLPatternSet::const_iterator host_perms_iter; for (host_perms_iter = host_perms.begin(); diff --git a/chrome/browser/extensions/extension_permissions_api.cc b/chrome/browser/extensions/extension_permissions_api.cc new file mode 100644 index 0000000..87a413b --- /dev/null +++ b/chrome/browser/extensions/extension_permissions_api.cc @@ -0,0 +1,378 @@ +// 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_permissions_api.h" + +#include "base/json/json_writer.h" +#include "base/stringprintf.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_event_router.h" +#include "chrome/browser/extensions/extension_permissions_api_constants.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_messages.h" +#include "chrome/common/extensions/extension_permission_set.h" +#include "chrome/common/extensions/url_pattern_set.h" +#include "content/common/notification_service.h" +#include "googleurl/src/gurl.h" + + +namespace keys = extension_permissions_module_constants; + +namespace { + +enum AutoConfirmForTest { + DO_NOT_SKIP = 0, + PROCEED, + ABORT +}; +AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP; + +DictionaryValue* PackPermissionsToValue(const ExtensionPermissionSet* set) { + DictionaryValue* value = new DictionaryValue(); + + // Generate the list of API permissions. + ListValue* apis = new ListValue(); + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (ExtensionAPIPermissionSet::const_iterator i = set->apis().begin(); + i != set->apis().end(); ++i) + apis->Append(Value::CreateStringValue(info->GetByID(*i)->name())); + + // TODO(jstritar): Include hosts once the API supports them. At that point, + // we could also shared this code with ExtensionPermissionSet methods in + // ExtensionPrefs. + + value->Set(keys::kApisKey, apis); + return value; +} + +// Creates a new ExtensionPermissionSet from its |value| and passes ownership to +// the caller through |ptr|. Sets |bad_message| to true if the message is badly +// formed. Returns false if the method fails to unpack a permission set. +bool UnpackPermissionsFromValue(DictionaryValue* value, + scoped_refptr<ExtensionPermissionSet>* ptr, + bool* bad_message, + std::string* error) { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + ExtensionAPIPermissionSet apis; + if (value->HasKey(keys::kApisKey)) { + ListValue* api_list = NULL; + if (!value->GetList(keys::kApisKey, &api_list)) { + *bad_message = true; + return false; + } + for (size_t i = 0; i < api_list->GetSize(); ++i) { + std::string api_name; + if (!api_list->GetString(i, &api_name)) { + *bad_message = true; + return false; + } + + ExtensionAPIPermission* permission = info->GetByName(api_name); + if (!permission) { + *error = base::StringPrintf( + keys::kUnknownPermissionError, api_name.c_str()); + return false; + } + apis.insert(permission->id()); + } + } + + // Ignore host permissions for now. + URLPatternSet empty_set; + *ptr = new ExtensionPermissionSet(apis, empty_set, empty_set); + return true; +} + +} // namespace + +ExtensionPermissionsManager::ExtensionPermissionsManager( + ExtensionService* extension_service) + : extension_service_(extension_service) { + RegisterWhitelist(); +} + +ExtensionPermissionsManager::~ExtensionPermissionsManager() { +} + +void ExtensionPermissionsManager::AddPermissions( + const Extension* extension, const ExtensionPermissionSet* permissions) { + scoped_refptr<const ExtensionPermissionSet> existing( + extension->GetActivePermissions()); + scoped_refptr<ExtensionPermissionSet> total( + ExtensionPermissionSet::CreateUnion(existing, permissions)); + scoped_refptr<ExtensionPermissionSet> added( + ExtensionPermissionSet::CreateDifference(total.get(), existing)); + + extension_service_->UpdateActivePermissions(extension, total.get()); + + // Update the granted permissions so we don't auto-disable the extension. + extension_service_->GrantPermissions(extension); + + NotifyPermissionsUpdated(extension, total.get(), added.get(), ADDED); +} + +void ExtensionPermissionsManager::RemovePermissions( + const Extension* extension, const ExtensionPermissionSet* permissions) { + scoped_refptr<const ExtensionPermissionSet> existing( + extension->GetActivePermissions()); + scoped_refptr<ExtensionPermissionSet> total( + ExtensionPermissionSet::CreateDifference(existing, permissions)); + scoped_refptr<ExtensionPermissionSet> removed( + ExtensionPermissionSet::CreateDifference(existing, total.get())); + + // We update the active permissions, and not the granted permissions, because + // the extension, not the user, removed the permissions. This allows the + // extension to add them again without prompting the user. + extension_service_->UpdateActivePermissions(extension, total.get()); + + NotifyPermissionsUpdated(extension, total.get(), removed.get(), REMOVED); +} + +void ExtensionPermissionsManager::DispatchEvent( + const std::string& extension_id, + const char* event_name, + const ExtensionPermissionSet* changed_permissions) { + Profile* profile = extension_service_->profile(); + if (profile && profile->GetExtensionEventRouter()) { + ListValue value; + value.Append(PackPermissionsToValue(changed_permissions)); + std::string json_value; + base::JSONWriter::Write(&value, false, &json_value); + profile->GetExtensionEventRouter()->DispatchEventToExtension( + extension_id, event_name, json_value, profile, GURL()); + } +} + +void ExtensionPermissionsManager::NotifyPermissionsUpdated( + const Extension* extension, + const ExtensionPermissionSet* active, + const ExtensionPermissionSet* changed, + EventType event_type) { + if (!changed || changed->IsEmpty()) + return; + + UpdatedExtensionPermissionsInfo::Reason reason; + const char* event_name = NULL; + + if (event_type == REMOVED) { + reason = UpdatedExtensionPermissionsInfo::REMOVED; + event_name = keys::kOnRemoved; + } else { + CHECK_EQ(ADDED, event_type); + reason = UpdatedExtensionPermissionsInfo::ADDED; + event_name = keys::kOnAdded; + } + + // Notify other APIs or interested parties. + UpdatedExtensionPermissionsInfo info = UpdatedExtensionPermissionsInfo( + extension, changed, reason); + NotificationService::current()->Notify( + chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED, + Source<Profile>(extension_service_->profile()), + Details<UpdatedExtensionPermissionsInfo>(&info)); + + // Trigger the onAdded and onRemoved events in the extension. + DispatchEvent(extension->id(), event_name, changed); + + // Send the new permissions to the renderers. + for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator()); + !i.IsAtEnd(); i.Advance()) { + RenderProcessHost* host = i.GetCurrentValue(); + if (extension_service_->profile()->IsSameProfile(host->profile())) + host->Send(new ExtensionMsg_UpdatePermissions( + extension->id(), + active->apis(), + active->explicit_hosts(), + active->scriptable_hosts())); + } +} + +void ExtensionPermissionsManager::RegisterWhitelist() { + // TODO(jstritar): This could be a field on ExtensionAPIPermission. + ExtensionAPIPermissionSet api_whitelist; + api_whitelist.insert(ExtensionAPIPermission::kClipboardRead); + api_whitelist.insert(ExtensionAPIPermission::kClipboardWrite); + api_whitelist.insert(ExtensionAPIPermission::kNotification); + api_whitelist.insert(ExtensionAPIPermission::kBookmark); + api_whitelist.insert(ExtensionAPIPermission::kContextMenus); + api_whitelist.insert(ExtensionAPIPermission::kCookie); + api_whitelist.insert(ExtensionAPIPermission::kDebugger); + api_whitelist.insert(ExtensionAPIPermission::kHistory); + api_whitelist.insert(ExtensionAPIPermission::kIdle); + api_whitelist.insert(ExtensionAPIPermission::kTab); + api_whitelist.insert(ExtensionAPIPermission::kManagement); + api_whitelist.insert(ExtensionAPIPermission::kBackground); + whitelist_ = new ExtensionPermissionSet( + api_whitelist, URLPatternSet(), URLPatternSet()); +} + +bool ContainsPermissionsFunction::RunImpl() { + DictionaryValue* args = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); + std::string error; + if (!args) + return false; + + scoped_refptr<ExtensionPermissionSet> permissions; + if (!UnpackPermissionsFromValue(args, &permissions, &bad_message_, &error_)) + return false; + CHECK(permissions.get()); + + result_.reset(Value::CreateBooleanValue( + GetExtension()->GetActivePermissions()->Contains(*permissions))); + return true; +} + +bool GetAllPermissionsFunction::RunImpl() { + result_.reset(PackPermissionsToValue( + GetExtension()->GetActivePermissions())); + return true; +} + +bool RemovePermissionsFunction::RunImpl() { + DictionaryValue* args = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); + if (!args) + return false; + + scoped_refptr<ExtensionPermissionSet> permissions; + if (!UnpackPermissionsFromValue(args, &permissions, &bad_message_, &error_)) + return false; + CHECK(permissions.get()); + + const Extension* extension = GetExtension(); + ExtensionPermissionsManager* perms_manager = + profile()->GetExtensionService()->permissions_manager(); + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + + // Make sure they're only trying to remove permissions supported by this API. + scoped_refptr<ExtensionPermissionSet> unsupported( + ExtensionPermissionSet::CreateDifference( + permissions.get(), &perms_manager->whitelist())); + if (unsupported->apis().size()) { + std::string api_name = info->GetByID(*unsupported->apis().begin())->name(); + error_ = base::StringPrintf(keys::kNotWhitelistedError, api_name.c_str()); + return false; + } + + // Make sure we don't remove any required pemissions. + const ExtensionPermissionSet* required = extension->required_permission_set(); + scoped_refptr<ExtensionPermissionSet> intersection( + ExtensionPermissionSet::CreateIntersection(permissions.get(), required)); + if (!intersection->IsEmpty()) { + error_ = keys::kCantRemoveRequiredPermissionsError; + result_.reset(Value::CreateBooleanValue(false)); + return false; + } + + perms_manager->RemovePermissions(extension, permissions.get()); + result_.reset(Value::CreateBooleanValue(true)); + return true; +} + +// static +void RequestPermissionsFunction::SetAutoConfirmForTests(bool should_proceed) { + auto_confirm_for_tests = should_proceed ? PROCEED : ABORT; +} + +RequestPermissionsFunction::RequestPermissionsFunction() {} +RequestPermissionsFunction::~RequestPermissionsFunction() {} + +bool RequestPermissionsFunction::RunImpl() { + DictionaryValue* args = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); + if (!args) + return false; + + if (!UnpackPermissionsFromValue( + args, &requested_permissions_, &bad_message_, &error_)) + return false; + CHECK(requested_permissions_.get()); + + extension_ = GetExtension(); + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + ExtensionPermissionsManager* perms_manager = + profile()->GetExtensionService()->permissions_manager(); + ExtensionPrefs* prefs = profile()->GetExtensionService()->extension_prefs(); + + // Make sure only white listed permissions have been requested. + scoped_refptr<ExtensionPermissionSet> unsupported( + ExtensionPermissionSet::CreateDifference( + requested_permissions_.get(), &perms_manager->whitelist())); + if (unsupported->apis().size()) { + std::string api_name = info->GetByID(*unsupported->apis().begin())->name(); + error_ = base::StringPrintf(keys::kNotWhitelistedError, api_name.c_str()); + return false; + } + + // The requested permissions must be defined as optional in the manifest. + if (!extension_->optional_permission_set()->Contains( + *requested_permissions_)) { + error_ = keys::kNotInOptionalPermissionsError; + result_.reset(Value::CreateBooleanValue(false)); + return false; + } + + // We don't need to prompt the user if the requested permissions are a subset + // of the granted permissions set. + const ExtensionPermissionSet* granted = + prefs->GetGrantedPermissions(extension_->id()); + if (granted && granted->Contains(*requested_permissions_)) { + perms_manager->AddPermissions(extension_, requested_permissions_.get()); + result_.reset(Value::CreateBooleanValue(true)); + SendResponse(true); + return true; + } + + // Filter out the granted permissions so we only prompt for new ones. + requested_permissions_ = ExtensionPermissionSet::CreateDifference( + requested_permissions_.get(), granted); + + // Balanced with Release() in InstallUIProceed() and InstallUIAbort(). + AddRef(); + + // We don't need to show the prompt if there are no new warnings, or if + // we're skipping the confirmation UI. All extension types but INTERNAL + // are allowed to silently increase their permission level. + if (auto_confirm_for_tests == PROCEED || + requested_permissions_->GetWarningMessages().size() == 0) { + InstallUIProceed(); + } else if (auto_confirm_for_tests == ABORT) { + // Pretend the user clicked cancel. + InstallUIAbort(true); + } else { + CHECK_EQ(DO_NOT_SKIP, auto_confirm_for_tests); + install_ui_.reset(new ExtensionInstallUI(profile())); + install_ui_->ConfirmPermissions( + this, extension_, requested_permissions_.get()); + } + + return true; +} + +void RequestPermissionsFunction::InstallUIProceed() { + ExtensionPermissionsManager* perms_manager = + profile()->GetExtensionService()->permissions_manager(); + + install_ui_.reset(); + result_.reset(Value::CreateBooleanValue(true)); + perms_manager->AddPermissions(extension_, requested_permissions_.get()); + + SendResponse(true); + + Release(); +} + +void RequestPermissionsFunction::InstallUIAbort(bool user_initiated) { + install_ui_.reset(); + result_.reset(Value::CreateBooleanValue(false)); + requested_permissions_ = NULL; + + SendResponse(true); + Release(); +} diff --git a/chrome/browser/extensions/extension_permissions_api.h b/chrome/browser/extensions/extension_permissions_api.h new file mode 100644 index 0000000..35da6ca --- /dev/null +++ b/chrome/browser/extensions/extension_permissions_api.h @@ -0,0 +1,119 @@ +// 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_PERMISSIONS_API_H__ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_H__ +#pragma once + +#include "chrome/browser/extensions/extension_function.h" +#include "chrome/browser/extensions/extension_install_ui.h" +#include "chrome/common/extensions/extension_permission_set.h" +#include "chrome/common/chrome_notification_types.h" +#include "content/browser/renderer_host/render_process_host.h" +#include "content/common/notification_service.h" + +namespace base { +class DictionaryValue; +} +class Extension; +class ExtensionPermissionSet; +class ExtensionService; +class Profile; + +class ExtensionPermissionsManager { + public: + explicit ExtensionPermissionsManager(ExtensionService* extension_service); + ~ExtensionPermissionsManager(); + + // Adds the set of |permissions| to the |extension|'s active permission set + // and sends the relevant messages and notifications. This method assumes the + // user has already been prompted, if necessary, for the extra permissions. + void AddPermissions(const Extension* extension, + const ExtensionPermissionSet* permissions); + + // Removes the set of |permissions| from the |extension|'s active permission + // set and sends the relevant messages and notifications. + void RemovePermissions(const Extension* extension, + const ExtensionPermissionSet* permissions); + + // Returns the list of API permissions that are supported by the optional + // permissions API. + const ExtensionPermissionSet& whitelist() const { return *whitelist_; } + + private: + enum EventType { + ADDED, + REMOVED, + }; + + // Dispatches specified event to the extension. + void DispatchEvent(const std::string& extension_id, + const char* event_name, + const ExtensionPermissionSet* changed_permissions); + + // Issues the relevant events, messages and notifications when the permissions + // have changed for the |extension| (|changed| is the permission delta, while + // |active| is the new permission set). Specifically, this sends the + // EXTENSION_PERMISSIONS_UPDATED notification, the + // ExtensionMsg_UpdatePermissions IPC message, and fires the onAdded/onRemoved + // events in the extension. + void NotifyPermissionsUpdated(const Extension* extension, + const ExtensionPermissionSet* active, + const ExtensionPermissionSet* changed, + EventType event_type); + + // Registers the list of APIs supported by the optional permissions API. + void RegisterWhitelist(); + + ExtensionService* extension_service_; + scoped_refptr<ExtensionPermissionSet> whitelist_; +}; + + +// chrome.permissions.contains +class ContainsPermissionsFunction : public SyncExtensionFunction { + virtual ~ContainsPermissionsFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("experimental.permissions.contains") +}; + +// chrome.permissions.getAll +class GetAllPermissionsFunction : public SyncExtensionFunction { + virtual ~GetAllPermissionsFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("experimental.permissions.getAll") +}; + +// chrome.permissions.remove +class RemovePermissionsFunction : public SyncExtensionFunction { + virtual ~RemovePermissionsFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("experimental.permissions.remove") +}; + +// chrome.permissions.request +class RequestPermissionsFunction : public AsyncExtensionFunction, + public ExtensionInstallUI::Delegate { + public: + // FOR TESTS ONLY to bypass the confirmation UI. + static void SetAutoConfirmForTests(bool should_proceed); + + RequestPermissionsFunction(); + + // Implementing ExtensionInstallUI::Delegate interface. + virtual void InstallUIProceed() OVERRIDE; + virtual void InstallUIAbort(bool user_initiated) OVERRIDE; + + protected: + virtual ~RequestPermissionsFunction(); + virtual bool RunImpl() OVERRIDE; + + private: + scoped_ptr<ExtensionInstallUI> install_ui_; + scoped_refptr<ExtensionPermissionSet> requested_permissions_; + const Extension* extension_; + DECLARE_EXTENSION_FUNCTION_NAME("experimental.permissions.request") +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_H__ diff --git a/chrome/browser/extensions/extension_permissions_api_constants.cc b/chrome/browser/extensions/extension_permissions_api_constants.cc new file mode 100644 index 0000000..5d75ccd --- /dev/null +++ b/chrome/browser/extensions/extension_permissions_api_constants.cc @@ -0,0 +1,23 @@ +// 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_permissions_api_constants.h" + +namespace extension_permissions_module_constants { + +const char kApisKey[] = "permissions"; + +const char kCantRemoveRequiredPermissionsError[] = + "You cannot remove required permissions."; +const char kNotInOptionalPermissionsError[] = + "Optional permissions must be listed in extension manifest."; +const char kNotWhitelistedError[] = + "The optional permissions API does not support '%s'."; +const char kUnknownPermissionError[] = + "'%s' is not a recognized permission."; + +const char kOnAdded[] = "experimental.permissions.onAdded"; +const char kOnRemoved[] = "experimental.permissions.onRemoved"; + +}; // namespace extension_permissions_module_constants diff --git a/chrome/browser/extensions/extension_permissions_api_constants.h b/chrome/browser/extensions/extension_permissions_api_constants.h new file mode 100644 index 0000000..64b5116 --- /dev/null +++ b/chrome/browser/extensions/extension_permissions_api_constants.h @@ -0,0 +1,28 @@ +// 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. + +// Constants used for the Tabs API and the Windows API. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_CONSTANTS_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_CONSTANTS_H_ +#pragma once + +namespace extension_permissions_module_constants { + +// Keys used in serializing permissions data and events. +extern const char kApisKey[]; + +// Error messages. +extern const char kCantRemoveRequiredPermissionsError[]; +extern const char kNotInOptionalPermissionsError[]; +extern const char kNotWhitelistedError[]; +extern const char kUnknownPermissionError[]; + +// Event names. +extern const char kOnAdded[]; +extern const char kOnRemoved[]; + +}; // namespace extension_permissions_module_constants + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_CONSTANTS_H_ diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index 334b64f..e0a8d6e 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -102,11 +102,15 @@ const char kBrowserActionVisible[] = "browser_action_visible"; // Preferences that hold which permissions the user has granted the extension. // We explicitly keep track of these so that extensions can contain unknown // permissions, for backwards compatibility reasons, and we can still prompt -// the user to accept them once recognized. -const char kPrefGrantedAPIs[] = "granted_permissions.api"; -const char kPrefGrantedExplicitHosts[] = "granted_permissions.explicit_host"; -const char kPrefGrantedScriptableHosts[] = - "granted_permissions.scriptable_host"; +// the user to accept them once recognized. We store the active permission +// permissions because they may differ from those defined in the manifest. +const char kPrefActivePermissions[] = "active_permissions"; +const char kPrefGrantedPermissions[] = "granted_permissions"; + +// The preference names for ExtensionPermissionSet values. +const char kPrefAPIs[] = "api"; +const char kPrefExplicitHosts[] = "explicit_host"; +const char kPrefScriptableHosts[] = "scriptable_host"; // The preference names for the old granted permissions scheme. const char kPrefOldGrantedFullAccess[] = "granted_permissions.full"; @@ -206,6 +210,10 @@ class ScopedExtensionControlledPrefUpdate : public DictionaryPrefUpdate { DISALLOW_COPY_AND_ASSIGN(ScopedExtensionControlledPrefUpdate); }; +std::string JoinPrefs(std::string parent, const char* child) { + return parent + "." + child; +} + } // namespace ExtensionPrefs::ExtensionPrefs( @@ -423,6 +431,75 @@ void ExtensionPrefs::SetExtensionPrefURLPatternSet( UpdateExtensionPref(extension_id, pref_key, value); } +ExtensionPermissionSet* ExtensionPrefs::ReadExtensionPrefPermissionSet( + const std::string& extension_id, + const std::string& pref_key) { + if (!GetExtensionPref(extension_id)) + return NULL; + + // Retrieve the API permissions. + ExtensionAPIPermissionSet apis; + const ListValue* api_values = NULL; + std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); + if (ReadExtensionPrefList(extension_id, api_pref, &api_values)) { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (size_t i = 0; i < api_values->GetSize(); ++i) { + std::string permission_name; + if (api_values->GetString(i, &permission_name)) { + ExtensionAPIPermission *permission = info->GetByName(permission_name); + if (permission) + apis.insert(permission->id()); + } + } + } + + // Retrieve the explicit host permissions. + URLPatternSet explicit_hosts; + ReadExtensionPrefURLPatternSet( + extension_id, JoinPrefs(pref_key, kPrefExplicitHosts), + &explicit_hosts, Extension::kValidHostPermissionSchemes); + + // Retrieve the scriptable host permissions. + URLPatternSet scriptable_hosts; + ReadExtensionPrefURLPatternSet( + extension_id, JoinPrefs(pref_key, kPrefScriptableHosts), + &scriptable_hosts, UserScript::kValidUserScriptSchemes); + + return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts); +} + +void ExtensionPrefs::SetExtensionPrefPermissionSet( + const std::string& extension_id, + const std::string& pref_key, + const ExtensionPermissionSet* new_value) { + // Set the API permissions. + ListValue* api_values = new ListValue(); + ExtensionAPIPermissionSet apis = new_value->apis(); + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); + for (ExtensionAPIPermissionSet::const_iterator i = apis.begin(); + i != apis.end(); ++i) { + ExtensionAPIPermission* perm = info->GetByID(*i); + if (perm) + api_values->Append(Value::CreateStringValue(perm->name())); + } + UpdateExtensionPref(extension_id, api_pref, api_values); + + // Set the explicit host permissions. + if (!new_value->explicit_hosts().is_empty()) { + SetExtensionPrefURLPatternSet(extension_id, + JoinPrefs(pref_key, kPrefExplicitHosts), + new_value->explicit_hosts()); + } + + // Set the scriptable host permissions. + if (!new_value->scriptable_hosts().is_empty()) { + SetExtensionPrefURLPatternSet(extension_id, + JoinPrefs(pref_key, kPrefScriptableHosts), + new_value->scriptable_hosts()); + } +} + void ExtensionPrefs::SavePrefs() { prefs_->ScheduleSavePersistentPrefs(); } @@ -638,7 +715,9 @@ void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) { ListValue* apis = NULL; ListValue* new_apis = NULL; - if (ext->GetList(kPrefGrantedAPIs, &apis)) + std::string granted_apis = + JoinPrefs(kPrefGrantedPermissions, kPrefAPIs); + if (ext->GetList(kPrefOldGrantedAPIs, &apis)) new_apis = apis->DeepCopy(); else new_apis = new ListValue(); @@ -646,7 +725,7 @@ void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) { std::string plugin_name = info->GetByID( ExtensionAPIPermission::kPlugin)->name(); new_apis->Append(Value::CreateStringValue(plugin_name)); - UpdateExtensionPref(*ext_id, kPrefGrantedAPIs, new_apis); + UpdateExtensionPref(*ext_id, granted_apis, new_apis); } // The granted permissions originally only held the effective hosts, @@ -656,9 +735,11 @@ void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) { // new effective hosts will be the same, so we move them to explicit // host permissions. ListValue* hosts; + std::string explicit_hosts = + JoinPrefs(kPrefGrantedPermissions, kPrefExplicitHosts); if (ext->GetList(kPrefOldGrantedHosts, &hosts)) { UpdateExtensionPref( - *ext_id, kPrefGrantedExplicitHosts, hosts->DeepCopy()); + *ext_id, explicit_hosts, hosts->DeepCopy()); // We can get rid of the old one by setting it to an empty list. UpdateExtensionPref(*ext_id, kPrefOldGrantedHosts, new ListValue()); @@ -669,39 +750,7 @@ void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) { ExtensionPermissionSet* ExtensionPrefs::GetGrantedPermissions( const std::string& extension_id) { CHECK(Extension::IdIsValid(extension_id)); - - const DictionaryValue* ext = GetExtensionPref(extension_id); - if (!ext) - return NULL; - - // Retrieve the API permissions. - ExtensionAPIPermissionSet apis; - const ListValue* api_values = NULL; - if (ReadExtensionPrefList(extension_id, kPrefGrantedAPIs, &api_values)) { - ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); - for (size_t i = 0; i < api_values->GetSize(); ++i) { - std::string permission_name; - if (api_values->GetString(i, &permission_name)) { - ExtensionAPIPermission *permission = info->GetByName(permission_name); - if (permission) - apis.insert(permission->id()); - } - } - } - - // Retrieve the explicit host permissions. - URLPatternSet explicit_hosts; - ReadExtensionPrefURLPatternSet( - extension_id, kPrefGrantedExplicitHosts, - &explicit_hosts, Extension::kValidHostPermissionSchemes); - - // Retrieve the scriptable host permissions. - URLPatternSet scriptable_hosts; - ReadExtensionPrefURLPatternSet( - extension_id, kPrefGrantedScriptableHosts, - &scriptable_hosts, UserScript::kValidUserScriptSchemes); - - return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts); + return ReadExtensionPrefPermissionSet(extension_id, kPrefGrantedPermissions); } void ExtensionPrefs::AddGrantedPermissions( @@ -709,40 +758,30 @@ void ExtensionPrefs::AddGrantedPermissions( const ExtensionPermissionSet* permissions) { CHECK(Extension::IdIsValid(extension_id)); - scoped_ptr<ExtensionPermissionSet> granted_permissions( + scoped_refptr<ExtensionPermissionSet> granted_permissions( GetGrantedPermissions(extension_id)); // The new granted permissions are the union of the already granted // permissions and the newly granted permissions. - scoped_ptr<ExtensionPermissionSet> new_perms( + scoped_refptr<ExtensionPermissionSet> new_perms( ExtensionPermissionSet::CreateUnion( permissions, granted_permissions.get())); - // Set the API permissions. - ListValue* api_values = new ListValue(); - ExtensionAPIPermissionSet apis = new_perms->apis(); - ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); - for (ExtensionAPIPermissionSet::const_iterator i = apis.begin(); - i != apis.end(); ++i) { - ExtensionAPIPermission* perm = info->GetByID(*i); - if (perm) - api_values->Append(Value::CreateStringValue(perm->name())); - } - UpdateExtensionPref(extension_id, kPrefGrantedAPIs, api_values); + SetExtensionPrefPermissionSet( + extension_id, kPrefGrantedPermissions, new_perms.get()); +} - // Set the explicit host permissions. - if (!new_perms->explicit_hosts().is_empty()) { - SetExtensionPrefURLPatternSet(extension_id, - kPrefGrantedExplicitHosts, - new_perms->explicit_hosts()); - } +ExtensionPermissionSet* ExtensionPrefs::GetActivePermissions( + const std::string& extension_id) { + CHECK(Extension::IdIsValid(extension_id)); + return ReadExtensionPrefPermissionSet(extension_id, kPrefActivePermissions); +} - // Set the scriptable host permissions. - if (!new_perms->scriptable_hosts().is_empty()) { - SetExtensionPrefURLPatternSet(extension_id, - kPrefGrantedScriptableHosts, - new_perms->scriptable_hosts()); - } +void ExtensionPrefs::SetActivePermissions( + const std::string& extension_id, + const ExtensionPermissionSet* permissions) { + SetExtensionPrefPermissionSet( + extension_id, kPrefActivePermissions, permissions); } bool ExtensionPrefs::IsIncognitoEnabled(const std::string& extension_id) { diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h index 6b5ffd4..92f8672 100644 --- a/chrome/browser/extensions/extension_prefs.h +++ b/chrome/browser/extensions/extension_prefs.h @@ -187,6 +187,16 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer { void AddGrantedPermissions(const std::string& extension_id, const ExtensionPermissionSet* permissions); + // Gets the active permission set for the specified extension. This may + // differ from the permissions in the manifest due to the optional + // permissions API. This passes ownership of the set to the caller. + ExtensionPermissionSet* GetActivePermissions( + const std::string& extension_id); + + // Sets the active |permissions| for the extension with |extension_id|. + void SetActivePermissions(const std::string& extension_id, + const ExtensionPermissionSet* permissions); + // Returns true if the user enabled this extension to be loaded in incognito // mode. bool IsIncognitoEnabled(const std::string& extension_id); @@ -409,6 +419,18 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer { const std::string& pref_key, const URLPatternSet& new_value); + // Interprets |pref_key| in |extension_id|'s preferences as an + // ExtensionPermissionSet, and passes ownership of the set to the caller. + ExtensionPermissionSet* ReadExtensionPrefPermissionSet( + const std::string& extension_id, + const std::string& pref_key); + + // Converts the |new_value| to its value and sets the |pref_key| pref + // belonging to |extension_id|. + void SetExtensionPrefPermissionSet(const std::string& extension_id, + const std::string& pref_key, + const ExtensionPermissionSet* new_value); + // Returns a dictionary for extension |id|'s prefs or NULL if it doesn't // exist. const base::DictionaryValue* GetExtensionPref(const std::string& id) const; diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc index f582077..2ed6588 100644 --- a/chrome/browser/extensions/extension_prefs_unittest.cc +++ b/chrome/browser/extensions/extension_prefs_unittest.cc @@ -219,32 +219,32 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { ExtensionAPIPermissionSet empty_set; URLPatternSet empty_extent; - scoped_ptr<ExtensionPermissionSet> permissions; - scoped_ptr<ExtensionPermissionSet> granted_permissions; + scoped_refptr<ExtensionPermissionSet> permissions; + scoped_refptr<ExtensionPermissionSet> granted_permissions; // Make sure both granted api and host permissions start empty. - granted_permissions.reset( - prefs()->GetGrantedPermissions(extension_id_)); + granted_permissions = + prefs()->GetGrantedPermissions(extension_id_); EXPECT_TRUE(granted_permissions->IsEmpty()); - permissions.reset(new ExtensionPermissionSet( - api_perm_set1_, empty_extent, empty_extent)); + permissions = new ExtensionPermissionSet( + api_perm_set1_, empty_extent, empty_extent); // Add part of the api permissions. prefs()->AddGrantedPermissions(extension_id_, permissions.get()); - granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_)); + granted_permissions = prefs()->GetGrantedPermissions(extension_id_); EXPECT_TRUE(granted_permissions.get()); EXPECT_FALSE(granted_permissions->IsEmpty()); EXPECT_EQ(expected_apis, granted_permissions->apis()); EXPECT_TRUE(granted_permissions->effective_hosts().is_empty()); EXPECT_FALSE(granted_permissions->HasEffectiveFullAccess()); - granted_permissions.reset(); + granted_permissions = NULL; // Add part of the explicit host permissions. - permissions.reset(new ExtensionPermissionSet( - empty_set, ehost_perm_set1_, empty_extent)); + permissions = new ExtensionPermissionSet( + empty_set, ehost_perm_set1_, empty_extent); prefs()->AddGrantedPermissions(extension_id_, permissions.get()); - granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_)); + granted_permissions = prefs()->GetGrantedPermissions(extension_id_); EXPECT_FALSE(granted_permissions->IsEmpty()); EXPECT_FALSE(granted_permissions->HasEffectiveFullAccess()); EXPECT_EQ(expected_apis, granted_permissions->apis()); @@ -254,10 +254,10 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { granted_permissions->effective_hosts()); // Add part of the scriptable host permissions. - permissions.reset(new ExtensionPermissionSet( - empty_set, empty_extent, shost_perm_set1_)); + permissions = new ExtensionPermissionSet( + empty_set, empty_extent, shost_perm_set1_); prefs()->AddGrantedPermissions(extension_id_, permissions.get()); - granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_)); + granted_permissions = prefs()->GetGrantedPermissions(extension_id_); EXPECT_FALSE(granted_permissions->IsEmpty()); EXPECT_FALSE(granted_permissions->HasEffectiveFullAccess()); EXPECT_EQ(expected_apis, granted_permissions->apis()); @@ -271,15 +271,15 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { EXPECT_EQ(effective_permissions_, granted_permissions->effective_hosts()); // Add the rest of both the permissions. - permissions.reset(new ExtensionPermissionSet( - api_perm_set2_, ehost_perm_set2_, shost_perm_set2_)); + permissions = new ExtensionPermissionSet( + api_perm_set2_, ehost_perm_set2_, shost_perm_set2_); std::set_union(expected_apis.begin(), expected_apis.end(), api_perm_set2_.begin(), api_perm_set2_.end(), std::inserter(api_permissions_, api_permissions_.begin())); prefs()->AddGrantedPermissions(extension_id_, permissions.get()); - granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_)); + granted_permissions = prefs()->GetGrantedPermissions(extension_id_); EXPECT_TRUE(granted_permissions.get()); EXPECT_FALSE(granted_permissions->IsEmpty()); EXPECT_EQ(api_permissions_, granted_permissions->apis()); @@ -294,7 +294,7 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { } virtual void Verify() { - scoped_ptr<ExtensionPermissionSet> permissions( + scoped_refptr<ExtensionPermissionSet> permissions( prefs()->GetGrantedPermissions(extension_id_)); EXPECT_TRUE(permissions.get()); EXPECT_FALSE(permissions->HasEffectiveFullAccess()); @@ -321,6 +321,54 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { }; TEST_F(ExtensionPrefsGrantedPermissions, GrantedPermissions) {} +// Tests the SetActivePermissions / GetActivePermissions functions. +class ExtensionPrefsActivePermissions : public ExtensionPrefsTest { + public: + virtual void Initialize() { + extension_id_ = prefs_.AddExtensionAndReturnId("test"); + + ExtensionAPIPermissionSet api_perms; + api_perms.insert(ExtensionAPIPermission::kTab); + api_perms.insert(ExtensionAPIPermission::kBookmark); + api_perms.insert(ExtensionAPIPermission::kHistory); + + URLPatternSet ehosts; + AddPattern(&ehosts, "http://*.google.com/*"); + AddPattern(&ehosts, "http://example.com/*"); + AddPattern(&ehosts, "chrome://favicon/*"); + + URLPatternSet shosts; + AddPattern(&shosts, "https://*.google.com/*"); + AddPattern(&shosts, "http://reddit.com/r/test/*"); + + active_perms_ = new ExtensionPermissionSet(api_perms, ehosts, shosts); + + // Make sure the active permissions start empty. + scoped_refptr<ExtensionPermissionSet> active( + prefs()->GetActivePermissions(extension_id_)); + EXPECT_TRUE(active->IsEmpty()); + + // Set the active permissions. + prefs()->SetActivePermissions(extension_id_, active_perms_.get()); + active = prefs()->GetActivePermissions(extension_id_); + EXPECT_EQ(active_perms_->apis(), active->apis()); + EXPECT_EQ(active_perms_->explicit_hosts(), active->explicit_hosts()); + EXPECT_EQ(active_perms_->scriptable_hosts(), active->scriptable_hosts()); + EXPECT_EQ(*active_perms_, *active); + } + + virtual void Verify() { + scoped_refptr<ExtensionPermissionSet> permissions( + prefs()->GetActivePermissions(extension_id_)); + EXPECT_EQ(*active_perms_, *permissions); + } + + private: + std::string extension_id_; + scoped_refptr<ExtensionPermissionSet> active_perms_; +}; +TEST_F(ExtensionPrefsActivePermissions, SetAndGetActivePermissions) {} + // Tests the GetVersionString function. class ExtensionPrefsVersionString : public ExtensionPrefsTest { public: diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 5ff972e..72667a1 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -552,6 +552,7 @@ ExtensionService::ExtensionService(Profile* profile, show_extensions_prompts_(true), ready_(false), toolbar_model_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + permissions_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), apps_promo_(profile->GetPrefs()), event_routers_initialized_(false) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -974,11 +975,13 @@ void ExtensionService::DisableExtension(const std::string& extension_id) { void ExtensionService::GrantPermissions(const Extension* extension) { CHECK(extension); - // We only maintain the granted permissions prefs for INTERNAL extensions. - CHECK_EQ(Extension::INTERNAL, extension->location()); + // We only maintain the granted permissions prefs for extensions that can't + // silently increase their permissions. + if (extension->CanSilentlyIncreasePermissions()) + return; extension_prefs_->AddGrantedPermissions(extension->id(), - extension->permission_set()); + extension->GetActivePermissions()); } void ExtensionService::GrantPermissionsAndEnableExtension( @@ -991,6 +994,13 @@ void ExtensionService::GrantPermissionsAndEnableExtension( EnableExtension(extension->id()); } +void ExtensionService::UpdateActivePermissions( + const Extension* extension, + const ExtensionPermissionSet* permissions) { + extension_prefs()->SetActivePermissions(extension->id(), permissions); + extension->SetActivePermissions(permissions); +} + void ExtensionService::LoadExtension(const FilePath& extension_path) { BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, NewRunnableMethod(backend_.get(), @@ -1356,7 +1366,8 @@ void ExtensionService::NotifyExtensionLoaded(const Extension* extension) { if (host->profile()->GetOriginalProfile() == profile_->GetOriginalProfile()) { host->Send( - new ExtensionMsg_Loaded(ExtensionMsg_Loaded_Params(extension))); + new ExtensionMsg_Loaded(ExtensionMsg_Loaded_Params( + extension, extension->GetActivePermissions()))); } } @@ -1964,7 +1975,7 @@ void ExtensionService::AddExtension(const Extension* extension) { // Check if the extension's privileges have changed and disable the // extension if necessary. - DisableIfPrivilegeIncrease(extension); + InitializePermissions(extension); bool disabled = Extension::UserMayDisable(extension->location()) && extension_prefs_->GetExtensionState(extension->id()) == @@ -1985,7 +1996,35 @@ void ExtensionService::AddExtension(const Extension* extension) { NotifyExtensionLoaded(extension); } -void ExtensionService::DisableIfPrivilegeIncrease(const Extension* extension) { +void ExtensionService::InitializePermissions(const Extension* extension) { + // If the extension has used the optional permissions API, it will have a + // custom set of active permissions defined in the extension prefs. Here, + // we update the extension's active permissions based on the prefs. + scoped_refptr<ExtensionPermissionSet> active_permissions = + extension_prefs()->GetActivePermissions(extension->id()); + + if (active_permissions.get()) { + // We restrict the active permissions to be within the bounds defined in the + // extension's manifest. + // a) active permissions must be a subset of optional + default permissions + // b) active permissions must contains all default permissions + scoped_refptr<ExtensionPermissionSet> total_permissions = + ExtensionPermissionSet::CreateUnion( + extension->required_permission_set(), + extension->optional_permission_set()); + + // Make sure the active permissions contain no more than optional + default. + scoped_refptr<ExtensionPermissionSet> adjusted_active = + ExtensionPermissionSet::CreateIntersection( + total_permissions.get(), active_permissions.get()); + + // Make sure the active permissions contain the default permissions. + adjusted_active = ExtensionPermissionSet::CreateUnion( + extension->required_permission_set(), adjusted_active.get()); + + UpdateActivePermissions(extension, adjusted_active); + } + // We keep track of all permissions the user has granted each extension. // This allows extensions to gracefully support backwards compatibility // by including unknown permissions in their manifests. When the user @@ -2012,13 +2051,13 @@ void ExtensionService::DisableIfPrivilegeIncrease(const Extension* extension) { bool is_extension_upgrade = old != NULL; bool is_privilege_increase = false; - // We only record the granted permissions for INTERNAL extensions, since - // they can't silently increase privileges. - if (extension->location() == Extension::INTERNAL) { + // We only need to compare the granted permissions to the current permissions + // if the extension is not allowed to silently increase its permissions. + if (!extension->CanSilentlyIncreasePermissions()) { // Add all the recognized permissions if the granted permissions list // hasn't been initialized yet. - scoped_ptr<ExtensionPermissionSet> granted_permissions( - extension_prefs_->GetGrantedPermissions(extension->id())); + scoped_refptr<ExtensionPermissionSet> granted_permissions = + extension_prefs_->GetGrantedPermissions(extension->id()); CHECK(granted_permissions.get()); // Here, we check if an extension's privileges have increased in a manner @@ -2026,7 +2065,8 @@ void ExtensionService::DisableIfPrivilegeIncrease(const Extension* extension) { // upgraded and recognized additional privileges, or an extension upgrades // to a version that requires additional privileges. is_privilege_increase = - granted_permissions->HasLessPrivilegesThan(extension->permission_set()); + granted_permissions->HasLessPrivilegesThan( + extension->GetActivePermissions()); } if (is_extension_upgrade) { @@ -2378,7 +2418,8 @@ void ExtensionService::Observe(int type, // Loaded extensions. for (size_t i = 0; i < extensions_.size(); ++i) { process->Send(new ExtensionMsg_Loaded( - ExtensionMsg_Loaded_Params(extensions_[i]))); + ExtensionMsg_Loaded_Params( + extensions_[i], extensions_[i]->GetActivePermissions()))); } break; } diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index 4c74f0a..901726f 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -26,6 +26,7 @@ #include "chrome/browser/extensions/extension_icon_manager.h" #include "chrome/browser/extensions/extension_menu_manager.h" #include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_permissions_api.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_toolbar_model.h" #include "chrome/browser/extensions/extensions_quota_service.h" @@ -320,6 +321,10 @@ class ExtensionService // extension. void GrantPermissionsAndEnableExtension(const Extension* extension); + // Sets the |extension|'s active permissions to |permissions|. + void UpdateActivePermissions(const Extension* extension, + const ExtensionPermissionSet* permissions); + // Loads the extension from the directory |extension_path|. void LoadExtension(const FilePath& extension_path); @@ -405,9 +410,9 @@ class ExtensionService void OnExtensionInstalled( const Extension* extension, bool from_webstore); - // Checks if the privileges requested by |extension| have increased, and if - // so, disables the extension and prompts the user to approve the change. - void DisableIfPrivilegeIncrease(const Extension* extension); + // Initializes the |extension|'s active permission set and disables the + // extension if the privilege level has increased (e.g., due to an upgrade). + void InitializePermissions(const Extension* extension); // Go through each extensions in pref, unload blacklisted extensions // and update the blacklist state in pref. @@ -468,6 +473,10 @@ class ExtensionService return &app_notification_manager_; } + ExtensionPermissionsManager* permissions_manager() { + return &permissions_manager_; + } + ExtensionBrowserEventRouter* browser_event_router() { return browser_event_router_.get(); } @@ -586,7 +595,6 @@ class ExtensionService bool include_disabled, bool include_terminated) const; - // Adds the given extension to the list of terminated extensions if // it is not already there and unloads it. void TrackTerminatedExtension(const Extension* extension); @@ -708,6 +716,9 @@ class ExtensionService // Keeps track of app notifications. AppNotificationManager app_notification_manager_; + // Keeps track of extension permissions. + ExtensionPermissionsManager permissions_manager_; + // Keeps track of favicon-sized omnibox icons for extensions. ExtensionIconManager omnibox_icon_manager_; ExtensionIconManager omnibox_popup_icon_manager_; diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc index bad22a1..c1a18d1 100644 --- a/chrome/browser/extensions/extension_service_unittest.cc +++ b/chrome/browser/extensions/extension_service_unittest.cc @@ -1023,7 +1023,8 @@ TEST_F(ExtensionServiceTest, LoadAllExtensionsFromDirectorySuccess) { expected_patterns.ClearPatterns(); AddPattern(&expected_patterns, "http://*.google.com/*"); AddPattern(&expected_patterns, "https://*.google.com/*"); - EXPECT_EQ(expected_patterns, extension->permission_set()->explicit_hosts()); + EXPECT_EQ(expected_patterns, + extension->GetActivePermissions()->explicit_hosts()); EXPECT_EQ(std::string(good1), loaded_[1]->id()); EXPECT_EQ(std::string("My extension 2"), loaded_[1]->name()); @@ -1364,7 +1365,7 @@ TEST_F(ExtensionServiceTest, GrantedPermissions) { // Make sure there aren't any granted permissions before the // extension is installed. - scoped_ptr<ExtensionPermissionSet> known_perms( + scoped_refptr<ExtensionPermissionSet> known_perms( prefs->GetGrantedPermissions(permissions_crx)); EXPECT_FALSE(known_perms.get()); @@ -1383,7 +1384,7 @@ TEST_F(ExtensionServiceTest, GrantedPermissions) { AddPattern(&expected_host_perms, "http://*.google.com.hk/*"); AddPattern(&expected_host_perms, "http://www.example.com/*"); - known_perms.reset(prefs->GetGrantedPermissions(extension_id)); + known_perms = prefs->GetGrantedPermissions(extension_id); EXPECT_TRUE(known_perms.get()); EXPECT_FALSE(known_perms->IsEmpty()); EXPECT_EQ(expected_api_perms, known_perms->apis()); @@ -1412,7 +1413,7 @@ TEST_F(ExtensionServiceTest, GrantedFullAccessPermissions) { std::string extension_id = extension->id(); ExtensionPrefs* prefs = service_->extension_prefs(); - scoped_ptr<ExtensionPermissionSet> permissions( + scoped_refptr<ExtensionPermissionSet> permissions( prefs->GetGrantedPermissions(extension_id)); EXPECT_FALSE(permissions->IsEmpty()); EXPECT_TRUE(permissions->HasEffectiveFullAccess()); @@ -1477,7 +1478,7 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) { ASSERT_TRUE(service_->IsExtensionEnabled(extension_id)); ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id)); - scoped_ptr<ExtensionPermissionSet> current_perms( + scoped_refptr<ExtensionPermissionSet> current_perms( prefs->GetGrantedPermissions(extension_id)); ASSERT_TRUE(current_perms.get()); ASSERT_FALSE(current_perms->IsEmpty()); @@ -1490,7 +1491,7 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) { // updating the browser to a version which recognizes additional host // permissions). host_permissions.clear(); - current_perms.reset(); + current_perms = NULL; host_permissions.insert("http://*.google.com/*"); host_permissions.insert("https://*.google.com/*"); @@ -1519,7 +1520,7 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) { ASSERT_TRUE(service_->IsExtensionEnabled(extension_id)); ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id)); - current_perms.reset(prefs->GetGrantedPermissions(extension_id)); + current_perms = prefs->GetGrantedPermissions(extension_id); ASSERT_TRUE(current_perms.get()); ASSERT_FALSE(current_perms->IsEmpty()); ASSERT_FALSE(current_perms->HasEffectiveFullAccess()); diff --git a/chrome/browser/extensions/permissions_apitest.cc b/chrome/browser/extensions/permissions_apitest.cc index e298a093..def0595 100644 --- a/chrome/browser/extensions/permissions_apitest.cc +++ b/chrome/browser/extensions/permissions_apitest.cc @@ -1,9 +1,14 @@ -// 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 "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension_permission_set.h" class ExperimentalApiTest : public ExtensionApiTest { public: @@ -47,3 +52,35 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FaviconPermission) { IN_PROC_BROWSER_TEST_F(ExtensionApiTest, AlwaysAllowed) { ASSERT_TRUE(RunExtensionTest("permissions/always_allowed")) << message_; } + +// Tests that the optional permissions API works correctly. +IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsGranted) { + // Mark all the tested APIs as granted to bypass the confirmation UI. + ExtensionAPIPermissionSet apis; + apis.insert(ExtensionAPIPermission::kTab); + apis.insert(ExtensionAPIPermission::kManagement); + apis.insert(ExtensionAPIPermission::kPermissions); + scoped_refptr<ExtensionPermissionSet> granted_permissions = + new ExtensionPermissionSet(apis, URLPatternSet(), URLPatternSet()); + + ExtensionPrefs* prefs = + browser()->profile()->GetExtensionService()->extension_prefs(); + prefs->AddGrantedPermissions("kjmkgkdkpedkejedfhmfcenooemhbpbo", + granted_permissions); + + EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_; +} + +// Tests that the optional permissions API works correctly. +IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsAutoConfirm) { + // Rather than setting the granted permissions, set the UI autoconfirm flag + // and run the same tests. + RequestPermissionsFunction::SetAutoConfirmForTests(true); + EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_; +} + +// Test that denying the optional permissions confirmation dialog works. +IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsDeny) { + RequestPermissionsFunction::SetAutoConfirmForTests(false); + EXPECT_TRUE(RunExtensionTest("permissions/optional_deny")) << message_; +} diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h index 8c7818d..2617a53 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h +++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h @@ -37,6 +37,7 @@ class Profile; scoped_nsobject<NSString> title_; scoped_nsobject<NSString> warnings_; scoped_nsobject<NSString> button_; + scoped_nsobject<NSString> cancel_button_; scoped_nsobject<NSString> subtitle_; SkBitmap icon_; } 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 1c8306a..0e79076f 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm @@ -92,6 +92,9 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) { retain]); button_.reset([l10n_util::GetNSString(ExtensionInstallUI::kButtonIds[type]) retain]); + int cancel_id = ExtensionInstallUI::kAbortButtonIds[type]; + cancel_button_.reset([l10n_util::GetNSString( + cancel_id > 0 ? cancel_id : IDS_CANCEL) retain]); // We display the warnings as a simple text string, separated by newlines. if (!warnings.empty()) { @@ -132,6 +135,7 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) { [titleField_ setStringValue:title_.get()]; [subtitleField_ setStringValue:subtitle_.get()]; [okButton_ setTitle:button_.get()]; + [cancelButton_ setTitle:cancel_button_.get()]; NSImage* image = gfx::SkBitmapToNSImage(icon_); [iconView_ setImage:image]; 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 cd2e99a..a49bc0a 100644 --- a/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc +++ b/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc @@ -62,8 +62,11 @@ void ShowInstallDialog(GtkWindow* parent, parent, GTK_DIALOG_MODAL, NULL); - GtkWidget* close_button = gtk_dialog_add_button(GTK_DIALOG(dialog), - GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE); + int cancel = ExtensionInstallUI::kAbortButtonIds[type]; + GtkWidget* close_button = gtk_dialog_add_button( + GTK_DIALOG(dialog), + cancel > 0 ? l10n_util::GetStringUTF8(cancel).c_str(): GTK_STOCK_CANCEL, + GTK_RESPONSE_CLOSE); gtk_dialog_add_button( GTK_DIALOG(dialog), l10n_util::GetStringUTF8(ExtensionInstallUI::kButtonIds[type]).c_str(), 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 1bf6d8f..b5c8a1ee 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc @@ -275,8 +275,10 @@ std::wstring ExtensionInstallDialogView::GetDialogButtonLabel( case MessageBoxFlags::DIALOGBUTTON_OK: return UTF16ToWide( l10n_util::GetStringUTF16(ExtensionInstallUI::kButtonIds[type_])); - case MessageBoxFlags::DIALOGBUTTON_CANCEL: - return UTF16ToWide(l10n_util::GetStringUTF16(IDS_CANCEL)); + case MessageBoxFlags::DIALOGBUTTON_CANCEL: { + int id = ExtensionInstallUI::kAbortButtonIds[type_]; + return UTF16ToWide(l10n_util::GetStringUTF16(id > 0 ? id : IDS_CANCEL)); + } default: NOTREACHED(); return std::wstring(); |