Home | History | Annotate | Download | only in webstore
      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(&params);
    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