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