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