[email protected] | 326e6f0 | 2014-06-20 04:53:37 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 326e6f0 | 2014-06-20 04:53:37 | [diff] [blame] | 5 | #include "extensions/browser/extension_icon_image.h" |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 6 | |
| 7 | #include <vector> |
| 8 | |
[email protected] | 6980547 | 2013-02-08 15:41:20 | [diff] [blame] | 9 | #include "base/bind.h" |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 10 | #include "content/public/browser/notification_service.h" |
[email protected] | 326e6f0 | 2014-06-20 04:53:37 | [diff] [blame] | 11 | #include "extensions/browser/image_loader.h" |
[email protected] | adf5a10 | 2014-07-31 12:44:06 | [diff] [blame] | 12 | #include "extensions/browser/notification_types.h" |
[email protected] | e4452d3 | 2013-11-15 23:07:41 | [diff] [blame] | 13 | #include "extensions/common/extension.h" |
ananta | 764a6e3d | 2015-03-25 20:04:32 | [diff] [blame] | 14 | #include "ui/base/layout.h" |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 15 | #include "ui/gfx/canvas.h" |
tfarina | ebe974f0 | 2015-01-03 04:25:32 | [diff] [blame] | 16 | #include "ui/gfx/geometry/size.h" |
| 17 | #include "ui/gfx/geometry/size_conversions.h" |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 18 | #include "ui/gfx/image/canvas_image_source.h" |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 19 | #include "ui/gfx/image/image.h" |
[email protected] | 7240505e | 2012-11-28 04:42:29 | [diff] [blame] | 20 | #include "ui/gfx/image/image_skia_operations.h" |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 21 | #include "ui/gfx/image/image_skia_source.h" |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 22 | |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 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 |
[email protected] | 75b91fd | 2013-02-12 22:14:25 | [diff] [blame] | 31 | // ImageLoader. |
| 32 | // - |ImageLoader| is asynchronous. |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 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 |
[email protected] | 75b91fd | 2013-02-12 22:14:25 | [diff] [blame] | 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 |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 40 | // representation). |
| 41 | |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 42 | namespace { |
| 43 | |
[email protected] | 993da5e | 2013-03-23 21:25:16 | [diff] [blame] | 44 | extensions::ExtensionResource GetExtensionIconResource( |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 45 | const extensions::Extension* extension, |
| 46 | const ExtensionIconSet& icons, |
| 47 | int size, |
| 48 | ExtensionIconSet::MatchType match_type) { |
[email protected] | 1771f16 | 2014-08-07 22:52:16 | [diff] [blame] | 49 | const std::string& path = icons.Get(size, match_type); |
| 50 | return path.empty() ? extensions::ExtensionResource() |
| 51 | : extension->GetResource(path); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 52 | } |
| 53 | |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 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) { |
| 58 | } |
dcheng | 9168b2f | 2014-10-21 12:38:24 | [diff] [blame] | 59 | ~BlankImageSource() override {} |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 60 | |
| 61 | private: |
| 62 | // gfx::CanvasImageSource overrides: |
dcheng | 9168b2f | 2014-10-21 12:38:24 | [diff] [blame] | 63 | void Draw(gfx::Canvas* canvas) override { |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 64 | canvas->DrawColor(SkColorSetARGB(0, 0, 0, 0)); |
| 65 | } |
| 66 | |
| 67 | DISALLOW_COPY_AND_ASSIGN(BlankImageSource); |
| 68 | }; |
| 69 | |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 70 | } // namespace |
| 71 | |
| 72 | namespace extensions { |
| 73 | |
| 74 | //////////////////////////////////////////////////////////////////////////////// |
[email protected] | 6f6ccb0 | 2012-09-11 03:38:10 | [diff] [blame] | 75 | // IconImage::Source |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 76 | |
| 77 | class IconImage::Source : public gfx::ImageSkiaSource { |
| 78 | public: |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 79 | Source(IconImage* host, const gfx::Size& size_in_dip); |
dcheng | 9168b2f | 2014-10-21 12:38:24 | [diff] [blame] | 80 | ~Source() override; |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 81 | |
| 82 | void ResetHost(); |
| 83 | |
| 84 | private: |
| 85 | // gfx::ImageSkiaSource overrides: |
dcheng | 9168b2f | 2014-10-21 12:38:24 | [diff] [blame] | 86 | gfx::ImageSkiaRep GetImageForScale(float scale) override; |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 87 | |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 88 | // Used to load images, possibly asynchronously. NULLed out when the IconImage |
| 89 | // is destroyed. |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 90 | IconImage* host_; |
| 91 | |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 92 | // Image whose representations will be used until |host_| loads the real |
| 93 | // representations for the image. |
| 94 | gfx::ImageSkia blank_image_; |
| 95 | |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 96 | DISALLOW_COPY_AND_ASSIGN(Source); |
| 97 | }; |
| 98 | |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 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) { |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 102 | } |
| 103 | |
| 104 | IconImage::Source::~Source() { |
| 105 | } |
| 106 | |
| 107 | void IconImage::Source::ResetHost() { |
| 108 | host_ = NULL; |
| 109 | } |
| 110 | |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 111 | gfx::ImageSkiaRep IconImage::Source::GetImageForScale(float scale) { |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 112 | gfx::ImageSkiaRep representation; |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 113 | if (host_) { |
| 114 | representation = |
| 115 | host_->LoadImageForScaleFactor(ui::GetSupportedScaleFactor(scale)); |
| 116 | } |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 117 | |
| 118 | if (!representation.is_null()) |
| 119 | return representation; |
| 120 | |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 121 | return blank_image_.GetRepresentation(scale); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | //////////////////////////////////////////////////////////////////////////////// |
[email protected] | 6f6ccb0 | 2012-09-11 03:38:10 | [diff] [blame] | 125 | // IconImage |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 126 | |
| 127 | IconImage::IconImage( |
[email protected] | 472522b | 2013-10-25 00:41:28 | [diff] [blame] | 128 | content::BrowserContext* context, |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 129 | const Extension* extension, |
| 130 | const ExtensionIconSet& icon_set, |
| 131 | int resource_size_in_dip, |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 132 | const gfx::ImageSkia& default_icon, |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 133 | Observer* observer) |
[email protected] | 472522b | 2013-10-25 00:41:28 | [diff] [blame] | 134 | : browser_context_(context), |
[email protected] | 6980547 | 2013-02-08 15:41:20 | [diff] [blame] | 135 | extension_(extension), |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 136 | icon_set_(icon_set), |
| 137 | resource_size_in_dip_(resource_size_in_dip), |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 138 | source_(NULL), |
[email protected] | 7240505e | 2012-11-28 04:42:29 | [diff] [blame] | 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))), |
[email protected] | 9c00909 | 2013-05-01 03:14:09 | [diff] [blame] | 143 | weak_ptr_factory_(this) { |
rdevlin.cronin | f19c7663 | 2015-01-29 16:57:35 | [diff] [blame] | 144 | if (observer) |
| 145 | AddObserver(observer); |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 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); |
rdevlin.cronin | bf734cd | 2015-01-29 20:03:53 | [diff] [blame] | 149 | image_ = gfx::Image(image_skia_); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 150 | |
[email protected] | 8c17903 | 2014-03-20 22:49:10 | [diff] [blame] | 151 | registrar_.Add(this, |
[email protected] | adf5a10 | 2014-07-31 12:44:06 | [diff] [blame] | 152 | extensions::NOTIFICATION_EXTENSION_REMOVED, |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 153 | content::NotificationService::AllSources()); |
| 154 | } |
| 155 | |
rdevlin.cronin | f19c7663 | 2015-01-29 16:57:35 | [diff] [blame] | 156 | void IconImage::AddObserver(Observer* observer) { |
| 157 | observers_.AddObserver(observer); |
| 158 | } |
| 159 | |
| 160 | void IconImage::RemoveObserver(Observer* observer) { |
| 161 | observers_.RemoveObserver(observer); |
| 162 | } |
| 163 | |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 164 | IconImage::~IconImage() { |
rdevlin.cronin | f19c7663 | 2015-01-29 16:57:35 | [diff] [blame] | 165 | FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageDestroyed(this)); |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 166 | source_->ResetHost(); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 167 | } |
| 168 | |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 169 | gfx::ImageSkiaRep IconImage::LoadImageForScaleFactor( |
| 170 | ui::ScaleFactor scale_factor) { |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 171 | // Do nothing if extension is unloaded. |
| 172 | if (!extension_) |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 173 | return gfx::ImageSkiaRep(); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 174 | |
[email protected] | 4d617d01 | 2014-05-21 03:56:33 | [diff] [blame] | 175 | const float scale = ui::GetScaleForScaleFactor(scale_factor); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 176 | const int resource_size_in_pixel = |
| 177 | static_cast<int>(resource_size_in_dip_ * scale); |
| 178 | |
[email protected] | 993da5e | 2013-03-23 21:25:16 | [diff] [blame] | 179 | extensions::ExtensionResource resource; |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 180 | |
[email protected] | 6f6ccb0 | 2012-09-11 03:38:10 | [diff] [blame] | 181 | // Find extension resource for non bundled component extensions. |
[email protected] | 9cfffad | 2014-08-19 17:42:31 | [diff] [blame] | 182 | resource = GetExtensionIconResource(extension_, |
| 183 | icon_set_, |
| 184 | resource_size_in_pixel, |
| 185 | ExtensionIconSet::MATCH_BIGGER); |
[email protected] | b9aae8e0 | 2012-10-03 19:42:57 | [diff] [blame] | 186 | |
[email protected] | d633575 | 2012-10-04 16:19:36 | [diff] [blame] | 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); |
| 191 | } |
| 192 | |
| 193 | // If there is no resource found, return default icon. |
| 194 | if (resource.empty()) |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 195 | return default_icon_.GetRepresentation(scale); |
[email protected] | d633575 | 2012-10-04 16:19:36 | [diff] [blame] | 196 | |
[email protected] | 6980547 | 2013-02-08 15:41:20 | [diff] [blame] | 197 | std::vector<ImageLoader::ImageRepresentation> info_list; |
| 198 | info_list.push_back(ImageLoader::ImageRepresentation( |
danakj | ddaec91 | 2015-09-25 19:38:40 | [diff] [blame] | 199 | resource, ImageLoader::ImageRepresentation::ALWAYS_RESIZE, |
| 200 | gfx::ScaleToFlooredSize( |
| 201 | gfx::Size(resource_size_in_dip_, resource_size_in_dip_), scale), |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 202 | scale_factor)); |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 203 | |
[email protected] | 472522b | 2013-10-25 00:41:28 | [diff] [blame] | 204 | extensions::ImageLoader* loader = |
| 205 | extensions::ImageLoader::Get(browser_context_); |
[email protected] | 6980547 | 2013-02-08 15:41:20 | [diff] [blame] | 206 | loader->LoadImagesAsync(extension_, info_list, |
| 207 | base::Bind(&IconImage::OnImageLoaded, |
| 208 | weak_ptr_factory_.GetWeakPtr(), |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 209 | scale)); |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 210 | |
| 211 | return gfx::ImageSkiaRep(); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 212 | } |
| 213 | |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 214 | void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) { |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 215 | const gfx::ImageSkia* image = |
| 216 | image_in.IsEmpty() ? &default_icon_ : image_in.ToImageSkia(); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 217 | |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 218 | // Maybe default icon was not set. |
| 219 | if (image->isNull()) |
| 220 | return; |
| 221 | |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 222 | gfx::ImageSkiaRep rep = image->GetRepresentation(scale); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 223 | DCHECK(!rep.is_null()); |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 224 | DCHECK_EQ(scale, rep.scale()); |
[email protected] | 99d3005 | 2012-09-07 05:42:21 | [diff] [blame] | 225 | |
ananta | 764a6e3d | 2015-03-25 20:04:32 | [diff] [blame] | 226 | // 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(); |
ananta | b5ff8d1f | 2015-03-31 03:01:59 | [diff] [blame] | 236 | for (const auto& image_rep : reps) { |
| 237 | if (!ui::IsSupportedScale(image_rep.scale())) |
| 238 | image_skia_.RemoveRepresentation(image_rep.scale()); |
ananta | 764a6e3d | 2015-03-25 20:04:32 | [diff] [blame] | 239 | } |
| 240 | |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 241 | image_skia_.RemoveRepresentation(scale); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 242 | image_skia_.AddRepresentation(rep); |
| 243 | |
rdevlin.cronin | bf734cd | 2015-01-29 20:03:53 | [diff] [blame] | 244 | // 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.cronin | f19c7663 | 2015-01-29 16:57:35 | [diff] [blame] | 250 | FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageChanged(this)); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 251 | } |
| 252 | |
| 253 | void IconImage::Observe(int type, |
| 254 | const content::NotificationSource& source, |
| 255 | const content::NotificationDetails& details) { |
[email protected] | adf5a10 | 2014-07-31 12:44:06 | [diff] [blame] | 256 | DCHECK_EQ(type, extensions::NOTIFICATION_EXTENSION_REMOVED); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 257 | |
[email protected] | 8c17903 | 2014-03-20 22:49:10 | [diff] [blame] | 258 | const Extension* extension = content::Details<const Extension>(details).ptr(); |
[email protected] | 74c938f | 2012-08-20 22:22:42 | [diff] [blame] | 259 | |
| 260 | if (extension_ == extension) |
| 261 | extension_ = NULL; |
| 262 | } |
| 263 | |
| 264 | } // namespace extensions |