Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 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/extensions/extension_icon_image.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/bind.h"
     10 #include "chrome/browser/chrome_notification_types.h"
     11 #include "chrome/browser/extensions/image_loader.h"
     12 #include "content/public/browser/notification_service.h"
     13 #include "extensions/common/extension.h"
     14 #include "ui/gfx/canvas.h"
     15 #include "ui/gfx/image/canvas_image_source.h"
     16 #include "ui/gfx/image/image.h"
     17 #include "ui/gfx/image/image_skia_operations.h"
     18 #include "ui/gfx/image/image_skia_source.h"
     19 #include "ui/gfx/size.h"
     20 #include "ui/gfx/size_conversions.h"
     21 
     22 // The ImageSkia provided by extensions::IconImage contains ImageSkiaReps that
     23 // are computed and updated using the following algorithm (if no default icon
     24 // was supplied, transparent icon is considered the default):
     25 // - |LoadImageForScaleFactors()| searches the extension for an icon of an
     26 //   appropriate size. If the extension doesn't have a icon resource needed for
     27 //   the image representation, the default icon's representation for the
     28 //   requested scale factor is returned by ImageSkiaSource.
     29 // - If the extension has the resource, IconImage tries to load it using
     30 //   ImageLoader.
     31 // - |ImageLoader| is asynchronous.
     32 //  - ImageSkiaSource will initially return transparent image resource of the
     33 //    desired size.
     34 //  - The image will be updated with an appropriate image representation when
     35 //    the |ImageLoader| finishes. The image representation is chosen the same
     36 //    way as in the synchronous case. The observer is notified of the image
     37 //    change, unless the added image representation is transparent (in which
     38 //    case the image had already contained the appropriate image
     39 //    representation).
     40 
     41 namespace {
     42 
     43 const int kMatchBiggerTreshold = 32;
     44 
     45 extensions::ExtensionResource GetExtensionIconResource(
     46     const extensions::Extension* extension,
     47     const ExtensionIconSet& icons,
     48     int size,
     49     ExtensionIconSet::MatchType match_type) {
     50   std::string path = icons.Get(size, match_type);
     51   if (path.empty())
     52     return extensions::ExtensionResource();
     53 
     54   return extension->GetResource(path);
     55 }
     56 
     57 class BlankImageSource : public gfx::CanvasImageSource {
     58  public:
     59   explicit BlankImageSource(const gfx::Size& size_in_dip)
     60       : CanvasImageSource(size_in_dip, /*is_opaque =*/ false) {
     61   }
     62   virtual ~BlankImageSource() {}
     63 
     64  private:
     65   // gfx::CanvasImageSource overrides:
     66   virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
     67     canvas->DrawColor(SkColorSetARGB(0, 0, 0, 0));
     68   }
     69 
     70   DISALLOW_COPY_AND_ASSIGN(BlankImageSource);
     71 };
     72 
     73 }  // namespace
     74 
     75 namespace extensions {
     76 
     77 ////////////////////////////////////////////////////////////////////////////////
     78 // IconImage::Source
     79 
     80 class IconImage::Source : public gfx::ImageSkiaSource {
     81  public:
     82   Source(IconImage* host, const gfx::Size& size_in_dip);
     83   virtual ~Source();
     84 
     85   void ResetHost();
     86 
     87  private:
     88   // gfx::ImageSkiaSource overrides:
     89   virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE;
     90 
     91   // Used to load images, possibly asynchronously. NULLed out when the IconImage
     92   // is destroyed.
     93   IconImage* host_;
     94 
     95   // Image whose representations will be used until |host_| loads the real
     96   // representations for the image.
     97   gfx::ImageSkia blank_image_;
     98 
     99   DISALLOW_COPY_AND_ASSIGN(Source);
    100 };
    101 
    102 IconImage::Source::Source(IconImage* host, const gfx::Size& size_in_dip)
    103     : host_(host),
    104       blank_image_(new BlankImageSource(size_in_dip), size_in_dip) {
    105 }
    106 
    107 IconImage::Source::~Source() {
    108 }
    109 
    110 void IconImage::Source::ResetHost() {
    111   host_ = NULL;
    112 }
    113 
    114 gfx::ImageSkiaRep IconImage::Source::GetImageForScale(float scale) {
    115   gfx::ImageSkiaRep representation;
    116   if (host_) {
    117     representation =
    118         host_->LoadImageForScaleFactor(ui::GetSupportedScaleFactor(scale));
    119   }
    120 
    121   if (!representation.is_null())
    122     return representation;
    123 
    124   return blank_image_.GetRepresentation(scale);
    125 }
    126 
    127 ////////////////////////////////////////////////////////////////////////////////
    128 // IconImage
    129 
    130 IconImage::IconImage(
    131     content::BrowserContext* context,
    132     const Extension* extension,
    133     const ExtensionIconSet& icon_set,
    134     int resource_size_in_dip,
    135     const gfx::ImageSkia& default_icon,
    136     Observer* observer)
    137     : browser_context_(context),
    138       extension_(extension),
    139       icon_set_(icon_set),
    140       resource_size_in_dip_(resource_size_in_dip),
    141       observer_(observer),
    142       source_(NULL),
    143       default_icon_(gfx::ImageSkiaOperations::CreateResizedImage(
    144           default_icon,
    145           skia::ImageOperations::RESIZE_BEST,
    146           gfx::Size(resource_size_in_dip, resource_size_in_dip))),
    147       weak_ptr_factory_(this) {
    148   gfx::Size resource_size(resource_size_in_dip, resource_size_in_dip);
    149   source_ = new Source(this, resource_size);
    150   image_skia_ = gfx::ImageSkia(source_, resource_size);
    151 
    152   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
    153                  content::NotificationService::AllSources());
    154 }
    155 
    156 IconImage::~IconImage() {
    157   source_->ResetHost();
    158 }
    159 
    160 gfx::ImageSkiaRep IconImage::LoadImageForScaleFactor(
    161     ui::ScaleFactor scale_factor) {
    162   // Do nothing if extension is unloaded.
    163   if (!extension_)
    164     return gfx::ImageSkiaRep();
    165 
    166   const float scale = ui::GetImageScale(scale_factor);
    167   const int resource_size_in_pixel =
    168       static_cast<int>(resource_size_in_dip_ * scale);
    169 
    170   extensions::ExtensionResource resource;
    171 
    172   // Find extension resource for non bundled component extensions.
    173   // We try loading bigger image only if resource size is >= 32.
    174   if (resource_size_in_pixel >= kMatchBiggerTreshold) {
    175     resource = GetExtensionIconResource(extension_, icon_set_,
    176         resource_size_in_pixel, ExtensionIconSet::MATCH_BIGGER);
    177   }
    178 
    179   // If resource is not found by now, try matching smaller one.
    180   if (resource.empty()) {
    181     resource = GetExtensionIconResource(extension_, icon_set_,
    182         resource_size_in_pixel, ExtensionIconSet::MATCH_SMALLER);
    183   }
    184 
    185   // If there is no resource found, return default icon.
    186   if (resource.empty())
    187     return default_icon_.GetRepresentation(scale);
    188 
    189   std::vector<ImageLoader::ImageRepresentation> info_list;
    190   info_list.push_back(ImageLoader::ImageRepresentation(
    191       resource,
    192       ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
    193       gfx::ToFlooredSize(gfx::ScaleSize(
    194           gfx::Size(resource_size_in_dip_, resource_size_in_dip_), scale)),
    195       scale_factor));
    196 
    197   extensions::ImageLoader* loader =
    198       extensions::ImageLoader::Get(browser_context_);
    199   if (extension_->location() == Manifest::COMPONENT) {
    200     // bshe's log for http://crbug.com/314872
    201     LOG(ERROR) << "Start loading extension icon for " << extension_->name()
    202                << ". scale = " << scale;
    203   }
    204 
    205   loader->LoadImagesAsync(extension_, info_list,
    206                           base::Bind(&IconImage::OnImageLoaded,
    207                                      weak_ptr_factory_.GetWeakPtr(),
    208                                      scale));
    209 
    210   return gfx::ImageSkiaRep();
    211 }
    212 
    213 void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) {
    214   const gfx::ImageSkia* image =
    215       image_in.IsEmpty() ? &default_icon_ : image_in.ToImageSkia();
    216 
    217   if (extension_->location() == Manifest::COMPONENT) {
    218     // bshe's log for http://crbug.com/314872
    219     LOG(ERROR) << "Component extension icon for " << extension_->name()
    220                << " is loaded. scale = " << scale;
    221   }
    222   // Maybe default icon was not set.
    223   if (image->isNull())
    224     return;
    225 
    226   gfx::ImageSkiaRep rep = image->GetRepresentation(scale);
    227   DCHECK(!rep.is_null());
    228   DCHECK_EQ(scale, rep.scale());
    229 
    230   // Remove old representation if there is one.
    231   image_skia_.RemoveRepresentation(scale);
    232   image_skia_.AddRepresentation(rep);
    233 
    234   if (observer_)
    235     observer_->OnExtensionIconImageChanged(this);
    236 }
    237 
    238 void IconImage::Observe(int type,
    239                         const content::NotificationSource& source,
    240                         const content::NotificationDetails& details) {
    241   DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_UNLOADED);
    242 
    243   const Extension* extension =
    244       content::Details<extensions::UnloadedExtensionInfo>(details)->extension;
    245 
    246   if (extension_ == extension)
    247     extension_ = NULL;
    248 }
    249 
    250 }  // namespace extensions
    251