[Smart Lock] Record a detailed UMA metric for each unlock attempt by Smart Lock users.
[chromium-blink-merge.git] / extensions / browser / extension_icon_image.cc
blob5ebc40017920f6190631bb21baf4fb5030308d28
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/gfx/canvas.h"
15 #include "ui/gfx/geometry/size.h"
16 #include "ui/gfx/geometry/size_conversions.h"
17 #include "ui/gfx/image/canvas_image_source.h"
18 #include "ui/gfx/image/image.h"
19 #include "ui/gfx/image/image_skia_operations.h"
20 #include "ui/gfx/image/image_skia_source.h"
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).
41 namespace {
43 extensions::ExtensionResource GetExtensionIconResource(
44 const extensions::Extension* extension,
45 const ExtensionIconSet& icons,
46 int size,
47 ExtensionIconSet::MatchType match_type) {
48 const std::string& path = icons.Get(size, match_type);
49 return path.empty() ? extensions::ExtensionResource()
50 : extension->GetResource(path);
53 class BlankImageSource : public gfx::CanvasImageSource {
54 public:
55 explicit BlankImageSource(const gfx::Size& size_in_dip)
56 : CanvasImageSource(size_in_dip, /*is_opaque =*/ false) {
58 ~BlankImageSource() override {}
60 private:
61 // gfx::CanvasImageSource overrides:
62 void Draw(gfx::Canvas* canvas) override {
63 canvas->DrawColor(SkColorSetARGB(0, 0, 0, 0));
66 DISALLOW_COPY_AND_ASSIGN(BlankImageSource);
69 } // namespace
71 namespace extensions {
73 ////////////////////////////////////////////////////////////////////////////////
74 // IconImage::Source
76 class IconImage::Source : public gfx::ImageSkiaSource {
77 public:
78 Source(IconImage* host, const gfx::Size& size_in_dip);
79 ~Source() override;
81 void ResetHost();
83 private:
84 // gfx::ImageSkiaSource overrides:
85 gfx::ImageSkiaRep GetImageForScale(float scale) override;
87 // Used to load images, possibly asynchronously. NULLed out when the IconImage
88 // is destroyed.
89 IconImage* host_;
91 // Image whose representations will be used until |host_| loads the real
92 // representations for the image.
93 gfx::ImageSkia blank_image_;
95 DISALLOW_COPY_AND_ASSIGN(Source);
98 IconImage::Source::Source(IconImage* host, const gfx::Size& size_in_dip)
99 : host_(host),
100 blank_image_(new BlankImageSource(size_in_dip), size_in_dip) {
103 IconImage::Source::~Source() {
106 void IconImage::Source::ResetHost() {
107 host_ = NULL;
110 gfx::ImageSkiaRep IconImage::Source::GetImageForScale(float scale) {
111 gfx::ImageSkiaRep representation;
112 if (host_) {
113 representation =
114 host_->LoadImageForScaleFactor(ui::GetSupportedScaleFactor(scale));
117 if (!representation.is_null())
118 return representation;
120 return blank_image_.GetRepresentation(scale);
123 ////////////////////////////////////////////////////////////////////////////////
124 // IconImage
126 IconImage::IconImage(
127 content::BrowserContext* context,
128 const Extension* extension,
129 const ExtensionIconSet& icon_set,
130 int resource_size_in_dip,
131 const gfx::ImageSkia& default_icon,
132 Observer* observer)
133 : browser_context_(context),
134 extension_(extension),
135 icon_set_(icon_set),
136 resource_size_in_dip_(resource_size_in_dip),
137 source_(NULL),
138 default_icon_(gfx::ImageSkiaOperations::CreateResizedImage(
139 default_icon,
140 skia::ImageOperations::RESIZE_BEST,
141 gfx::Size(resource_size_in_dip, resource_size_in_dip))),
142 weak_ptr_factory_(this) {
143 if (observer)
144 AddObserver(observer);
145 gfx::Size resource_size(resource_size_in_dip, resource_size_in_dip);
146 source_ = new Source(this, resource_size);
147 image_skia_ = gfx::ImageSkia(source_, resource_size);
148 image_ = gfx::Image(image_skia_);
150 registrar_.Add(this,
151 extensions::NOTIFICATION_EXTENSION_REMOVED,
152 content::NotificationService::AllSources());
155 void IconImage::AddObserver(Observer* observer) {
156 observers_.AddObserver(observer);
159 void IconImage::RemoveObserver(Observer* observer) {
160 observers_.RemoveObserver(observer);
163 IconImage::~IconImage() {
164 FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageDestroyed(this));
165 source_->ResetHost();
168 gfx::ImageSkiaRep IconImage::LoadImageForScaleFactor(
169 ui::ScaleFactor scale_factor) {
170 // Do nothing if extension is unloaded.
171 if (!extension_)
172 return gfx::ImageSkiaRep();
174 const float scale = ui::GetScaleForScaleFactor(scale_factor);
175 const int resource_size_in_pixel =
176 static_cast<int>(resource_size_in_dip_ * scale);
178 extensions::ExtensionResource resource;
180 // Find extension resource for non bundled component extensions.
181 resource = GetExtensionIconResource(extension_,
182 icon_set_,
183 resource_size_in_pixel,
184 ExtensionIconSet::MATCH_BIGGER);
186 // If resource is not found by now, try matching smaller one.
187 if (resource.empty()) {
188 resource = GetExtensionIconResource(extension_, icon_set_,
189 resource_size_in_pixel, ExtensionIconSet::MATCH_SMALLER);
192 // If there is no resource found, return default icon.
193 if (resource.empty())
194 return default_icon_.GetRepresentation(scale);
196 std::vector<ImageLoader::ImageRepresentation> info_list;
197 info_list.push_back(ImageLoader::ImageRepresentation(
198 resource,
199 ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
200 gfx::ToFlooredSize(gfx::ScaleSize(
201 gfx::Size(resource_size_in_dip_, resource_size_in_dip_), scale)),
202 scale_factor));
204 extensions::ImageLoader* loader =
205 extensions::ImageLoader::Get(browser_context_);
206 loader->LoadImagesAsync(extension_, info_list,
207 base::Bind(&IconImage::OnImageLoaded,
208 weak_ptr_factory_.GetWeakPtr(),
209 scale));
211 return gfx::ImageSkiaRep();
214 void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) {
215 const gfx::ImageSkia* image =
216 image_in.IsEmpty() ? &default_icon_ : image_in.ToImageSkia();
218 // Maybe default icon was not set.
219 if (image->isNull())
220 return;
222 gfx::ImageSkiaRep rep = image->GetRepresentation(scale);
223 DCHECK(!rep.is_null());
224 DCHECK_EQ(scale, rep.scale());
226 // Remove old representation if there is one.
227 image_skia_.RemoveRepresentation(scale);
228 image_skia_.AddRepresentation(rep);
230 // Update the image to use the updated image skia.
231 // It's a shame we have to do this because it means that all the other
232 // representations stored on |image_| will be deleted, but unfortunately
233 // there's no way to combine the storage of two images.
234 image_ = gfx::Image(image_skia_);
236 FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageChanged(this));
239 void IconImage::Observe(int type,
240 const content::NotificationSource& source,
241 const content::NotificationDetails& details) {
242 DCHECK_EQ(type, extensions::NOTIFICATION_EXTENSION_REMOVED);
244 const Extension* extension = content::Details<const Extension>(details).ptr();
246 if (extension_ == extension)
247 extension_ = NULL;
250 } // namespace extensions