Home | History | Annotate | Download | only in drive
      1 // Copyright 2014 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/apps/drive/drive_app_converter.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 
     10 #include "base/logging.h"
     11 #include "base/memory/ref_counted.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chrome/browser/extensions/crx_installer.h"
     14 #include "chrome/browser/extensions/install_tracker.h"
     15 #include "chrome/browser/image_decoder.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "content/public/browser/browser_thread.h"
     18 #include "extensions/browser/extension_system.h"
     19 #include "extensions/common/constants.h"
     20 #include "net/base/load_flags.h"
     21 #include "net/http/http_status_code.h"
     22 #include "net/url_request/url_fetcher.h"
     23 #include "net/url_request/url_fetcher_delegate.h"
     24 #include "net/url_request/url_request_status.h"
     25 #include "third_party/skia/include/core/SkBitmap.h"
     26 
     27 using content::BrowserThread;
     28 
     29 // IconFetcher downloads |icon_url| using |converter|'s profile. The icon
     30 // url is passed from a DriveAppInfo and should follow icon url definition
     31 // in Drive API:
     32 //   https://developers.google.com/drive/v2/reference/apps#resource
     33 // Each icon url represents a single image associated with a certain size.
     34 class DriveAppConverter::IconFetcher : public net::URLFetcherDelegate,
     35                                        public ImageDecoder::Delegate {
     36  public:
     37   IconFetcher(DriveAppConverter* converter,
     38               const GURL& icon_url,
     39               int expected_size)
     40       : converter_(converter),
     41         icon_url_(icon_url),
     42         expected_size_(expected_size) {}
     43   virtual ~IconFetcher() {
     44     if (image_decoder_.get())
     45       image_decoder_->set_delegate(NULL);
     46   }
     47 
     48   void Start() {
     49     fetcher_.reset(
     50         net::URLFetcher::Create(icon_url_, net::URLFetcher::GET, this));
     51     fetcher_->SetRequestContext(converter_->profile_->GetRequestContext());
     52     fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
     53     fetcher_->Start();
     54   }
     55 
     56   const GURL& icon_url() const { return icon_url_; }
     57   const SkBitmap& icon() const { return icon_; }
     58 
     59  private:
     60   // net::URLFetcherDelegate overrides:
     61   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
     62     CHECK_EQ(fetcher_.get(), source);
     63     scoped_ptr<net::URLFetcher> fetcher(fetcher_.Pass());
     64 
     65     if (!fetcher->GetStatus().is_success() ||
     66         fetcher->GetResponseCode() != net::HTTP_OK) {
     67       converter_->OnIconFetchComplete(this);
     68       return;
     69     }
     70 
     71     std::string unsafe_icon_data;
     72     fetcher->GetResponseAsString(&unsafe_icon_data);
     73 
     74     image_decoder_ =
     75         new ImageDecoder(this, unsafe_icon_data, ImageDecoder::DEFAULT_CODEC);
     76     image_decoder_->Start(
     77         BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI));
     78   }
     79 
     80   // ImageDecoder::Delegate overrides:
     81   virtual void OnImageDecoded(const ImageDecoder* decoder,
     82                               const SkBitmap& decoded_image) OVERRIDE {
     83     if (decoded_image.width() == expected_size_)
     84       icon_ = decoded_image;
     85     converter_->OnIconFetchComplete(this);
     86   }
     87 
     88   virtual void OnDecodeImageFailed(const ImageDecoder* decoder) OVERRIDE {
     89     converter_->OnIconFetchComplete(this);
     90   }
     91 
     92   DriveAppConverter* converter_;
     93   const GURL icon_url_;
     94   const int expected_size_;
     95 
     96   scoped_ptr<net::URLFetcher> fetcher_;
     97   scoped_refptr<ImageDecoder> image_decoder_;
     98   SkBitmap icon_;
     99 
    100   DISALLOW_COPY_AND_ASSIGN(IconFetcher);
    101 };
    102 
    103 DriveAppConverter::DriveAppConverter(Profile* profile,
    104                                      const drive::DriveAppInfo& drive_app_info,
    105                                      const FinishedCallback& finished_callback)
    106     : profile_(profile),
    107       drive_app_info_(drive_app_info),
    108       extension_(NULL),
    109       is_new_install_(false),
    110       finished_callback_(finished_callback) {
    111   DCHECK(profile_);
    112 }
    113 
    114 DriveAppConverter::~DriveAppConverter() {
    115   PostInstallCleanUp();
    116 }
    117 
    118 void DriveAppConverter::Start() {
    119   DCHECK(!IsStarted());
    120 
    121   if (drive_app_info_.app_name.empty() ||
    122       !drive_app_info_.create_url.is_valid()) {
    123     finished_callback_.Run(this, false);
    124     return;
    125   }
    126 
    127   web_app_.title = base::UTF8ToUTF16(drive_app_info_.app_name);
    128   web_app_.app_url = drive_app_info_.create_url;
    129 
    130   const std::set<int> allowed_sizes(extension_misc::kExtensionIconSizes,
    131                                     extension_misc::kExtensionIconSizes +
    132                                         extension_misc::kNumExtensionIconSizes);
    133   std::set<int> pending_sizes;
    134   for (size_t i = 0; i < drive_app_info_.app_icons.size(); ++i) {
    135     const int icon_size = drive_app_info_.app_icons[i].first;
    136     if (allowed_sizes.find(icon_size) == allowed_sizes.end() ||
    137         pending_sizes.find(icon_size) != pending_sizes.end()) {
    138       continue;
    139     }
    140 
    141     pending_sizes.insert(icon_size);
    142     const GURL& icon_url = drive_app_info_.app_icons[i].second;
    143     IconFetcher* fetcher = new IconFetcher(this, icon_url, icon_size);
    144     fetchers_.push_back(fetcher);  // Pass ownership to |fetchers|.
    145     fetcher->Start();
    146   }
    147 
    148   if (fetchers_.empty())
    149     StartInstall();
    150 }
    151 
    152 bool DriveAppConverter::IsStarted() const {
    153   return !fetchers_.empty() || crx_installer_.get();
    154 }
    155 
    156 bool DriveAppConverter::IsInstalling(const std::string& app_id) const {
    157   return crx_installer_.get() && crx_installer_->extension() &&
    158          crx_installer_->extension()->id() == app_id;
    159 }
    160 
    161 void DriveAppConverter::OnIconFetchComplete(const IconFetcher* fetcher) {
    162   const SkBitmap& icon = fetcher->icon();
    163   if (!icon.empty() && icon.width() != 0) {
    164     WebApplicationInfo::IconInfo icon_info;
    165     icon_info.url = fetcher->icon_url();
    166     icon_info.data = icon;
    167     icon_info.width = icon.width();
    168     icon_info.height = icon.height();
    169     web_app_.icons.push_back(icon_info);
    170   }
    171 
    172   fetchers_.erase(std::find(fetchers_.begin(), fetchers_.end(), fetcher));
    173 
    174   if (fetchers_.empty())
    175     StartInstall();
    176 }
    177 
    178 void DriveAppConverter::StartInstall() {
    179   DCHECK(!crx_installer_.get());
    180   crx_installer_ = extensions::CrxInstaller::CreateSilent(
    181       extensions::ExtensionSystem::Get(profile_)->extension_service());
    182   // The converted url app should not be syncable. Drive apps go with the user's
    183   // account and url apps will be created when needed. Syncing those apps could
    184   // hit an edge case where the synced url apps become orphans when the user has
    185   // corresponding chrome apps.
    186   crx_installer_->set_do_not_sync(true);
    187 
    188   extensions::InstallTracker::Get(profile_)->AddObserver(this);
    189   crx_installer_->InstallWebApp(web_app_);
    190 }
    191 
    192 void DriveAppConverter::PostInstallCleanUp() {
    193   if (!crx_installer_.get())
    194     return;
    195 
    196   extensions::InstallTracker::Get(profile_)->RemoveObserver(this);
    197   crx_installer_ = NULL;
    198 }
    199 
    200 void DriveAppConverter::OnFinishCrxInstall(const std::string& extension_id,
    201                                            bool success) {
    202   if (!crx_installer_->extension() ||
    203       crx_installer_->extension()->id() != extension_id) {
    204     return;
    205   }
    206 
    207   extension_ = crx_installer_->extension();
    208   is_new_install_ = success && crx_installer_->current_version().empty();
    209   PostInstallCleanUp();
    210 
    211   finished_callback_.Run(this, success);
    212   // |finished_callback_| could delete this.
    213 }
    214