blob: c0ab8d5d6cd7ff38cb3d8690e0a37322ffa62c1f [file] [log] [blame]
[email protected]326e6f02014-06-20 04:53:371// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]74c938f2012-08-20 22:22:422// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]326e6f02014-06-20 04:53:375#include "extensions/browser/extension_icon_image.h"
[email protected]74c938f2012-08-20 22:22:426
7#include <vector>
8
[email protected]69805472013-02-08 15:41:209#include "base/bind.h"
[email protected]74c938f2012-08-20 22:22:4210#include "content/public/browser/notification_service.h"
[email protected]326e6f02014-06-20 04:53:3711#include "extensions/browser/image_loader.h"
[email protected]adf5a102014-07-31 12:44:0612#include "extensions/browser/notification_types.h"
[email protected]e4452d32013-11-15 23:07:4113#include "extensions/common/extension.h"
ananta764a6e3d2015-03-25 20:04:3214#include "ui/base/layout.h"
[email protected]99d30052012-09-07 05:42:2115#include "ui/gfx/canvas.h"
tfarinaebe974f02015-01-03 04:25:3216#include "ui/gfx/geometry/size.h"
17#include "ui/gfx/geometry/size_conversions.h"
[email protected]99d30052012-09-07 05:42:2118#include "ui/gfx/image/canvas_image_source.h"
[email protected]74c938f2012-08-20 22:22:4219#include "ui/gfx/image/image.h"
[email protected]7240505e2012-11-28 04:42:2920#include "ui/gfx/image/image_skia_operations.h"
[email protected]74c938f2012-08-20 22:22:4221#include "ui/gfx/image/image_skia_source.h"
[email protected]74c938f2012-08-20 22:22:4222
[email protected]99d30052012-09-07 05:42:2123// The ImageSkia provided by extensions::IconImage contains ImageSkiaReps that
24// are computed and updated using the following algorithm (if no default icon
25// was supplied, transparent icon is considered the default):
26// - |LoadImageForScaleFactors()| searches the extension for an icon of an
27// appropriate size. If the extension doesn't have a icon resource needed for
28// the image representation, the default icon's representation for the
29// requested scale factor is returned by ImageSkiaSource.
30// - If the extension has the resource, IconImage tries to load it using
[email protected]75b91fd2013-02-12 22:14:2531// ImageLoader.
32// - |ImageLoader| is asynchronous.
[email protected]99d30052012-09-07 05:42:2133// - ImageSkiaSource will initially return transparent image resource of the
34// desired size.
35// - The image will be updated with an appropriate image representation when
[email protected]75b91fd2013-02-12 22:14:2536// the |ImageLoader| finishes. The image representation is chosen the same
37// way as in the synchronous case. The observer is notified of the image
38// change, unless the added image representation is transparent (in which
39// case the image had already contained the appropriate image
[email protected]99d30052012-09-07 05:42:2140// representation).
41
[email protected]74c938f2012-08-20 22:22:4242namespace {
43
[email protected]993da5e2013-03-23 21:25:1644extensions::ExtensionResource GetExtensionIconResource(
[email protected]74c938f2012-08-20 22:22:4245 const extensions::Extension* extension,
46 const ExtensionIconSet& icons,
47 int size,
48 ExtensionIconSet::MatchType match_type) {
[email protected]1771f162014-08-07 22:52:1649 const std::string& path = icons.Get(size, match_type);
50 return path.empty() ? extensions::ExtensionResource()
51 : extension->GetResource(path);
[email protected]74c938f2012-08-20 22:22:4252}
53
[email protected]99d30052012-09-07 05:42:2154class BlankImageSource : public gfx::CanvasImageSource {
55 public:
56 explicit BlankImageSource(const gfx::Size& size_in_dip)
57 : CanvasImageSource(size_in_dip, /*is_opaque =*/ false) {
58 }
dcheng9168b2f2014-10-21 12:38:2459 ~BlankImageSource() override {}
[email protected]99d30052012-09-07 05:42:2160
61 private:
62 // gfx::CanvasImageSource overrides:
dcheng9168b2f2014-10-21 12:38:2463 void Draw(gfx::Canvas* canvas) override {
[email protected]99d30052012-09-07 05:42:2164 canvas->DrawColor(SkColorSetARGB(0, 0, 0, 0));
65 }
66
67 DISALLOW_COPY_AND_ASSIGN(BlankImageSource);
68};
69
[email protected]74c938f2012-08-20 22:22:4270} // namespace
71
72namespace extensions {
73
74////////////////////////////////////////////////////////////////////////////////
[email protected]6f6ccb02012-09-11 03:38:1075// IconImage::Source
[email protected]74c938f2012-08-20 22:22:4276
77class IconImage::Source : public gfx::ImageSkiaSource {
78 public:
[email protected]99d30052012-09-07 05:42:2179 Source(IconImage* host, const gfx::Size& size_in_dip);
dcheng9168b2f2014-10-21 12:38:2480 ~Source() override;
[email protected]74c938f2012-08-20 22:22:4281
82 void ResetHost();
83
84 private:
85 // gfx::ImageSkiaSource overrides:
dcheng9168b2f2014-10-21 12:38:2486 gfx::ImageSkiaRep GetImageForScale(float scale) override;
[email protected]74c938f2012-08-20 22:22:4287
[email protected]99d30052012-09-07 05:42:2188 // Used to load images, possibly asynchronously. NULLed out when the IconImage
89 // is destroyed.
[email protected]74c938f2012-08-20 22:22:4290 IconImage* host_;
91
[email protected]99d30052012-09-07 05:42:2192 // Image whose representations will be used until |host_| loads the real
93 // representations for the image.
94 gfx::ImageSkia blank_image_;
95
[email protected]74c938f2012-08-20 22:22:4296 DISALLOW_COPY_AND_ASSIGN(Source);
97};
98
[email protected]99d30052012-09-07 05:42:2199IconImage::Source::Source(IconImage* host, const gfx::Size& size_in_dip)
100 : host_(host),
101 blank_image_(new BlankImageSource(size_in_dip), size_in_dip) {
[email protected]74c938f2012-08-20 22:22:42102}
103
104IconImage::Source::~Source() {
105}
106
107void IconImage::Source::ResetHost() {
108 host_ = NULL;
109}
110
[email protected]50b66262013-09-24 03:25:48111gfx::ImageSkiaRep IconImage::Source::GetImageForScale(float scale) {
[email protected]99d30052012-09-07 05:42:21112 gfx::ImageSkiaRep representation;
[email protected]50b66262013-09-24 03:25:48113 if (host_) {
114 representation =
115 host_->LoadImageForScaleFactor(ui::GetSupportedScaleFactor(scale));
116 }
[email protected]99d30052012-09-07 05:42:21117
118 if (!representation.is_null())
119 return representation;
120
[email protected]50b66262013-09-24 03:25:48121 return blank_image_.GetRepresentation(scale);
[email protected]74c938f2012-08-20 22:22:42122}
123
124////////////////////////////////////////////////////////////////////////////////
[email protected]6f6ccb02012-09-11 03:38:10125// IconImage
[email protected]74c938f2012-08-20 22:22:42126
127IconImage::IconImage(
[email protected]472522b2013-10-25 00:41:28128 content::BrowserContext* context,
[email protected]74c938f2012-08-20 22:22:42129 const Extension* extension,
130 const ExtensionIconSet& icon_set,
131 int resource_size_in_dip,
[email protected]99d30052012-09-07 05:42:21132 const gfx::ImageSkia& default_icon,
[email protected]74c938f2012-08-20 22:22:42133 Observer* observer)
[email protected]472522b2013-10-25 00:41:28134 : browser_context_(context),
[email protected]69805472013-02-08 15:41:20135 extension_(extension),
[email protected]74c938f2012-08-20 22:22:42136 icon_set_(icon_set),
137 resource_size_in_dip_(resource_size_in_dip),
[email protected]74c938f2012-08-20 22:22:42138 source_(NULL),
[email protected]7240505e2012-11-28 04:42:29139 default_icon_(gfx::ImageSkiaOperations::CreateResizedImage(
140 default_icon,
141 skia::ImageOperations::RESIZE_BEST,
142 gfx::Size(resource_size_in_dip, resource_size_in_dip))),
[email protected]9c009092013-05-01 03:14:09143 weak_ptr_factory_(this) {
rdevlin.croninf19c76632015-01-29 16:57:35144 if (observer)
145 AddObserver(observer);
[email protected]99d30052012-09-07 05:42:21146 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);
rdevlin.croninbf734cd2015-01-29 20:03:53149 image_ = gfx::Image(image_skia_);
[email protected]74c938f2012-08-20 22:22:42150
[email protected]8c179032014-03-20 22:49:10151 registrar_.Add(this,
[email protected]adf5a102014-07-31 12:44:06152 extensions::NOTIFICATION_EXTENSION_REMOVED,
[email protected]74c938f2012-08-20 22:22:42153 content::NotificationService::AllSources());
154}
155
rdevlin.croninf19c76632015-01-29 16:57:35156void IconImage::AddObserver(Observer* observer) {
157 observers_.AddObserver(observer);
158}
159
160void IconImage::RemoveObserver(Observer* observer) {
161 observers_.RemoveObserver(observer);
162}
163
[email protected]74c938f2012-08-20 22:22:42164IconImage::~IconImage() {
rdevlin.croninf19c76632015-01-29 16:57:35165 FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageDestroyed(this));
[email protected]99d30052012-09-07 05:42:21166 source_->ResetHost();
[email protected]74c938f2012-08-20 22:22:42167}
168
[email protected]99d30052012-09-07 05:42:21169gfx::ImageSkiaRep IconImage::LoadImageForScaleFactor(
170 ui::ScaleFactor scale_factor) {
[email protected]74c938f2012-08-20 22:22:42171 // Do nothing if extension is unloaded.
172 if (!extension_)
[email protected]99d30052012-09-07 05:42:21173 return gfx::ImageSkiaRep();
[email protected]74c938f2012-08-20 22:22:42174
[email protected]4d617d012014-05-21 03:56:33175 const float scale = ui::GetScaleForScaleFactor(scale_factor);
[email protected]74c938f2012-08-20 22:22:42176 const int resource_size_in_pixel =
177 static_cast<int>(resource_size_in_dip_ * scale);
178
[email protected]993da5e2013-03-23 21:25:16179 extensions::ExtensionResource resource;
[email protected]74c938f2012-08-20 22:22:42180
[email protected]6f6ccb02012-09-11 03:38:10181 // Find extension resource for non bundled component extensions.
[email protected]9cfffad2014-08-19 17:42:31182 resource = GetExtensionIconResource(extension_,
183 icon_set_,
184 resource_size_in_pixel,
185 ExtensionIconSet::MATCH_BIGGER);
[email protected]b9aae8e02012-10-03 19:42:57186
[email protected]d6335752012-10-04 16:19:36187 // If resource is not found by now, try matching smaller one.
188 if (resource.empty()) {
189 resource = GetExtensionIconResource(extension_, icon_set_,
190 resource_size_in_pixel, ExtensionIconSet::MATCH_SMALLER);
191 }
192
193 // If there is no resource found, return default icon.
194 if (resource.empty())
[email protected]50b66262013-09-24 03:25:48195 return default_icon_.GetRepresentation(scale);
[email protected]d6335752012-10-04 16:19:36196
[email protected]69805472013-02-08 15:41:20197 std::vector<ImageLoader::ImageRepresentation> info_list;
198 info_list.push_back(ImageLoader::ImageRepresentation(
danakjddaec912015-09-25 19:38:40199 resource, ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
200 gfx::ScaleToFlooredSize(
201 gfx::Size(resource_size_in_dip_, resource_size_in_dip_), scale),
[email protected]74c938f2012-08-20 22:22:42202 scale_factor));
[email protected]99d30052012-09-07 05:42:21203
[email protected]472522b2013-10-25 00:41:28204 extensions::ImageLoader* loader =
205 extensions::ImageLoader::Get(browser_context_);
[email protected]69805472013-02-08 15:41:20206 loader->LoadImagesAsync(extension_, info_list,
207 base::Bind(&IconImage::OnImageLoaded,
208 weak_ptr_factory_.GetWeakPtr(),
[email protected]50b66262013-09-24 03:25:48209 scale));
[email protected]99d30052012-09-07 05:42:21210
211 return gfx::ImageSkiaRep();
[email protected]74c938f2012-08-20 22:22:42212}
213
[email protected]50b66262013-09-24 03:25:48214void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) {
[email protected]99d30052012-09-07 05:42:21215 const gfx::ImageSkia* image =
216 image_in.IsEmpty() ? &default_icon_ : image_in.ToImageSkia();
[email protected]74c938f2012-08-20 22:22:42217
[email protected]99d30052012-09-07 05:42:21218 // Maybe default icon was not set.
219 if (image->isNull())
220 return;
221
[email protected]50b66262013-09-24 03:25:48222 gfx::ImageSkiaRep rep = image->GetRepresentation(scale);
[email protected]74c938f2012-08-20 22:22:42223 DCHECK(!rep.is_null());
[email protected]50b66262013-09-24 03:25:48224 DCHECK_EQ(scale, rep.scale());
[email protected]99d30052012-09-07 05:42:21225
ananta764a6e3d2015-03-25 20:04:32226 // Remove fractional scale image representations as they may have become
227 // stale here. These images are generated by ImageSkia on request from
228 // supported scales like 1x, 2x, etc.
229 // TODO(oshima)
230 // A better approach might be to set the |image_| member using the ImageSkia
231 // copy from the image passed in and set the |image_skia_| member using
232 // image_.ToImageSkia(). However that does not work correctly as the
233 // ImageSkia from the image does not contain a source which breaks requests
234 // for scaled images.
235 std::vector<gfx::ImageSkiaRep> reps = image_skia_.image_reps();
anantab5ff8d1f2015-03-31 03:01:59236 for (const auto& image_rep : reps) {
237 if (!ui::IsSupportedScale(image_rep.scale()))
238 image_skia_.RemoveRepresentation(image_rep.scale());
ananta764a6e3d2015-03-25 20:04:32239 }
240
[email protected]50b66262013-09-24 03:25:48241 image_skia_.RemoveRepresentation(scale);
[email protected]74c938f2012-08-20 22:22:42242 image_skia_.AddRepresentation(rep);
243
rdevlin.croninbf734cd2015-01-29 20:03:53244 // Update the image to use the updated image skia.
245 // It's a shame we have to do this because it means that all the other
246 // representations stored on |image_| will be deleted, but unfortunately
247 // there's no way to combine the storage of two images.
248 image_ = gfx::Image(image_skia_);
249
rdevlin.croninf19c76632015-01-29 16:57:35250 FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageChanged(this));
[email protected]74c938f2012-08-20 22:22:42251}
252
253void IconImage::Observe(int type,
254 const content::NotificationSource& source,
255 const content::NotificationDetails& details) {
[email protected]adf5a102014-07-31 12:44:06256 DCHECK_EQ(type, extensions::NOTIFICATION_EXTENSION_REMOVED);
[email protected]74c938f2012-08-20 22:22:42257
[email protected]8c179032014-03-20 22:49:10258 const Extension* extension = content::Details<const Extension>(details).ptr();
[email protected]74c938f2012-08-20 22:22:42259
260 if (extension_ == extension)
261 extension_ = NULL;
262}
263
264} // namespace extensions