From e3643158bd3a528ead7362ea91c9ae630972d934 Mon Sep 17 00:00:00 2001 From: treib Date: Wed, 24 Jun 2015 08:59:05 -0700 Subject: [PATCH] Add a webstorePrivate API to show a permission prompt for delegated bundle installs, try 2 (i.e. remote installs of a bundle for another user). Also add a new permission prompt type for this case. This is the "bundle" version of https://crrev.com/850283003. This CL was originally committed as https://crrev.com/1189133002 and reverted as https://crrev.com/1205863002 due to breaking browser_tests (dbg). TBR since the original CL has been reviewed, and the additional fix is trivial. TBR=kalman BUG=397951 Review URL: https://codereview.chromium.org/1208563002 Cr-Commit-Position: refs/heads/master@{#335928} --- chrome/app/generated_resources.grd | 9 ++ .../api/webstore_private/webstore_private_api.cc | 116 ++++++++++++++------- .../api/webstore_private/webstore_private_api.h | 77 ++++++++++++-- chrome/browser/extensions/bundle_installer.cc | 71 +++++++------ chrome/browser/extensions/bundle_installer.h | 7 +- .../browser/extensions/extension_install_prompt.cc | 34 +++++- .../browser/extensions/extension_install_prompt.h | 23 +++- .../extensions/extension_install_dialog_view.h | 4 +- chrome/common/extensions/api/webstore_private.json | 65 +++++++++++- .../browser/extension_function_histogram_value.h | 1 + tools/metrics/histograms/histograms.xml | 2 + 11 files changed, 322 insertions(+), 87 deletions(-) diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index faea2134e8df..1a78f77b36b0 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4283,6 +4283,15 @@ Even if you have downloaded files from this website before, the website might ha "$1My Awesome Bundle" adds these apps and extensions: + + "$1My Awesome Bundle" adds these extensions for $2Wallace: + + + "$1My Awesome Bundle" adds these apps for $2Wallace: + + + "$1My Awesome Bundle" adds these apps and extensions for $2Wallace: + Remove "$1Gmail Checker"? diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc index 10898777a2bf..e436be7da416 100644 --- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc +++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc @@ -54,6 +54,8 @@ namespace LaunchEphemeralApp = api::webstore_private::LaunchEphemeralApp; namespace SetStoreLogin = api::webstore_private::SetStoreLogin; namespace ShowPermissionPromptForDelegatedInstall = api::webstore_private::ShowPermissionPromptForDelegatedInstall; +namespace ShowPermissionPromptForDelegatedBundleInstall = + api::webstore_private::ShowPermissionPromptForDelegatedBundleInstall; namespace { @@ -388,13 +390,12 @@ ExtensionFunction::ResponseValue WebstorePrivateBeginInstallWithManifest3Function::RunExtraForResponse() { InstallTracker* tracker = InstallTracker::Get(browser_context()); DCHECK(tracker); - if (util::IsExtensionInstalledPermanently(params().details.id, - browser_context()) || - tracker->GetActiveInstall(params().details.id)) { + if (util::IsExtensionInstalledPermanently(details().id, browser_context()) || + tracker->GetActiveInstall(details().id)) { return BuildResponse(api::webstore_private::RESULT_ALREADY_INSTALLED, kAlreadyInstalledError); } - ActiveInstallData install_data(params().details.id); + ActiveInstallData install_data(details().id); scoped_active_install_.reset(new ScopedActiveInstall(tracker, install_data)); return ExtensionFunction::ResponseValue(); } @@ -406,18 +407,18 @@ void WebstorePrivateBeginInstallWithManifest3Function::InstallUIProceedHook() { scoped_ptr approval( WebstoreInstaller::Approval::CreateWithNoInstallPrompt( chrome_details_.GetProfile(), - params().details.id, + details().id, PassParsedManifest(), false)); - approval->use_app_installed_bubble = params().details.app_install_bubble; - approval->enable_launcher = params().details.enable_launcher; + approval->use_app_installed_bubble = details().app_install_bubble; + approval->enable_launcher = details().enable_launcher; // If we are enabling the launcher, we should not show the app list in order // to train the user to open it themselves at least once. - approval->skip_post_install_ui = params().details.enable_launcher; + approval->skip_post_install_ui = details().enable_launcher; approval->dummy_extension = dummy_extension(); approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon()); - if (params().details.authuser) - approval->authuser = *params().details.authuser; + if (details().authuser) + approval->authuser = *details().authuser; g_pending_approvals.Get().PushApproval(approval.Pass()); DCHECK(scoped_active_install_.get()); @@ -584,7 +585,7 @@ WebstorePrivateShowPermissionPromptForDelegatedInstallFunction:: void WebstorePrivateShowPermissionPromptForDelegatedInstallFunction::ShowPrompt( ExtensionInstallPrompt* install_prompt) { install_prompt->ConfirmPermissionsForDelegatedInstall( - this, dummy_extension().get(), params().details.delegated_user, &icon()); + this, dummy_extension().get(), details().delegated_user, &icon()); } scoped_ptr @@ -593,15 +594,20 @@ WebstorePrivateShowPermissionPromptForDelegatedInstallFunction::CreateResults( return ShowPermissionPromptForDelegatedInstall::Results::Create(result); } -WebstorePrivateInstallBundleFunction::WebstorePrivateInstallBundleFunction() +template +WebstorePrivateFunctionWithBundle::WebstorePrivateFunctionWithBundle() : chrome_details_(this) { } -WebstorePrivateInstallBundleFunction::~WebstorePrivateInstallBundleFunction() { +template +WebstorePrivateFunctionWithBundle:: + ~WebstorePrivateFunctionWithBundle() { } -ExtensionFunction::ResponseAction WebstorePrivateInstallBundleFunction::Run() { - params_ = InstallBundle::Params::Create(*args_); +template +ExtensionFunction::ResponseAction +WebstorePrivateFunctionWithBundle::Run() { + params_ = Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params_); if (params_->contents.empty()) @@ -622,7 +628,7 @@ ExtensionFunction::ResponseAction WebstorePrivateInstallBundleFunction::Run() { } else { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&WebstorePrivateInstallBundleFunction::OnFetchComplete, + base::Bind(&WebstorePrivateFunctionWithBundle::OnFetchComplete, this, GURL(), nullptr)); } @@ -633,22 +639,13 @@ ExtensionFunction::ResponseAction WebstorePrivateInstallBundleFunction::Run() { return RespondLater(); } -void WebstorePrivateInstallBundleFunction::OnFetchComplete( +template +void WebstorePrivateFunctionWithBundle::OnFetchComplete( const GURL& url, const SkBitmap* bitmap) { - InstallTracker* tracker = InstallTracker::Get(browser_context()); - DCHECK(tracker); - - std::string authuser; - if (params_->details.authuser) - authuser = *params_->details.authuser; - BundleInstaller::ItemList items; for (const auto& entry : params_->contents) { - // Skip already-installed items. - if (util::IsExtensionInstalledPermanently(entry->id, browser_context()) || - tracker->GetActiveInstall(entry->id)) { + if (ShouldSkipItem(entry->id)) continue; - } BundleInstaller::Item item; item.id = entry->id; item.manifest = entry->manifest; @@ -662,20 +659,20 @@ void WebstorePrivateInstallBundleFunction::OnFetchComplete( Release(); // Matches the AddRef in Run. return; } + bundle_.reset(new BundleInstaller(chrome_details_.GetCurrentBrowser(), params_->details.localized_name, - bitmap ? *bitmap : SkBitmap(), authuser, - items)); + bitmap ? *bitmap : SkBitmap(), + auth_user_, delegated_user_, items)); - // The bundle installer will call us back via OnInstallApproval. bundle_->PromptForApproval( - base::Bind(&WebstorePrivateInstallBundleFunction::OnInstallApproval, - this)); + base::Bind(&WebstorePrivateFunctionWithBundle::OnInstallApproval, this)); Release(); // Matches the AddRef in Run. } -void WebstorePrivateInstallBundleFunction::OnInstallApproval( +template +void WebstorePrivateFunctionWithBundle::OnInstallApproval( BundleInstaller::ApprovalState state) { if (state != BundleInstaller::APPROVED) { Respond(Error(state == BundleInstaller::USER_CANCELED @@ -684,9 +681,31 @@ void WebstorePrivateInstallBundleFunction::OnInstallApproval( return; } + OnInstallApprovalHook(); +} + +WebstorePrivateInstallBundleFunction::WebstorePrivateInstallBundleFunction() { +} + +WebstorePrivateInstallBundleFunction::~WebstorePrivateInstallBundleFunction() { +} + +void WebstorePrivateInstallBundleFunction::ProcessParams() { + if (details().authuser) + set_auth_user(*details().authuser); +} + +bool WebstorePrivateInstallBundleFunction::ShouldSkipItem( + const std::string& id) const { + // Skip already-installed items. + return util::IsExtensionInstalledPermanently(id, browser_context()) || + InstallTracker::Get(browser_context())->GetActiveInstall(id); +} + +void WebstorePrivateInstallBundleFunction::OnInstallApprovalHook() { // The bundle installer will call us back via OnInstallComplete. - bundle_->CompleteInstall( - chrome_details_.GetAssociatedWebContents(), + bundle()->CompleteInstall( + GetSenderWebContents(), base::Bind(&WebstorePrivateInstallBundleFunction::OnInstallComplete, this)); } @@ -695,6 +714,31 @@ void WebstorePrivateInstallBundleFunction::OnInstallComplete() { Respond(NoArguments()); } +WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction:: + WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction() { +} + +WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction:: + ~WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction() { +} + +void WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction:: + ProcessParams() { + set_delegated_user(details().delegated_user); +} + +bool WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction:: + ShouldSkipItem(const std::string& id) const { + // For delegated installs, don't skip any items (we don't know if any are + // installed already). + return false; +} + +void WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction:: + OnInstallApprovalHook() { + Respond(NoArguments()); +} + WebstorePrivateEnableAppLauncherFunction:: WebstorePrivateEnableAppLauncherFunction() : chrome_details_(this) {} diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.h b/chrome/browser/extensions/api/webstore_private/webstore_private_api.h index a6b83b18f37b..ad9e05209017 100644 --- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.h +++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.h @@ -76,7 +76,7 @@ class WebstorePrivateFunctionWithPermissionPrompt api::webstore_private::Result result, const std::string& error); - const Params& params() const { return *params_; } + const typename Params::Details& details() const { return params_->details; } const SkBitmap& icon() const { return icon_; } const scoped_refptr& dummy_extension() const { return dummy_extension_; @@ -188,18 +188,34 @@ class WebstorePrivateShowPermissionPromptForDelegatedInstallFunction api::webstore_private::Result result) const override; }; -class WebstorePrivateInstallBundleFunction +// Base class for webstorePrivate functions that deal with bundles. +template +class WebstorePrivateFunctionWithBundle : public UIThreadExtensionFunction, public chrome::BitmapFetcherDelegate { public: - DECLARE_EXTENSION_FUNCTION("webstorePrivate.installBundle", - WEBSTOREPRIVATE_INSTALLBUNDLE) + WebstorePrivateFunctionWithBundle(); - WebstorePrivateInstallBundleFunction(); + protected: + ~WebstorePrivateFunctionWithBundle() override; - private: - ~WebstorePrivateInstallBundleFunction() override; + const typename Params::Details& details() const { return params_->details; } + extensions::BundleInstaller* bundle() { return bundle_.get(); } + ChromeExtensionFunctionDetails* chrome_details() { return &chrome_details_; } + + void set_auth_user(const std::string& user) { auth_user_ = user; } + void set_delegated_user(const std::string& user) { + delegated_user_ = user; + } + // Called after |params_| has been created. + virtual void ProcessParams() = 0; + // Called for each bundle item before showing them in the dialog. + virtual bool ShouldSkipItem(const std::string& id) const = 0; + // Called after the user has confirmed the dialog. + virtual void OnInstallApprovalHook() = 0; + + private: // ExtensionFunction: ExtensionFunction::ResponseAction Run() override; @@ -207,18 +223,61 @@ class WebstorePrivateInstallBundleFunction void OnFetchComplete(const GURL& url, const SkBitmap* bitmap) override; void OnInstallApproval(BundleInstaller::ApprovalState state); - void OnInstallComplete(); ChromeExtensionFunctionDetails chrome_details_; // This stores the input parameters to the function. - scoped_ptr params_; + scoped_ptr params_; + + std::string auth_user_; + std::string delegated_user_; scoped_ptr bundle_; scoped_ptr icon_fetcher_; }; +class WebstorePrivateInstallBundleFunction + : public WebstorePrivateFunctionWithBundle + { + public: + DECLARE_EXTENSION_FUNCTION("webstorePrivate.installBundle", + WEBSTOREPRIVATE_INSTALLBUNDLE) + + WebstorePrivateInstallBundleFunction(); + + private: + ~WebstorePrivateInstallBundleFunction() override; + + // WebstorePrivateFunctionWithBundle: + void ProcessParams() override; + bool ShouldSkipItem(const std::string& id) const override; + void OnInstallApprovalHook() override; + + void OnInstallComplete(); +}; + +class WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction + : public WebstorePrivateFunctionWithBundle + { + public: + DECLARE_EXTENSION_FUNCTION( + "webstorePrivate.showPermissionPromptForDelegatedBundleInstall", + WEBSTOREPRIVATE_SHOWPERMISSIONPROMPTFORDELEGATEDBUNDLEINSTALL) + + WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction(); + + private: + ~WebstorePrivateShowPermissionPromptForDelegatedBundleInstallFunction() + override; + + // WebstorePrivateFunctionWithBundle: + void ProcessParams() override; + bool ShouldSkipItem(const std::string& id) const override; + void OnInstallApprovalHook() override; +}; + class WebstorePrivateEnableAppLauncherFunction : public UIThreadExtensionFunction { public: diff --git a/chrome/browser/extensions/bundle_installer.cc b/chrome/browser/extensions/bundle_installer.cc index 4c4af6ac63eb..136b5f027674 100644 --- a/chrome/browser/extensions/bundle_installer.cc +++ b/chrome/browser/extensions/bundle_installer.cc @@ -62,20 +62,22 @@ scoped_refptr CreateDummyExtension( return extension; } -// Holds the message IDs for BundleInstaller::GetHeadingTextFor. -const int kHeadingIds[3][4] = { - { - 0, - IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_TITLE_EXTENSIONS, - IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_TITLE_APPS, - IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_TITLE_EXTENSION_APPS - }, - { - 0, - IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSIONS, - IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_APPS, - IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSION_APPS - } +const int kHeadingIdsInstallPrompt[] = { + IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_TITLE_EXTENSIONS, + IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_TITLE_APPS, + IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_TITLE_EXTENSION_APPS +}; + +const int kHeadingIdsDelegatedInstallPrompt[] = { + IDS_EXTENSION_BUNDLE_DELEGATED_INSTALL_PROMPT_TITLE_EXTENSIONS, + IDS_EXTENSION_BUNDLE_DELEGATED_INSTALL_PROMPT_TITLE_APPS, + IDS_EXTENSION_BUNDLE_DELEGATED_INSTALL_PROMPT_TITLE_EXTENSION_APPS +}; + +const int kHeadingIdsInstalledBubble[] = { + IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSIONS, + IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_APPS, + IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSION_APPS }; } // namespace @@ -100,12 +102,14 @@ BundleInstaller::BundleInstaller(Browser* browser, const std::string& name, const SkBitmap& icon, const std::string& authuser, + const std::string& delegated_username, const BundleInstaller::ItemList& items) : approved_(false), browser_(browser), name_(name), icon_(icon), authuser_(authuser), + delegated_username_(delegated_username), host_desktop_type_(browser->host_desktop_type()), profile_(browser->profile()) { BrowserList::AddObserver(this); @@ -200,27 +204,29 @@ base::string16 BundleInstaller::GetHeadingTextFor(Item::State state) const { } size_t total = CountItemsWithState(state); + if (total == 0) + return base::string16(); + size_t apps = std::count_if( dummy_extensions_.begin(), dummy_extensions_.end(), [] (const scoped_refptr& ext) { return ext->is_app(); }); bool has_apps = apps > 0; bool has_extensions = apps < total; - size_t index = (has_extensions << 0) + (has_apps << 1); - - CHECK_LT(static_cast(state), arraysize(kHeadingIds)); - CHECK_LT(index, arraysize(kHeadingIds[state])); - - int msg_id = kHeadingIds[state][index]; - if (!msg_id) - return base::string16(); - - // Only the "pending" message (in the confirmation prompt) contains the - // bundle name. - if (state == Item::STATE_PENDING) - return l10n_util::GetStringFUTF16(msg_id, base::UTF8ToUTF16(name_)); - - return l10n_util::GetStringUTF16(msg_id); + size_t index = (has_extensions << 0) + (has_apps << 1) - 1; + + if (state == Item::STATE_PENDING) { + if (!delegated_username_.empty()) { + return l10n_util::GetStringFUTF16( + kHeadingIdsDelegatedInstallPrompt[index], base::UTF8ToUTF16(name_), + base::UTF8ToUTF16(delegated_username_)); + } else { + return l10n_util::GetStringFUTF16(kHeadingIdsInstallPrompt[index], + base::UTF8ToUTF16(name_)); + } + } else { + return l10n_util::GetStringUTF16(kHeadingIdsInstalledBubble[index]); + } } void BundleInstaller::ParseManifests() { @@ -277,7 +283,12 @@ void BundleInstaller::ShowPrompt() { if (browser) web_contents = browser->tab_strip_model()->GetActiveWebContents(); install_ui_.reset(new ExtensionInstallPrompt(web_contents)); - install_ui_->ConfirmBundleInstall(this, &icon_, permissions.get()); + if (delegated_username_.empty()) { + install_ui_->ConfirmBundleInstall(this, &icon_, permissions.get()); + } else { + install_ui_->ConfirmPermissionsForDelegatedBundleInstall( + this, delegated_username_, &icon_, permissions.get()); + } } } diff --git a/chrome/browser/extensions/bundle_installer.h b/chrome/browser/extensions/bundle_installer.h index a4f49740725f..61bf273aeb05 100644 --- a/chrome/browser/extensions/bundle_installer.h +++ b/chrome/browser/extensions/bundle_installer.h @@ -83,9 +83,10 @@ class BundleInstaller : public WebstoreInstallHelper::Delegate, typedef std::vector ItemList; BundleInstaller(Browser* browser, - const std::string& name, + const std::string& localized_name, const SkBitmap& icon, const std::string& authuser, + const std::string& delegated_username, const ItemList& items); ~BundleInstaller() override; @@ -193,6 +194,10 @@ class BundleInstaller : public WebstoreInstallHelper::Delegate, // requests. May be empty. std::string authuser_; + // The display name of the user for which this install happens, in the case + // of delegated installs. Empty for regular installs. + std::string delegated_username_; + // The desktop type of the browser. chrome::HostDesktopType host_desktop_type_; diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc index 433d7da5de00..6c093e1dba7a 100644 --- a/chrome/browser/extensions/extension_install_prompt.cc +++ b/chrome/browser/extensions/extension_install_prompt.cc @@ -76,6 +76,7 @@ static const int kTitleIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_EXTENSION_REMOTE_INSTALL_PROMPT_TITLE, IDS_EXTENSION_REPAIR_PROMPT_TITLE, IDS_EXTENSION_DELEGATED_INSTALL_PROMPT_TITLE, + 0, // Heading for delegated bundle installs depends on the bundle contents. }; static const int kButtons[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, @@ -92,6 +93,7 @@ static const int kButtons[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, + ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL, }; static const int kAcceptButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 0, // Regular installs use different strings for extensions/apps/themes. @@ -105,6 +107,7 @@ static const int kAcceptButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { 0, // Remote installs use different strings for extensions/apps. 0, // Repairs use different strings for extensions/apps. 0, // Delegated installs use different strings for extensions/apps/themes. + IDS_EXTENSION_PROMPT_INSTALL_BUTTON, }; static const int kAbortButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_CANCEL, @@ -118,6 +121,7 @@ static const int kAbortButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { IDS_CANCEL, IDS_CANCEL, IDS_CANCEL, + IDS_CANCEL, }; static const int kPermissionsHeaderIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = { @@ -132,6 +136,7 @@ static const int IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, IDS_EXTENSION_PROMPT_CAN_ACCESS, IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, + IDS_EXTENSION_PROMPT_THESE_WILL_HAVE_ACCESS_TO, }; // Returns bitmap for the default icon with size equal to the default icon's @@ -219,6 +224,8 @@ std::string ExtensionInstallPrompt::PromptTypeToString(PromptType type) { return "REPAIR_PROMPT"; case ExtensionInstallPrompt::DELEGATED_PERMISSIONS_PROMPT: return "DELEGATED_PERMISSIONS_PROMPT"; + case ExtensionInstallPrompt::DELEGATED_BUNDLE_PERMISSIONS_PROMPT: + return "DELEGATED_BUNDLE_PERMISSIONS_PROMPT"; case ExtensionInstallPrompt::UNSET_PROMPT_TYPE: case ExtensionInstallPrompt::NUM_PROMPT_TYPES: break; @@ -305,7 +312,8 @@ void ExtensionInstallPrompt::Prompt::SetWebstoreData( base::string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const { int id = kTitleIds[type_]; - if (type_ == BUNDLE_INSTALL_PROMPT) { + if (type_ == BUNDLE_INSTALL_PROMPT || + type_ == DELEGATED_BUNDLE_PERMISSIONS_PROMPT) { return bundle_->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING); } if (type_ == DELEGATED_PERMISSIONS_PROMPT) { @@ -635,6 +643,22 @@ void ExtensionInstallPrompt::ConfirmBundleInstall( ShowConfirmation(); } +void ExtensionInstallPrompt::ConfirmPermissionsForDelegatedBundleInstall( + extensions::BundleInstaller* bundle, + const std::string& delegated_username, + const SkBitmap* icon, + const extensions::PermissionSet* permissions) { + DCHECK(ui_loop_ == base::MessageLoop::current()); + bundle_ = bundle; + delegated_username_ = delegated_username; + custom_permissions_ = permissions; + delegate_ = bundle; + prompt_ = new Prompt(DELEGATED_BUNDLE_PERMISSIONS_PROMPT); + + SetIcon(icon); + ShowConfirmation(); +} + void ExtensionInstallPrompt::ConfirmStandaloneInstall( Delegate* delegate, const Extension* extension, @@ -843,7 +867,8 @@ void ExtensionInstallPrompt::ShowConfirmation() { extension_->permissions_data()->active_permissions(); // For delegated installs, all optional permissions are pre-approved by the // person who triggers the install, so add them to the list. - if (prompt_->type() == DELEGATED_PERMISSIONS_PROMPT) { + if (prompt_->type() == DELEGATED_PERMISSIONS_PROMPT || + prompt_->type() == DELEGATED_BUNDLE_PERMISSIONS_PROMPT) { scoped_refptr optional_permissions = extensions::PermissionsParser::GetOptionalPermissions(extension_); permissions_to_display = PermissionSet::CreateUnion( @@ -887,10 +912,10 @@ void ExtensionInstallPrompt::ShowConfirmation() { case REPAIR_PROMPT: case DELEGATED_PERMISSIONS_PROMPT: { prompt_->set_extension(extension_); - prompt_->set_delegated_username(delegated_username_); break; } - case BUNDLE_INSTALL_PROMPT: { + case BUNDLE_INSTALL_PROMPT: + case DELEGATED_BUNDLE_PERMISSIONS_PROMPT: { prompt_->set_bundle(bundle_); break; } @@ -898,6 +923,7 @@ void ExtensionInstallPrompt::ShowConfirmation() { NOTREACHED() << "Unknown message"; return; } + prompt_->set_delegated_username(delegated_username_); prompt_->set_icon(gfx::Image::CreateFrom1xBitmap(icon_)); g_last_prompt_type_for_tests = prompt_->type(); diff --git a/chrome/browser/extensions/extension_install_prompt.h b/chrome/browser/extensions/extension_install_prompt.h index 32243c5aba08..a931e5f7673f 100644 --- a/chrome/browser/extensions/extension_install_prompt.h +++ b/chrome/browser/extensions/extension_install_prompt.h @@ -67,6 +67,7 @@ class ExtensionInstallPrompt REMOTE_INSTALL_PROMPT, REPAIR_PROMPT, DELEGATED_PERMISSIONS_PROMPT, + DELEGATED_BUNDLE_PERMISSIONS_PROMPT, NUM_PROMPT_TYPES }; @@ -151,7 +152,8 @@ class ExtensionInstallPrompt size_t GetRetainedDeviceCount() const; base::string16 GetRetainedDeviceMessageString(size_t index) const; - // Populated for BUNDLE_INSTALL_PROMPT. + // Populated for BUNDLE_INSTALL_PROMPT and + // DELEGATED_BUNDLE_PERMISSIONS_PROMPT. const extensions::BundleInstaller* bundle() const { return bundle_; } void set_bundle(const extensions::BundleInstaller* bundle) { bundle_ = bundle; @@ -303,12 +305,22 @@ class ExtensionInstallPrompt // This is called by the bundle installer to verify whether the bundle // should be installed. // - // We *MUST* eventually call either Proceed() or Abort() on |delegate|. + // We *MUST* eventually call either Proceed() or Abort() on |bundle|. virtual void ConfirmBundleInstall( extensions::BundleInstaller* bundle, const SkBitmap* icon, const extensions::PermissionSet* permissions); + // This is called by the bundle installer to verify the permissions for a + // delegated bundle install. + // + // We *MUST* eventually call either Proceed() or Abort() on |bundle|. + virtual void ConfirmPermissionsForDelegatedBundleInstall( + extensions::BundleInstaller* bundle, + const std::string& delegated_username, + const SkBitmap* icon, + const extensions::PermissionSet* permissions); + // This is called by the standalone installer to verify whether the install // from the webstore should proceed. // @@ -427,14 +439,15 @@ class ExtensionInstallPrompt SkBitmap icon_; // The extension we are showing the UI for, if type is not - // BUNDLE_INSTALL_PROMPT. + // BUNDLE_INSTALL_PROMPT or DELEGATED_BUNDLE_PERMISSIONS_PROMPT. const extensions::Extension* extension_; - // The bundle we are showing the UI for, if type BUNDLE_INSTALL_PROMPT. + // The bundle we are showing the UI for, if type BUNDLE_INSTALL_PROMPT or + // DELEGATED_BUNDLE_PERMISSIONS_PROMPT. const extensions::BundleInstaller* bundle_; // The name of the user we are asking about, if type - // DELEGATED_PERMISSIONS_PROMPT. + // DELEGATED_PERMISSIONS_PROMPT or DELEGATED_BUNDLE_PERMISSIONS_PROMPT. std::string delegated_username_; // A custom set of permissions to show in the install prompt instead of the diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h index 5729635536d7..e00417d4999d 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h @@ -96,7 +96,9 @@ class ExtensionInstallDialogView : public views::DialogDelegateView, int column_set_id) const; bool is_bundle_install() const { - return prompt_->type() == ExtensionInstallPrompt::BUNDLE_INSTALL_PROMPT; + return prompt_->type() == ExtensionInstallPrompt::BUNDLE_INSTALL_PROMPT || + prompt_->type() == + ExtensionInstallPrompt::DELEGATED_BUNDLE_PERMISSIONS_PROMPT; } bool is_external_install() const { diff --git a/chrome/common/extensions/api/webstore_private.json b/chrome/common/extensions/api/webstore_private.json index aa6642c139ef..81463883873e 100644 --- a/chrome/common/extensions/api/webstore_private.json +++ b/chrome/common/extensions/api/webstore_private.json @@ -295,7 +295,70 @@ { "name": "callback", "type": "function", - "description": "Called when the install process completes. Upon failures, chrome.runtime.lastError will be set to 'Invalid id', 'User cancelled install', or 'Unknown error'.", + "description": "Called when the install process completes. Upon failures, chrome.runtime.lastError will be set to 'Invalid bundle', 'Invalid icon url', 'This item is already installed', or 'User cancelled install'.", + "optional": true, + "parameters": [] + } + ] + }, + { + "name": "showPermissionPromptForDelegatedBundleInstall", + "description": "Shows a permission prompt for the given bundle, for installing to a different account.", + "parameters": [ + { + "name": "details", + "type": "object", + "properties": { + "localizedName": { + "type": "string", + "description": "A title to use for display in a confirmation dialog." + }, + "delegatedUser": { + "type": "string", + "description": "The display name of the user for whom the extension should be installed." + }, + "iconUrl": { + "type": "string", + "optional": true, + "desciption": "A URL for the image to display in the confirmation dialog" + } + }, + "additionalProperties": { "type": "any" } + }, + { + "name": "contents", + "description": "An array of extension details to be installed.", + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The id of the extension to be installed.", + "minLength": 32, + "maxLength": 32 + }, + "manifest": { + "type": "string", + "description": "A string with the contents of the extension's manifest.json file. During the install process, the browser will check that the downloaded extension's manifest matches what was passed in here.", + "minLength": 1 + }, + "iconUrl": { + "type": "string", + "optional": true, + "desciption": "A URL for the image to display in the confirmation dialog" + }, + "localizedName": { + "type": "string", + "description": "A string to use instead of the raw value of the 'name' key from manifest.json." + } + } + } + }, + { + "name": "callback", + "type": "function", + "description": "Called when the install process completes. Upon failures, chrome.runtime.lastError will be set to 'Invalid bundle', 'Invalid icon url', or 'User cancelled install'.", "optional": true, "parameters": [] } diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index c18b8c8774fa..98e6850a7c80 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h @@ -1117,6 +1117,7 @@ enum HistogramValue { EXTENSIONVIEWINTERNAL_PARSESRC, HID_GETUSERSELECTEDDEVICES, FILESYSTEMPROVIDERINTERNAL_GETACTIONSREQUESTEDSUCCESS, + WEBSTOREPRIVATE_SHOWPERMISSIONPROMPTFORDELEGATEDBUNDLEINSTALL, // Last entry: Add new entries above and ensure to update // tools/metrics/histograms/histograms.xml. ENUM_BOUNDARY diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 03d3f6d6bdb2..428012cccd79 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -54830,6 +54830,8 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. + -- 2.11.4.GIT