Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / extensions / browser / extension_icon_image.cc
blob023879f10cb562925b5f1cb5f5186a7bb69fcf55
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.
5 #include "extensions/browser/extension_icon_image.h"
7 #include <vector>
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/base/layout.h"
15 #include "ui/gfx/canvas.h"
16 #include "ui/gfx/geometry/size.h"
17 #include "ui/gfx/geometry/size_conversions.h"
18 #include "ui/gfx/image/canvas_image_source.h"
19 #include "ui/gfx/image/image.h"
20 #include "ui/gfx/image/image_skia_operations.h"
21 #include "ui/gfx/image/image_skia_source.h"
23 // 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
31 // ImageLoader.
32 // - |ImageLoader| is asynchronous.
33 // - ImageSkiaSource will initially return transparent image resource of the
34 // desired size.
35 // - The image will be updated with an appropriate image representation when
36 // 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
40 // representation).
42 namespace {
44 extensions::ExtensionResource GetExtensionIconResource(
45 const extensions::Extension* extension,
46 const ExtensionIconSet& icons,
47 int size,
48 ExtensionIconSet::MatchType match_type) {
49 const std::string& path = icons.Get(size, match_type);
50 return path.empty() ? extensions::ExtensionResource()
51 : extension->GetResource(path);
54 class BlankImageSource : public gfx::CanvasImageSource {
55 public:
56 explicit BlankImageSource(const gfx::Size& size_in_dip)
57 : CanvasImageSource(size_in_dip, /*is_opaque =*/ false) {
59 ~BlankImageSource() override {}
61 private:
62 // gfx::CanvasImageSource overrides:
63 void Draw(gfx::Canvas* canvas) override {
64 canvas->DrawColor(SkColorSetARGB(0, 0, 0, 0));
67 DISALLOW_COPY_AND_ASSIGN(BlankImageSource);
70 } // namespace
72 namespace extensions {
74 ////////////////////////////////////////////////////////////////////////////////
75 // IconImage::Source
77 class IconImage::Source : public gfx::ImageSkiaSource {
78 public:
79 Source(IconImage* host, const gfx::Size& size_in_dip);
80 ~Source() override;
82 void ResetHost();
84 private:
85 // gfx::ImageSkiaSource overrides:
86 gfx::ImageSkiaRep GetImageForScale(float scale) override;
88 // Used to load images, possibly asynchronously. NULLed out when the IconImage
89 // is destroyed.
90 IconImage* host_;
92 // Image whose representations will be used until |host_| loads the real
93 // representations for the image.
94 gfx::ImageSkia blank_image_;
96 DISALLOW_COPY_AND_ASSIGN(Source);
99 IconImage::Source::Source(IconImage* host, const gfx::Size& size_in_dip)
100 : host_(host),
101 blank_image_(new BlankImageSource(size_in_dip), size_in_dip) {
104 IconImage::Source::~Source() {
107 void IconImage::Source::ResetHost() {
108 host_ = NULL;
111 gfx::ImageSkiaRep IconImage::Source::GetImageForScale(float scale) {
112 gfx::ImageSkiaRep representation;
113 if (host_) {
114 representation =
115 host_->LoadImageForScaleFactor(ui::GetSupportedScaleFactor(scale));
118 if (!representation.is_null())
119 return representation;
121 return blank_image_.GetRepresentation(scale);
124 ////////////////////////////////////////////////////////////////////////////////
125 // IconImage
127 IconImage::IconImage(
128 content::BrowserContext* context,
129 const Extension* extension,
130 const ExtensionIconSet& icon_set,
131 int resource_size_in_dip,
132 const gfx::ImageSkia& default_icon,
133 Observer* observer)
134 : browser_context_(context),
135 extension_(extension),
136 icon_set_(icon_set),
137 resource_size_in_dip_(resource_size_in_dip),
138 source_(NULL),
139 default_icon_(gfx::ImageSkiaOperations::CreateResizedImage(
140 default_icon,
141 skia::ImageOperations::RESIZE_BEST,
142 gfx::Size(resource_size_in_dip, resource_size_in_dip))),
143 weak_ptr_factory_(this) {
144 if (observer)
145 AddObserver(observer);
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 image_ = gfx::Image(image_skia_);
151 registrar_.Add(this,
152 extensions::NOTIFICATION_EXTENSION_REMOVED,
153 content::NotificationService::AllSources());
156 void IconImage::AddObserver(Observer* observer) {
157 observers_.AddObserver(observer);
160 void IconImage::RemoveObserver(Observer* observer) {
161 observers_.RemoveObserver(observer);
164 IconImage::~IconImage() {
165 FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageDestroyed(this));
166 source_->ResetHost();
169 gfx::ImageSkiaRep IconImage::LoadImageForScaleFactor(
170 ui::ScaleFactor scale_factor) {
171 // Do nothing if extension is unloaded.
172 if (!extension_)
173 return gfx::ImageSkiaRep();
175 const float scale = ui::GetScaleForScaleFactor(scale_factor);
176 const int resource_size_in_pixel =
177 static_cast<int>(resource_size_in_dip_ * scale);
179 extensions::ExtensionResource resource;
181 // Find extension resource for non bundled component extensions.
182 resource = GetExtensionIconResource(extension_,
183 icon_set_,
184 resource_size_in_pixel,
185 ExtensionIconSet::MATCH_BIGGER);
187 // 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);
193 // If there is no resource found, return default icon.
194 if (resource.empty())
195 return default_icon_.GetRepresentation(scale);
197 std::vector<ImageLoader::ImageRepresentation> info_list;
198 info_list.push_back(ImageLoader::ImageRepresentation(
199 resource,
200 ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
201 gfx::ToFlooredSize(gfx::ScaleSize(
202 gfx::Size(resource_size_in_dip_, resource_size_in_dip_), scale)),
203 scale_factor));
205 extensions::ImageLoader* loader =
206 extensions::ImageLoader::Get(browser_context_);
207 loader->LoadImagesAsync(extension_, info_list,
208 base::Bind(&IconImage::OnImageLoaded,
209 weak_ptr_factory_.GetWeakPtr(),
210 scale));
212 return gfx::ImageSkiaRep();
215 void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) {
216 const gfx::ImageSkia* image =
217 image_in.IsEmpty() ? &default_icon_ : image_in.ToImageSkia();
219 // Maybe default icon was not set.
220 if (image->isNull())
221 return;
223 gfx::ImageSkiaRep rep = image->GetRepresentation(scale);
224 DCHECK(!rep.is_null());
225 DCHECK_EQ(scale, rep.scale());
227 // Remove fractional scale image representations as they may have become
228 // stale here. These images are generated by ImageSkia on request from
229 // supported scales like 1x, 2x, etc.
230 // TODO(oshima)
231 // A better approach might be to set the |image_| member using the ImageSkia
232 // copy from the image passed in and set the |image_skia_| member using
233 // image_.ToImageSkia(). However that does not work correctly as the
234 // ImageSkia from the image does not contain a source which breaks requests
235 // for scaled images.
236 std::vector<gfx::ImageSkiaRep> reps = image_skia_.image_reps();
237 for (const auto& image_rep : reps) {
238 if (!ui::IsSupportedScale(image_rep.scale()))
239 image_skia_.RemoveRepresentation(image_rep.scale());
242 image_skia_.RemoveRepresentation(scale);
243 image_skia_.AddRepresentation(rep);
245 // Update the image to use the updated image skia.
246 // It's a shame we have to do this because it means that all the other
247 // representations stored on |image_| will be deleted, but unfortunately
248 // there's no way to combine the storage of two images.
249 image_ = gfx::Image(image_skia_);
251 FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageChanged(this));
254 void IconImage::Observe(int type,
255 const content::NotificationSource& source,
256 const content::NotificationDetails& details) {
257 DCHECK_EQ(type, extensions::NOTIFICATION_EXTENSION_REMOVED);
259 const Extension* extension = content::Details<const Extension>(details).ptr();
261 if (extension_ == extension)
262 extension_ = NULL;
265 } // namespace extensions