• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "extensions/browser/extension_icon_image.h"
6 
7 #include <vector>
8 
9 #include "base/bind.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "content/public/browser/notification_service.h"
12 #include "extensions/browser/image_loader.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 
GetExtensionIconResource(const extensions::Extension * extension,const ExtensionIconSet & icons,int size,ExtensionIconSet::MatchType match_type)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:
BlankImageSource(const gfx::Size & size_in_dip)59   explicit BlankImageSource(const gfx::Size& size_in_dip)
60       : CanvasImageSource(size_in_dip, /*is_opaque =*/ false) {
61   }
~BlankImageSource()62   virtual ~BlankImageSource() {}
63 
64  private:
65   // gfx::CanvasImageSource overrides:
Draw(gfx::Canvas * canvas)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 
Source(IconImage * host,const gfx::Size & size_in_dip)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 
~Source()107 IconImage::Source::~Source() {
108 }
109 
ResetHost()110 void IconImage::Source::ResetHost() {
111   host_ = NULL;
112 }
113 
GetImageForScale(float scale)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 
IconImage(content::BrowserContext * context,const Extension * extension,const ExtensionIconSet & icon_set,int resource_size_in_dip,const gfx::ImageSkia & default_icon,Observer * observer)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,
153                  chrome::NOTIFICATION_EXTENSION_REMOVED,
154                  content::NotificationService::AllSources());
155 }
156 
~IconImage()157 IconImage::~IconImage() {
158   source_->ResetHost();
159 }
160 
LoadImageForScaleFactor(ui::ScaleFactor scale_factor)161 gfx::ImageSkiaRep IconImage::LoadImageForScaleFactor(
162     ui::ScaleFactor scale_factor) {
163   // Do nothing if extension is unloaded.
164   if (!extension_)
165     return gfx::ImageSkiaRep();
166 
167   const float scale = ui::GetScaleForScaleFactor(scale_factor);
168   const int resource_size_in_pixel =
169       static_cast<int>(resource_size_in_dip_ * scale);
170 
171   extensions::ExtensionResource resource;
172 
173   // Find extension resource for non bundled component extensions.
174   // We try loading bigger image only if resource size is >= 32.
175   if (resource_size_in_pixel >= kMatchBiggerTreshold) {
176     resource = GetExtensionIconResource(extension_, icon_set_,
177         resource_size_in_pixel, ExtensionIconSet::MATCH_BIGGER);
178   }
179 
180   // If resource is not found by now, try matching smaller one.
181   if (resource.empty()) {
182     resource = GetExtensionIconResource(extension_, icon_set_,
183         resource_size_in_pixel, ExtensionIconSet::MATCH_SMALLER);
184   }
185 
186   // If there is no resource found, return default icon.
187   if (resource.empty())
188     return default_icon_.GetRepresentation(scale);
189 
190   std::vector<ImageLoader::ImageRepresentation> info_list;
191   info_list.push_back(ImageLoader::ImageRepresentation(
192       resource,
193       ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
194       gfx::ToFlooredSize(gfx::ScaleSize(
195           gfx::Size(resource_size_in_dip_, resource_size_in_dip_), scale)),
196       scale_factor));
197 
198   extensions::ImageLoader* loader =
199       extensions::ImageLoader::Get(browser_context_);
200   loader->LoadImagesAsync(extension_, info_list,
201                           base::Bind(&IconImage::OnImageLoaded,
202                                      weak_ptr_factory_.GetWeakPtr(),
203                                      scale));
204 
205   return gfx::ImageSkiaRep();
206 }
207 
OnImageLoaded(float scale,const gfx::Image & image_in)208 void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) {
209   const gfx::ImageSkia* image =
210       image_in.IsEmpty() ? &default_icon_ : image_in.ToImageSkia();
211 
212   // Maybe default icon was not set.
213   if (image->isNull())
214     return;
215 
216   gfx::ImageSkiaRep rep = image->GetRepresentation(scale);
217   DCHECK(!rep.is_null());
218   DCHECK_EQ(scale, rep.scale());
219 
220   // Remove old representation if there is one.
221   image_skia_.RemoveRepresentation(scale);
222   image_skia_.AddRepresentation(rep);
223 
224   if (observer_)
225     observer_->OnExtensionIconImageChanged(this);
226 }
227 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)228 void IconImage::Observe(int type,
229                         const content::NotificationSource& source,
230                         const content::NotificationDetails& details) {
231   DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_REMOVED);
232 
233   const Extension* extension = content::Details<const Extension>(details).ptr();
234 
235   if (extension_ == extension)
236     extension_ = NULL;
237 }
238 
239 }  // namespace extensions
240