webstore_result.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/app_list/search/webstore/webstore_result.h" 6 7#include <vector> 8 9#include "base/bind.h" 10#include "base/memory/ref_counted.h" 11#include "base/strings/utf_string_conversions.h" 12#include "chrome/browser/apps/ephemeral_app_launcher.h" 13#include "chrome/browser/extensions/extension_service.h" 14#include "chrome/browser/extensions/install_tracker.h" 15#include "chrome/browser/extensions/install_tracker_factory.h" 16#include "chrome/browser/profiles/profile.h" 17#include "chrome/browser/ui/app_list/app_list_controller_delegate.h" 18#include "chrome/browser/ui/app_list/search/common/url_icon_source.h" 19#include "chrome/browser/ui/app_list/search/webstore/webstore_installer.h" 20#include "chrome/browser/ui/browser_navigator.h" 21#include "chrome/browser/ui/extensions/application_launch.h" 22#include "extensions/browser/extension_registry.h" 23#include "extensions/browser/extension_system.h" 24#include "extensions/browser/extension_util.h" 25#include "extensions/common/extension.h" 26#include "grit/chromium_strings.h" 27#include "grit/generated_resources.h" 28#include "grit/theme_resources.h" 29#include "net/base/url_util.h" 30#include "ui/base/l10n/l10n_util.h" 31#include "ui/base/resource/resource_bundle.h" 32#include "ui/gfx/canvas.h" 33#include "ui/gfx/image/canvas_image_source.h" 34 35namespace { 36 37const int kIconSize = 32; 38const int kLaunchEphemeralAppAction = 1; 39 40// BadgedImageSource adds a webstore badge to a webstore app icon. 41class BadgedIconSource : public gfx::CanvasImageSource { 42 public: 43 explicit BadgedIconSource(const gfx::ImageSkia& icon) 44 : CanvasImageSource(gfx::Size(kIconSize, kIconSize), false), 45 icon_(icon) { 46 } 47 48 virtual void Draw(gfx::Canvas* canvas) OVERRIDE { 49 canvas->DrawImageInt(icon_, 0, 0); 50 const gfx::ImageSkia& badge = *ui::ResourceBundle::GetSharedInstance(). 51 GetImageSkiaNamed(IDR_WEBSTORE_ICON_16); 52 canvas->DrawImageInt( 53 badge, icon_.width() - badge.width(), icon_.height() - badge.height()); 54 } 55 56 private: 57 gfx::ImageSkia icon_; 58 59 DISALLOW_COPY_AND_ASSIGN(BadgedIconSource); 60}; 61 62} // namespace 63 64namespace app_list { 65 66WebstoreResult::WebstoreResult(Profile* profile, 67 const std::string& app_id, 68 const std::string& localized_name, 69 const GURL& icon_url, 70 extensions::Manifest::Type item_type, 71 AppListControllerDelegate* controller) 72 : profile_(profile), 73 app_id_(app_id), 74 localized_name_(localized_name), 75 icon_url_(icon_url), 76 item_type_(item_type), 77 controller_(controller), 78 install_tracker_(NULL), 79 extension_registry_(NULL), 80 weak_factory_(this) { 81 set_id(extensions::Extension::GetBaseURLFromExtensionId(app_id_).spec()); 82 set_relevance(0.0); // What is the right value to use? 83 84 set_title(base::UTF8ToUTF16(localized_name_)); 85 SetDefaultDetails(); 86 87 InitAndStartObserving(); 88 UpdateActions(); 89 90 icon_ = gfx::ImageSkia( 91 new UrlIconSource(base::Bind(&WebstoreResult::OnIconLoaded, 92 weak_factory_.GetWeakPtr()), 93 profile_->GetRequestContext(), 94 icon_url_, 95 kIconSize, 96 IDR_WEBSTORE_ICON_32), 97 gfx::Size(kIconSize, kIconSize)); 98 SetIcon(icon_); 99} 100 101WebstoreResult::~WebstoreResult() { 102 StopObservingInstall(); 103 StopObservingRegistry(); 104} 105 106void WebstoreResult::Open(int event_flags) { 107 const GURL store_url = net::AppendQueryParameter( 108 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + app_id_), 109 extension_urls::kWebstoreSourceField, 110 extension_urls::kLaunchSourceAppListSearch); 111 112 chrome::NavigateParams params(profile_, 113 store_url, 114 content::PAGE_TRANSITION_LINK); 115 params.disposition = ui::DispositionFromEventFlags(event_flags); 116 chrome::Navigate(¶ms); 117} 118 119void WebstoreResult::InvokeAction(int action_index, int event_flags) { 120 StartInstall(action_index == kLaunchEphemeralAppAction); 121} 122 123scoped_ptr<ChromeSearchResult> WebstoreResult::Duplicate() { 124 return scoped_ptr<ChromeSearchResult>(new WebstoreResult(profile_, 125 app_id_, 126 localized_name_, 127 icon_url_, 128 item_type_, 129 controller_)).Pass(); 130} 131 132void WebstoreResult::InitAndStartObserving() { 133 DCHECK(!install_tracker_ && !extension_registry_); 134 135 install_tracker_ = 136 extensions::InstallTrackerFactory::GetForBrowserContext(profile_); 137 extension_registry_ = extensions::ExtensionRegistry::Get(profile_); 138 139 const extensions::ActiveInstallData* install_data = 140 install_tracker_->GetActiveInstall(app_id_); 141 if (install_data) { 142 SetPercentDownloaded(install_data->percent_downloaded); 143 SetIsInstalling(true); 144 } 145 146 install_tracker_->AddObserver(this); 147 extension_registry_->AddObserver(this); 148} 149 150void WebstoreResult::UpdateActions() { 151 Actions actions; 152 153 const bool is_otr = profile_->IsOffTheRecord(); 154 const bool is_installed = 155 extensions::util::IsExtensionInstalledPermanently(app_id_, profile_); 156 157 if (!is_otr && !is_installed && !is_installing()) { 158 if (EphemeralAppLauncher::IsFeatureEnabled()) { 159 actions.push_back(Action( 160 l10n_util::GetStringUTF16(IDS_WEBSTORE_RESULT_INSTALL), 161 l10n_util::GetStringUTF16( 162 IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE))); 163 164 if (item_type_ == extensions::Manifest::TYPE_PLATFORM_APP || 165 item_type_ == extensions::Manifest::TYPE_HOSTED_APP) { 166 actions.push_back(Action( 167 l10n_util::GetStringUTF16(IDS_WEBSTORE_RESULT_LAUNCH), 168 l10n_util::GetStringUTF16(IDS_WEBSTORE_RESULT_LAUNCH_APP_TOOLTIP))); 169 } 170 } else { 171 actions.push_back(Action( 172 l10n_util::GetStringUTF16(IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE), 173 base::string16())); 174 } 175 } 176 177 SetActions(actions); 178} 179 180void WebstoreResult::SetDefaultDetails() { 181 const base::string16 details = 182 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE); 183 Tags details_tags; 184 details_tags.push_back(Tag(SearchResult::Tag::DIM, 0, details.length())); 185 186 set_details(details); 187 set_details_tags(details_tags); 188} 189 190void WebstoreResult::OnIconLoaded() { 191 // Remove the existing image reps since the icon data is loaded and they 192 // need to be re-created. 193 const std::vector<gfx::ImageSkiaRep>& image_reps = icon_.image_reps(); 194 for (size_t i = 0; i < image_reps.size(); ++i) 195 icon_.RemoveRepresentation(image_reps[i].scale()); 196 197 icon_ = gfx::ImageSkia(new BadgedIconSource(icon_), 198 gfx::Size(kIconSize, kIconSize)); 199 200 SetIcon(icon_); 201} 202 203void WebstoreResult::StartInstall(bool launch_ephemeral_app) { 204 SetPercentDownloaded(0); 205 SetIsInstalling(true); 206 207 if (launch_ephemeral_app) { 208 scoped_refptr<EphemeralAppLauncher> installer = 209 EphemeralAppLauncher::CreateForLauncher( 210 app_id_, 211 profile_, 212 controller_->GetAppListWindow(), 213 base::Bind(&WebstoreResult::LaunchCallback, 214 weak_factory_.GetWeakPtr())); 215 installer->Start(); 216 return; 217 } 218 219 scoped_refptr<WebstoreInstaller> installer = 220 new WebstoreInstaller( 221 app_id_, 222 profile_, 223 controller_->GetAppListWindow(), 224 base::Bind(&WebstoreResult::InstallCallback, 225 weak_factory_.GetWeakPtr())); 226 installer->BeginInstall(); 227} 228 229void WebstoreResult::InstallCallback( 230 bool success, 231 const std::string& error, 232 extensions::webstore_install::Result result) { 233 if (!success) { 234 LOG(ERROR) << "Failed to install app, error=" << error; 235 SetIsInstalling(false); 236 return; 237 } 238 239 // Success handling is continued in OnExtensionInstalled. 240 SetPercentDownloaded(100); 241} 242 243void WebstoreResult::LaunchCallback(extensions::webstore_install::Result result, 244 const std::string& error) { 245 if (result != extensions::webstore_install::SUCCESS) 246 LOG(ERROR) << "Failed to launch app, error=" << error; 247 248 SetIsInstalling(false); 249} 250 251void WebstoreResult::StopObservingInstall() { 252 if (install_tracker_) 253 install_tracker_->RemoveObserver(this); 254 install_tracker_ = NULL; 255} 256 257void WebstoreResult::StopObservingRegistry() { 258 if (extension_registry_) 259 extension_registry_->RemoveObserver(this); 260 extension_registry_ = NULL; 261} 262 263void WebstoreResult::OnDownloadProgress(const std::string& extension_id, 264 int percent_downloaded) { 265 if (extension_id != app_id_ || percent_downloaded < 0) 266 return; 267 268 SetPercentDownloaded(percent_downloaded); 269} 270 271void WebstoreResult::OnExtensionInstalled( 272 content::BrowserContext* browser_context, 273 const extensions::Extension* extension, 274 bool is_update) { 275 if (extension->id() != app_id_) 276 return; 277 278 SetIsInstalling(false); 279 UpdateActions(); 280 281 if (extensions::util::IsExtensionInstalledPermanently(extension->id(), 282 profile_)) { 283 NotifyItemInstalled(); 284 } 285} 286 287void WebstoreResult::OnShutdown() { 288 StopObservingInstall(); 289} 290 291void WebstoreResult::OnShutdown(extensions::ExtensionRegistry* registry) { 292 StopObservingRegistry(); 293} 294 295ChromeSearchResultType WebstoreResult::GetType() { 296 return SEARCH_WEBSTORE_SEARCH_RESULT; 297} 298 299} // namespace app_list 300