Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / extension_icon_image.cc
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 "content/public/browser/notification_service.h"
11 #include "extensions/browser/image_loader.h"
12 #include "extensions/browser/notification_types.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   const std::string& path = icons.Get(size, match_type);
51   return path.empty() ? extensions::ExtensionResource()
52                       : extension->GetResource(path);
53 }
54
55 class BlankImageSource : public gfx::CanvasImageSource {
56  public:
57   explicit BlankImageSource(const gfx::Size& size_in_dip)
58       : CanvasImageSource(size_in_dip, /*is_opaque =*/ false) {
59   }
60   virtual ~BlankImageSource() {}
61
62  private:
63   // gfx::CanvasImageSource overrides:
64   virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
65     canvas->DrawColor(SkColorSetARGB(0, 0, 0, 0));
66   }
67
68   DISALLOW_COPY_AND_ASSIGN(BlankImageSource);
69 };
70
71 }  // namespace
72
73 namespace extensions {
74
75 ////////////////////////////////////////////////////////////////////////////////
76 // IconImage::Source
77
78 class IconImage::Source : public gfx::ImageSkiaSource {
79  public:
80   Source(IconImage* host, const gfx::Size& size_in_dip);
81   virtual ~Source();
82
83   void ResetHost();
84
85  private:
86   // gfx::ImageSkiaSource overrides:
87   virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE;
88
89   // Used to load images, possibly asynchronously. NULLed out when the IconImage
90   // is destroyed.
91   IconImage* host_;
92
93   // Image whose representations will be used until |host_| loads the real
94   // representations for the image.
95   gfx::ImageSkia blank_image_;
96
97   DISALLOW_COPY_AND_ASSIGN(Source);
98 };
99
100 IconImage::Source::Source(IconImage* host, const gfx::Size& size_in_dip)
101     : host_(host),
102       blank_image_(new BlankImageSource(size_in_dip), size_in_dip) {
103 }
104
105 IconImage::Source::~Source() {
106 }
107
108 void IconImage::Source::ResetHost() {
109   host_ = NULL;
110 }
111
112 gfx::ImageSkiaRep IconImage::Source::GetImageForScale(float scale) {
113   gfx::ImageSkiaRep representation;
114   if (host_) {
115     representation =
116         host_->LoadImageForScaleFactor(ui::GetSupportedScaleFactor(scale));
117   }
118
119   if (!representation.is_null())
120     return representation;
121
122   return blank_image_.GetRepresentation(scale);
123 }
124
125 ////////////////////////////////////////////////////////////////////////////////
126 // IconImage
127
128 IconImage::IconImage(
129     content::BrowserContext* context,
130     const Extension* extension,
131     const ExtensionIconSet& icon_set,
132     int resource_size_in_dip,
133     const gfx::ImageSkia& default_icon,
134     Observer* observer)
135     : browser_context_(context),
136       extension_(extension),
137       icon_set_(icon_set),
138       resource_size_in_dip_(resource_size_in_dip),
139       observer_(observer),
140       source_(NULL),
141       default_icon_(gfx::ImageSkiaOperations::CreateResizedImage(
142           default_icon,
143           skia::ImageOperations::RESIZE_BEST,
144           gfx::Size(resource_size_in_dip, resource_size_in_dip))),
145       weak_ptr_factory_(this) {
146   gfx::Size resource_size(resource_size_in_dip, resource_size_in_dip);
147   source_ = new Source(this, resource_size);
148   image_skia_ = gfx::ImageSkia(source_, resource_size);
149
150   registrar_.Add(this,
151                  extensions::NOTIFICATION_EXTENSION_REMOVED,
152                  content::NotificationService::AllSources());
153 }
154
155 IconImage::~IconImage() {
156   source_->ResetHost();
157 }
158
159 gfx::ImageSkiaRep IconImage::LoadImageForScaleFactor(
160     ui::ScaleFactor scale_factor) {
161   // Do nothing if extension is unloaded.
162   if (!extension_)
163     return gfx::ImageSkiaRep();
164
165   const float scale = ui::GetScaleForScaleFactor(scale_factor);
166   const int resource_size_in_pixel =
167       static_cast<int>(resource_size_in_dip_ * scale);
168
169   extensions::ExtensionResource resource;
170
171   // Find extension resource for non bundled component extensions.
172   // We try loading bigger image only if resource size is >= 32.
173   if (resource_size_in_pixel >= kMatchBiggerTreshold) {
174     resource = GetExtensionIconResource(extension_, icon_set_,
175         resource_size_in_pixel, ExtensionIconSet::MATCH_BIGGER);
176   }
177
178   // If resource is not found by now, try matching smaller one.
179   if (resource.empty()) {
180     resource = GetExtensionIconResource(extension_, icon_set_,
181         resource_size_in_pixel, ExtensionIconSet::MATCH_SMALLER);
182   }
183
184   // If there is no resource found, return default icon.
185   if (resource.empty())
186     return default_icon_.GetRepresentation(scale);
187
188   std::vector<ImageLoader::ImageRepresentation> info_list;
189   info_list.push_back(ImageLoader::ImageRepresentation(
190       resource,
191       ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
192       gfx::ToFlooredSize(gfx::ScaleSize(
193           gfx::Size(resource_size_in_dip_, resource_size_in_dip_), scale)),
194       scale_factor));
195
196   extensions::ImageLoader* loader =
197       extensions::ImageLoader::Get(browser_context_);
198   loader->LoadImagesAsync(extension_, info_list,
199                           base::Bind(&IconImage::OnImageLoaded,
200                                      weak_ptr_factory_.GetWeakPtr(),
201                                      scale));
202
203   return gfx::ImageSkiaRep();
204 }
205
206 void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) {
207   const gfx::ImageSkia* image =
208       image_in.IsEmpty() ? &default_icon_ : image_in.ToImageSkia();
209
210   // Maybe default icon was not set.
211   if (image->isNull())
212     return;
213
214   gfx::ImageSkiaRep rep = image->GetRepresentation(scale);
215   DCHECK(!rep.is_null());
216   DCHECK_EQ(scale, rep.scale());
217
218   // Remove old representation if there is one.
219   image_skia_.RemoveRepresentation(scale);
220   image_skia_.AddRepresentation(rep);
221
222   if (observer_)
223     observer_->OnExtensionIconImageChanged(this);
224 }
225
226 void IconImage::Observe(int type,
227                         const content::NotificationSource& source,
228                         const content::NotificationDetails& details) {
229   DCHECK_EQ(type, extensions::NOTIFICATION_EXTENSION_REMOVED);
230
231   const Extension* extension = content::Details<const Extension>(details).ptr();
232
233   if (extension_ == extension)
234     extension_ = NULL;
235 }
236
237 }  // namespace extensions