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.
5 #include "chrome/browser/ui/app_list/search/webstore/webstore_result.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/search_util.h"
20 #include "chrome/browser/ui/app_list/search/webstore/webstore_installer.h"
21 #include "chrome/browser/ui/extensions/application_launch.h"
22 #include "chrome/grit/chromium_strings.h"
23 #include "chrome/grit/generated_resources.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 "extensions/common/extension_urls.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"
38 const int kLaunchEphemeralAppAction = 1;
40 // BadgedImageSource adds a webstore badge to a webstore app icon.
41 class BadgedIconSource : public gfx::CanvasImageSource {
43 BadgedIconSource(const gfx::ImageSkia& icon, const gfx::Size& icon_size)
44 : CanvasImageSource(icon_size, false), icon_(icon) {}
46 void Draw(gfx::Canvas* canvas) override {
47 canvas->DrawImageInt(icon_, 0, 0);
48 const gfx::ImageSkia& badge = *ui::ResourceBundle::GetSharedInstance().
49 GetImageSkiaNamed(IDR_WEBSTORE_ICON_16);
51 badge, icon_.width() - badge.width(), icon_.height() - badge.height());
57 DISALLOW_COPY_AND_ASSIGN(BadgedIconSource);
64 WebstoreResult::WebstoreResult(Profile* profile,
65 const std::string& app_id,
66 const std::string& localized_name,
69 extensions::Manifest::Type item_type,
70 AppListControllerDelegate* controller)
73 localized_name_(localized_name),
76 item_type_(item_type),
77 controller_(controller),
78 install_tracker_(NULL),
79 extension_registry_(NULL),
81 set_id(extensions::Extension::GetBaseURLFromExtensionId(app_id_).spec());
82 set_relevance(0.0); // What is the right value to use?
84 set_title(base::UTF8ToUTF16(localized_name_));
87 InitAndStartObserving();
90 int icon_dimension = GetPreferredIconDimension();
91 icon_ = gfx::ImageSkia(
93 base::Bind(&WebstoreResult::OnIconLoaded, weak_factory_.GetWeakPtr()),
94 profile_->GetRequestContext(),
97 IDR_WEBSTORE_ICON_32),
98 gfx::Size(icon_dimension, icon_dimension));
102 WebstoreResult::~WebstoreResult() {
103 StopObservingInstall();
104 StopObservingRegistry();
107 void WebstoreResult::Open(int event_flags) {
108 RecordHistogram(SEARCH_WEBSTORE_SEARCH_RESULT);
109 const GURL store_url = net::AppendQueryParameter(
110 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + app_id_),
111 extension_urls::kWebstoreSourceField,
112 extension_urls::kLaunchSourceAppListSearch);
114 controller_->OpenURL(profile_,
116 ui::PAGE_TRANSITION_LINK,
117 ui::DispositionFromEventFlags(event_flags));
120 void WebstoreResult::InvokeAction(int action_index, int event_flags) {
122 // Paid apps cannot be installed directly from the launcher. Instead, open
123 // the webstore page for the app.
128 StartInstall(action_index == kLaunchEphemeralAppAction);
131 scoped_ptr<SearchResult> WebstoreResult::Duplicate() {
132 return scoped_ptr<SearchResult>(new WebstoreResult(profile_,
141 void WebstoreResult::InitAndStartObserving() {
142 DCHECK(!install_tracker_ && !extension_registry_);
145 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
146 extension_registry_ = extensions::ExtensionRegistry::Get(profile_);
148 const extensions::ActiveInstallData* install_data =
149 install_tracker_->GetActiveInstall(app_id_);
151 SetPercentDownloaded(install_data->percent_downloaded);
152 SetIsInstalling(true);
155 install_tracker_->AddObserver(this);
156 extension_registry_->AddObserver(this);
159 void WebstoreResult::UpdateActions() {
162 const bool is_otr = profile_->IsOffTheRecord();
163 const bool is_installed =
164 extensions::util::IsExtensionInstalledPermanently(app_id_, profile_);
166 if (!is_otr && !is_installed && !is_installing()) {
167 if (EphemeralAppLauncher::IsFeatureEnabled()) {
168 actions.push_back(Action(
169 l10n_util::GetStringUTF16(IDS_WEBSTORE_RESULT_INSTALL),
170 l10n_util::GetStringUTF16(
171 IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE)));
172 if ((item_type_ == extensions::Manifest::TYPE_PLATFORM_APP ||
173 item_type_ == extensions::Manifest::TYPE_HOSTED_APP) &&
175 actions.push_back(Action(
176 l10n_util::GetStringUTF16(IDS_WEBSTORE_RESULT_LAUNCH),
177 l10n_util::GetStringUTF16(IDS_WEBSTORE_RESULT_LAUNCH_APP_TOOLTIP)));
180 actions.push_back(Action(
181 l10n_util::GetStringUTF16(IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE),
189 void WebstoreResult::SetDefaultDetails() {
190 const base::string16 details =
191 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE);
193 details_tags.push_back(Tag(SearchResult::Tag::DIM, 0, details.length()));
195 set_details(details);
196 set_details_tags(details_tags);
199 void WebstoreResult::OnIconLoaded() {
200 // Remove the existing image reps since the icon data is loaded and they
201 // need to be re-created.
202 const std::vector<gfx::ImageSkiaRep>& image_reps = icon_.image_reps();
203 for (size_t i = 0; i < image_reps.size(); ++i)
204 icon_.RemoveRepresentation(image_reps[i].scale());
205 int icon_dimension = GetPreferredIconDimension();
206 gfx::Size icon_size(icon_dimension, icon_dimension);
207 icon_ = gfx::ImageSkia(new BadgedIconSource(icon_, icon_size), icon_size);
212 void WebstoreResult::StartInstall(bool launch_ephemeral_app) {
213 SetPercentDownloaded(0);
214 SetIsInstalling(true);
216 if (launch_ephemeral_app) {
217 scoped_refptr<EphemeralAppLauncher> installer =
218 EphemeralAppLauncher::CreateForLauncher(
221 controller_->GetAppListWindow(),
222 base::Bind(&WebstoreResult::LaunchCallback,
223 weak_factory_.GetWeakPtr()));
228 scoped_refptr<WebstoreInstaller> installer =
229 new WebstoreInstaller(
232 controller_->GetAppListWindow(),
233 base::Bind(&WebstoreResult::InstallCallback,
234 weak_factory_.GetWeakPtr()));
235 installer->BeginInstall();
238 void WebstoreResult::InstallCallback(
240 const std::string& error,
241 extensions::webstore_install::Result result) {
243 LOG(ERROR) << "Failed to install app, error=" << error;
244 SetIsInstalling(false);
248 // Success handling is continued in OnExtensionInstalled.
249 SetPercentDownloaded(100);
252 void WebstoreResult::LaunchCallback(extensions::webstore_install::Result result,
253 const std::string& error) {
254 if (result != extensions::webstore_install::SUCCESS)
255 LOG(ERROR) << "Failed to launch app, error=" << error;
257 SetIsInstalling(false);
260 void WebstoreResult::StopObservingInstall() {
261 if (install_tracker_)
262 install_tracker_->RemoveObserver(this);
263 install_tracker_ = NULL;
266 void WebstoreResult::StopObservingRegistry() {
267 if (extension_registry_)
268 extension_registry_->RemoveObserver(this);
269 extension_registry_ = NULL;
272 void WebstoreResult::OnDownloadProgress(const std::string& extension_id,
273 int percent_downloaded) {
274 if (extension_id != app_id_ || percent_downloaded < 0)
277 SetPercentDownloaded(percent_downloaded);
280 void WebstoreResult::OnExtensionInstalled(
281 content::BrowserContext* browser_context,
282 const extensions::Extension* extension,
284 if (extension->id() != app_id_)
287 SetIsInstalling(false);
290 if (extensions::util::IsExtensionInstalledPermanently(extension->id(),
292 NotifyItemInstalled();
296 void WebstoreResult::OnShutdown() {
297 StopObservingInstall();
300 void WebstoreResult::OnShutdown(extensions::ExtensionRegistry* registry) {
301 StopObservingRegistry();
304 } // namespace app_list