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