webstore_result.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 kLaunchEphemeralAppAction = 1; 38 39// BadgedImageSource adds a webstore badge to a webstore app icon. 40class BadgedIconSource : public gfx::CanvasImageSource { 41 public: 42 BadgedIconSource(const gfx::ImageSkia& icon, const gfx::Size& icon_size) 43 : CanvasImageSource(icon_size, false), icon_(icon) {} 44 45 virtual void Draw(gfx::Canvas* canvas) OVERRIDE { 46 canvas->DrawImageInt(icon_, 0, 0); 47 const gfx::ImageSkia& badge = *ui::ResourceBundle::GetSharedInstance(). 48 GetImageSkiaNamed(IDR_WEBSTORE_ICON_16); 49 canvas->DrawImageInt( 50 badge, icon_.width() - badge.width(), icon_.height() - badge.height()); 51 } 52 53 private: 54 gfx::ImageSkia icon_; 55 56 DISALLOW_COPY_AND_ASSIGN(BadgedIconSource); 57}; 58 59} // namespace 60 61namespace app_list { 62 63WebstoreResult::WebstoreResult(Profile* profile, 64 const std::string& app_id, 65 const std::string& localized_name, 66 const GURL& icon_url, 67 bool is_paid, 68 extensions::Manifest::Type item_type, 69 AppListControllerDelegate* controller) 70 : profile_(profile), 71 app_id_(app_id), 72 localized_name_(localized_name), 73 icon_url_(icon_url), 74 is_paid_(is_paid), 75 item_type_(item_type), 76 controller_(controller), 77 install_tracker_(NULL), 78 extension_registry_(NULL), 79 weak_factory_(this) { 80 set_id(extensions::Extension::GetBaseURLFromExtensionId(app_id_).spec()); 81 set_relevance(0.0); // What is the right value to use? 82 83 set_title(base::UTF8ToUTF16(localized_name_)); 84 SetDefaultDetails(); 85 86 InitAndStartObserving(); 87 UpdateActions(); 88 89 int icon_dimension = GetPreferredIconDimension(); 90 icon_ = gfx::ImageSkia( 91 new UrlIconSource( 92 base::Bind(&WebstoreResult::OnIconLoaded, weak_factory_.GetWeakPtr()), 93 profile_->GetRequestContext(), 94 icon_url_, 95 icon_dimension, 96 IDR_WEBSTORE_ICON_32), 97 gfx::Size(icon_dimension, icon_dimension)); 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 if (is_paid_) { 121 // Paid apps cannot be installed directly from the launcher. Instead, open 122 // the webstore page for the app. 123 Open(event_flags); 124 return; 125 } 126 127 StartInstall(action_index == kLaunchEphemeralAppAction); 128} 129 130scoped_ptr<ChromeSearchResult> WebstoreResult::Duplicate() { 131 return scoped_ptr<ChromeSearchResult>(new WebstoreResult(profile_, 132 app_id_, 133 localized_name_, 134 icon_url_, 135 is_paid_, 136 item_type_, 137 controller_)).Pass(); 138} 139 140void WebstoreResult::InitAndStartObserving() { 141 DCHECK(!install_tracker_ && !extension_registry_); 142 143 install_tracker_ = 144 extensions::InstallTrackerFactory::GetForBrowserContext(profile_); 145 extension_registry_ = extensions::ExtensionRegistry::Get(profile_); 146 147 const extensions::ActiveInstallData* install_data = 148 install_tracker_->GetActiveInstall(app_id_); 149 if (install_data) { 150 SetPercentDownloaded(install_data->percent_downloaded); 151 SetIsInstalling(true); 152 } 153 154 install_tracker_->AddObserver(this); 155 extension_registry_->AddObserver(this); 156} 157 158void WebstoreResult::UpdateActions() { 159 Actions actions; 160 161 const bool is_otr = profile_->IsOffTheRecord(); 162 const bool is_installed = 163 extensions::util::IsExtensionInstalledPermanently(app_id_, profile_); 164 165 if (!is_otr && !is_installed && !is_installing()) { 166 if (EphemeralAppLauncher::IsFeatureEnabled()) { 167 actions.push_back(Action( 168 l10n_util::GetStringUTF16(IDS_WEBSTORE_RESULT_INSTALL), 169 l10n_util::GetStringUTF16( 170 IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE))); 171 if ((item_type_ == extensions::Manifest::TYPE_PLATFORM_APP || 172 item_type_ == extensions::Manifest::TYPE_HOSTED_APP) && 173 !is_paid_) { 174 actions.push_back(Action( 175 l10n_util::GetStringUTF16(IDS_WEBSTORE_RESULT_LAUNCH), 176 l10n_util::GetStringUTF16(IDS_WEBSTORE_RESULT_LAUNCH_APP_TOOLTIP))); 177 } 178 } else { 179 actions.push_back(Action( 180 l10n_util::GetStringUTF16(IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE), 181 base::string16())); 182 } 183 } 184 185 SetActions(actions); 186} 187 188void WebstoreResult::SetDefaultDetails() { 189 const base::string16 details = 190 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE); 191 Tags details_tags; 192 details_tags.push_back(Tag(SearchResult::Tag::DIM, 0, details.length())); 193 194 set_details(details); 195 set_details_tags(details_tags); 196} 197 198void WebstoreResult::OnIconLoaded() { 199 // Remove the existing image reps since the icon data is loaded and they 200 // need to be re-created. 201 const std::vector<gfx::ImageSkiaRep>& image_reps = icon_.image_reps(); 202 for (size_t i = 0; i < image_reps.size(); ++i) 203 icon_.RemoveRepresentation(image_reps[i].scale()); 204 int icon_dimension = GetPreferredIconDimension(); 205 gfx::Size icon_size(icon_dimension, icon_dimension); 206 icon_ = gfx::ImageSkia(new BadgedIconSource(icon_, icon_size), icon_size); 207 208 SetIcon(icon_); 209} 210 211void WebstoreResult::StartInstall(bool launch_ephemeral_app) { 212 SetPercentDownloaded(0); 213 SetIsInstalling(true); 214 215 if (launch_ephemeral_app) { 216 scoped_refptr<EphemeralAppLauncher> installer = 217 EphemeralAppLauncher::CreateForLauncher( 218 app_id_, 219 profile_, 220 controller_->GetAppListWindow(), 221 base::Bind(&WebstoreResult::LaunchCallback, 222 weak_factory_.GetWeakPtr())); 223 installer->Start(); 224 return; 225 } 226 227 scoped_refptr<WebstoreInstaller> installer = 228 new WebstoreInstaller( 229 app_id_, 230 profile_, 231 controller_->GetAppListWindow(), 232 base::Bind(&WebstoreResult::InstallCallback, 233 weak_factory_.GetWeakPtr())); 234 installer->BeginInstall(); 235} 236 237void WebstoreResult::InstallCallback( 238 bool success, 239 const std::string& error, 240 extensions::webstore_install::Result result) { 241 if (!success) { 242 LOG(ERROR) << "Failed to install app, error=" << error; 243 SetIsInstalling(false); 244 return; 245 } 246 247 // Success handling is continued in OnExtensionInstalled. 248 SetPercentDownloaded(100); 249} 250 251void WebstoreResult::LaunchCallback(extensions::webstore_install::Result result, 252 const std::string& error) { 253 if (result != extensions::webstore_install::SUCCESS) 254 LOG(ERROR) << "Failed to launch app, error=" << error; 255 256 SetIsInstalling(false); 257} 258 259void WebstoreResult::StopObservingInstall() { 260 if (install_tracker_) 261 install_tracker_->RemoveObserver(this); 262 install_tracker_ = NULL; 263} 264 265void WebstoreResult::StopObservingRegistry() { 266 if (extension_registry_) 267 extension_registry_->RemoveObserver(this); 268 extension_registry_ = NULL; 269} 270 271void WebstoreResult::OnDownloadProgress(const std::string& extension_id, 272 int percent_downloaded) { 273 if (extension_id != app_id_ || percent_downloaded < 0) 274 return; 275 276 SetPercentDownloaded(percent_downloaded); 277} 278 279void WebstoreResult::OnExtensionInstalled( 280 content::BrowserContext* browser_context, 281 const extensions::Extension* extension, 282 bool is_update) { 283 if (extension->id() != app_id_) 284 return; 285 286 SetIsInstalling(false); 287 UpdateActions(); 288 289 if (extensions::util::IsExtensionInstalledPermanently(extension->id(), 290 profile_)) { 291 NotifyItemInstalled(); 292 } 293} 294 295void WebstoreResult::OnShutdown() { 296 StopObservingInstall(); 297} 298 299void WebstoreResult::OnShutdown(extensions::ExtensionRegistry* registry) { 300 StopObservingRegistry(); 301} 302 303ChromeSearchResultType WebstoreResult::GetType() { 304 return SEARCH_WEBSTORE_SEARCH_RESULT; 305} 306 307} // namespace app_list 308