[email protected] | 326e6f0 | 2014-06-20 04:53:37 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [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/image_loader.h" |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 6 | |
[email protected] | 95fd255 | 2013-07-04 21:19:24 | [diff] [blame] | 7 | #include <map> |
| 8 | #include <vector> |
| 9 | |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 10 | #include "base/callback.h" |
[email protected] | c8b8587b | 2012-11-21 23:23:32 | [diff] [blame] | 11 | #include "base/compiler_specific.h" |
thestig | 9471270 | 2014-09-10 07:46:59 | [diff] [blame] | 12 | #include "base/files/file_util.h" |
[email protected] | 3ea1b18 | 2013-02-08 22:38:41 | [diff] [blame] | 13 | #include "base/strings/string_number_conversions.h" |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 14 | #include "base/threading/sequenced_worker_pool.h" |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 15 | #include "content/public/browser/browser_thread.h" |
[email protected] | 326e6f0 | 2014-06-20 04:53:37 | [diff] [blame] | 16 | #include "extensions/browser/component_extension_resource_manager.h" |
| 17 | #include "extensions/browser/extensions_browser_client.h" |
| 18 | #include "extensions/browser/image_loader_factory.h" |
[email protected] | e4452d3 | 2013-11-15 23:07:41 | [diff] [blame] | 19 | #include "extensions/common/extension.h" |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 20 | #include "skia/ext/image_operations.h" |
| 21 | #include "ui/base/resource/resource_bundle.h" |
[email protected] | da87eec2 | 2013-05-14 09:25:28 | [diff] [blame] | 22 | #include "ui/gfx/codec/png_codec.h" |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 23 | #include "ui/gfx/image/image_family.h" |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 24 | #include "ui/gfx/image/image_skia.h" |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 25 | |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 26 | using content::BrowserThread; |
| 27 | using extensions::Extension; |
[email protected] | 326e6f0 | 2014-06-20 04:53:37 | [diff] [blame] | 28 | using extensions::ExtensionsBrowserClient; |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 29 | using extensions::ImageLoader; |
[email protected] | 1d5e58b | 2013-01-31 08:41:40 | [diff] [blame] | 30 | using extensions::Manifest; |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 31 | |
| 32 | namespace { |
| 33 | |
| 34 | bool ShouldResizeImageRepresentation( |
| 35 | ImageLoader::ImageRepresentation::ResizeCondition resize_method, |
| 36 | const gfx::Size& decoded_size, |
| 37 | const gfx::Size& desired_size) { |
| 38 | switch (resize_method) { |
| 39 | case ImageLoader::ImageRepresentation::ALWAYS_RESIZE: |
| 40 | return decoded_size != desired_size; |
| 41 | case ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER: |
| 42 | return decoded_size.width() > desired_size.width() || |
| 43 | decoded_size.height() > desired_size.height(); |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 44 | case ImageLoader::ImageRepresentation::NEVER_RESIZE: |
| 45 | return false; |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 46 | default: |
| 47 | NOTREACHED(); |
| 48 | return false; |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | SkBitmap ResizeIfNeeded(const SkBitmap& bitmap, |
| 53 | const ImageLoader::ImageRepresentation& image_info) { |
| 54 | gfx::Size original_size(bitmap.width(), bitmap.height()); |
| 55 | if (ShouldResizeImageRepresentation(image_info.resize_condition, |
| 56 | original_size, |
| 57 | image_info.desired_size)) { |
| 58 | return skia::ImageOperations::Resize( |
| 59 | bitmap, skia::ImageOperations::RESIZE_LANCZOS3, |
| 60 | image_info.desired_size.width(), image_info.desired_size.height()); |
| 61 | } |
| 62 | |
| 63 | return bitmap; |
| 64 | } |
| 65 | |
| 66 | void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) { |
[email protected] | 54ee819 | 2014-03-29 17:37:24 | [diff] [blame] | 67 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 68 | |
| 69 | gfx::ImageSkia image( |
| 70 | *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id)); |
| 71 | image.MakeThreadSafe(); |
| 72 | *bitmap = *image.bitmap(); |
| 73 | } |
| 74 | |
| 75 | void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation& image_info, |
| 76 | SkBitmap* bitmap) { |
| 77 | DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| 78 | |
| 79 | // Read the file from disk. |
| 80 | std::string file_contents; |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 81 | base::FilePath path = image_info.resource.GetFilePath(); |
[email protected] | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 82 | if (path.empty() || !base::ReadFileToString(path, &file_contents)) { |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 83 | return; |
| 84 | } |
| 85 | |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 86 | const unsigned char* data = |
| 87 | reinterpret_cast<const unsigned char*>(file_contents.data()); |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 88 | // Note: This class only decodes bitmaps from extension resources. Chrome |
| 89 | // doesn't (for security reasons) directly load extension resources provided |
| 90 | // by the extension author, but instead decodes them in a separate |
| 91 | // locked-down utility process. Only if the decoding succeeds is the image |
| 92 | // saved from memory to disk and subsequently used in the Chrome UI. |
| 93 | // Chrome is therefore decoding bitmaps here that were generated by Chrome. |
[email protected] | da87eec2 | 2013-05-14 09:25:28 | [diff] [blame] | 94 | gfx::PNGCodec::Decode(data, file_contents.length(), bitmap); |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 95 | } |
| 96 | |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 97 | std::vector<SkBitmap> LoadResourceBitmaps( |
| 98 | const Extension* extension, |
| 99 | const std::vector<ImageLoader::ImageRepresentation>& info_list) { |
| 100 | // Loading resources has to happen on the UI thread. So do this first, and |
| 101 | // pass the rest of the work off as a blocking pool task. |
| 102 | std::vector<SkBitmap> bitmaps; |
| 103 | bitmaps.resize(info_list.size()); |
| 104 | |
| 105 | int i = 0; |
| 106 | for (std::vector<ImageLoader::ImageRepresentation>::const_iterator |
| 107 | it = info_list.begin(); |
| 108 | it != info_list.end(); |
| 109 | ++it, ++i) { |
| 110 | DCHECK(it->resource.relative_path().empty() || |
| 111 | extension->path() == it->resource.extension_root()); |
| 112 | |
| 113 | int resource_id; |
[email protected] | 326e6f0 | 2014-06-20 04:53:37 | [diff] [blame] | 114 | if (extension->location() == Manifest::COMPONENT) { |
mukai | ee458c9 | 2015-01-06 01:30:33 | [diff] [blame] | 115 | const extensions::ComponentExtensionResourceManager* manager = |
| 116 | extensions::ExtensionsBrowserClient::Get() |
| 117 | ->GetComponentExtensionResourceManager(); |
[email protected] | 326e6f0 | 2014-06-20 04:53:37 | [diff] [blame] | 118 | if (manager && manager->IsComponentExtensionResource( |
| 119 | extension->path(), it->resource.relative_path(), &resource_id)) { |
| 120 | LoadResourceOnUIThread(resource_id, &bitmaps[i]); |
| 121 | } |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 122 | } |
| 123 | } |
| 124 | return bitmaps; |
| 125 | } |
| 126 | |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 127 | } // namespace |
| 128 | |
| 129 | namespace extensions { |
| 130 | |
| 131 | //////////////////////////////////////////////////////////////////////////////// |
| 132 | // ImageLoader::ImageRepresentation |
| 133 | |
| 134 | ImageLoader::ImageRepresentation::ImageRepresentation( |
| 135 | const ExtensionResource& resource, |
| 136 | ResizeCondition resize_condition, |
| 137 | const gfx::Size& desired_size, |
| 138 | ui::ScaleFactor scale_factor) |
| 139 | : resource(resource), |
| 140 | resize_condition(resize_condition), |
| 141 | desired_size(desired_size), |
| 142 | scale_factor(scale_factor) { |
| 143 | } |
| 144 | |
| 145 | ImageLoader::ImageRepresentation::~ImageRepresentation() { |
| 146 | } |
| 147 | |
| 148 | //////////////////////////////////////////////////////////////////////////////// |
| 149 | // ImageLoader::LoadResult |
| 150 | |
| 151 | struct ImageLoader::LoadResult { |
| 152 | LoadResult(const SkBitmap& bitmap, |
| 153 | const gfx::Size& original_size, |
| 154 | const ImageRepresentation& image_representation); |
| 155 | ~LoadResult(); |
| 156 | |
| 157 | SkBitmap bitmap; |
| 158 | gfx::Size original_size; |
| 159 | ImageRepresentation image_representation; |
| 160 | }; |
| 161 | |
| 162 | ImageLoader::LoadResult::LoadResult( |
| 163 | const SkBitmap& bitmap, |
| 164 | const gfx::Size& original_size, |
| 165 | const ImageLoader::ImageRepresentation& image_representation) |
| 166 | : bitmap(bitmap), |
| 167 | original_size(original_size), |
| 168 | image_representation(image_representation) { |
| 169 | } |
| 170 | |
| 171 | ImageLoader::LoadResult::~LoadResult() { |
| 172 | } |
| 173 | |
[email protected] | f5bb764 | 2013-11-23 19:03:53 | [diff] [blame] | 174 | namespace { |
| 175 | |
| 176 | // Need to be after ImageRepresentation and LoadResult are defined. |
| 177 | std::vector<ImageLoader::LoadResult> LoadImagesOnBlockingPool( |
| 178 | const std::vector<ImageLoader::ImageRepresentation>& info_list, |
| 179 | const std::vector<SkBitmap>& bitmaps) { |
| 180 | DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| 181 | std::vector<ImageLoader::LoadResult> load_result; |
| 182 | |
| 183 | for (size_t i = 0; i < info_list.size(); ++i) { |
| 184 | const ImageLoader::ImageRepresentation& image = info_list[i]; |
| 185 | |
| 186 | // If we don't have a path there isn't anything we can do, just skip it. |
| 187 | if (image.resource.relative_path().empty()) |
| 188 | continue; |
| 189 | |
| 190 | SkBitmap bitmap; |
| 191 | if (bitmaps[i].isNull()) |
| 192 | LoadImageOnBlockingPool(image, &bitmap); |
| 193 | else |
| 194 | bitmap = bitmaps[i]; |
| 195 | |
| 196 | // If the image failed to load, skip it. |
| 197 | if (bitmap.isNull() || bitmap.empty()) |
| 198 | continue; |
| 199 | |
| 200 | gfx::Size original_size(bitmap.width(), bitmap.height()); |
| 201 | bitmap = ResizeIfNeeded(bitmap, image); |
| 202 | |
| 203 | load_result.push_back( |
| 204 | ImageLoader::LoadResult(bitmap, original_size, image)); |
| 205 | } |
| 206 | |
| 207 | return load_result; |
| 208 | } |
| 209 | |
| 210 | } // namespace |
| 211 | |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 212 | //////////////////////////////////////////////////////////////////////////////// |
| 213 | // ImageLoader |
| 214 | |
[email protected] | d77020ce | 2013-10-08 16:00:57 | [diff] [blame] | 215 | ImageLoader::ImageLoader() |
| 216 | : weak_ptr_factory_(this) { |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 217 | } |
| 218 | |
| 219 | ImageLoader::~ImageLoader() { |
| 220 | } |
| 221 | |
| 222 | // static |
[email protected] | 472522b | 2013-10-25 00:41:28 | [diff] [blame] | 223 | ImageLoader* ImageLoader::Get(content::BrowserContext* context) { |
| 224 | return ImageLoaderFactory::GetForBrowserContext(context); |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 225 | } |
| 226 | |
[email protected] | f5bb764 | 2013-11-23 19:03:53 | [diff] [blame] | 227 | void ImageLoader::LoadImageAsync(const Extension* extension, |
| 228 | const ExtensionResource& resource, |
| 229 | const gfx::Size& max_size, |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 230 | const ImageLoaderImageCallback& callback) { |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 231 | std::vector<ImageRepresentation> info_list; |
| 232 | info_list.push_back(ImageRepresentation( |
| 233 | resource, |
| 234 | ImageRepresentation::RESIZE_WHEN_LARGER, |
| 235 | max_size, |
| 236 | ui::SCALE_FACTOR_100P)); |
| 237 | LoadImagesAsync(extension, info_list, callback); |
| 238 | } |
| 239 | |
| 240 | void ImageLoader::LoadImagesAsync( |
| 241 | const Extension* extension, |
| 242 | const std::vector<ImageRepresentation>& info_list, |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 243 | const ImageLoaderImageCallback& callback) { |
[email protected] | 54ee819 | 2014-03-29 17:37:24 | [diff] [blame] | 244 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 245 | DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
[email protected] | f5bb764 | 2013-11-23 19:03:53 | [diff] [blame] | 246 | base::PostTaskAndReplyWithResult( |
| 247 | BrowserThread::GetBlockingPool(), |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 248 | FROM_HERE, |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 249 | base::Bind(LoadImagesOnBlockingPool, |
| 250 | info_list, |
| 251 | LoadResourceBitmaps(extension, info_list)), |
| 252 | base::Bind( |
| 253 | &ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(), callback)); |
| 254 | } |
| 255 | |
| 256 | void ImageLoader::LoadImageFamilyAsync( |
| 257 | const extensions::Extension* extension, |
| 258 | const std::vector<ImageRepresentation>& info_list, |
| 259 | const ImageLoaderImageFamilyCallback& callback) { |
[email protected] | 54ee819 | 2014-03-29 17:37:24 | [diff] [blame] | 260 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 261 | DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| 262 | base::PostTaskAndReplyWithResult( |
| 263 | BrowserThread::GetBlockingPool(), |
| 264 | FROM_HERE, |
| 265 | base::Bind(LoadImagesOnBlockingPool, |
| 266 | info_list, |
| 267 | LoadResourceBitmaps(extension, info_list)), |
| 268 | base::Bind(&ImageLoader::ReplyBackWithImageFamily, |
| 269 | weak_ptr_factory_.GetWeakPtr(), |
[email protected] | f5bb764 | 2013-11-23 19:03:53 | [diff] [blame] | 270 | callback)); |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 271 | } |
| 272 | |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 273 | void ImageLoader::ReplyBack(const ImageLoaderImageCallback& callback, |
[email protected] | f5bb764 | 2013-11-23 19:03:53 | [diff] [blame] | 274 | const std::vector<LoadResult>& load_result) { |
[email protected] | 54ee819 | 2014-03-29 17:37:24 | [diff] [blame] | 275 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 276 | |
| 277 | gfx::ImageSkia image_skia; |
| 278 | |
[email protected] | f5bb764 | 2013-11-23 19:03:53 | [diff] [blame] | 279 | for (std::vector<LoadResult>::const_iterator it = load_result.begin(); |
| 280 | it != load_result.end(); ++it) { |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 281 | const SkBitmap& bitmap = it->bitmap; |
| 282 | const ImageRepresentation& image_rep = it->image_representation; |
| 283 | |
| 284 | image_skia.AddRepresentation(gfx::ImageSkiaRep( |
[email protected] | 50b6626 | 2013-09-24 03:25:48 | [diff] [blame] | 285 | bitmap, |
[email protected] | 3ea137b | 2014-05-22 09:27:35 | [diff] [blame] | 286 | ui::GetScaleForScaleFactor(image_rep.scale_factor))); |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | gfx::Image image; |
| 290 | if (!image_skia.isNull()) { |
| 291 | image_skia.MakeThreadSafe(); |
| 292 | image = gfx::Image(image_skia); |
| 293 | } |
| 294 | |
| 295 | callback.Run(image); |
| 296 | } |
| 297 | |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 298 | void ImageLoader::ReplyBackWithImageFamily( |
| 299 | const ImageLoaderImageFamilyCallback& callback, |
| 300 | const std::vector<LoadResult>& load_result) { |
[email protected] | 54ee819 | 2014-03-29 17:37:24 | [diff] [blame] | 301 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 302 | |
| 303 | std::map<std::pair<int, int>, gfx::ImageSkia> image_skia_map; |
| 304 | gfx::ImageFamily image_family; |
| 305 | |
| 306 | for (std::vector<LoadResult>::const_iterator it = load_result.begin(); |
| 307 | it != load_result.end(); |
| 308 | ++it) { |
| 309 | const SkBitmap& bitmap = it->bitmap; |
| 310 | const ImageRepresentation& image_rep = it->image_representation; |
| 311 | const std::pair<int, int> key = std::make_pair( |
| 312 | image_rep.desired_size.width(), image_rep.desired_size.height()); |
| 313 | // Create a new ImageSkia for this width/height, or add a representation to |
| 314 | // an existing ImageSkia with the same width/height. |
| 315 | image_skia_map[key].AddRepresentation( |
[email protected] | 3ea137b | 2014-05-22 09:27:35 | [diff] [blame] | 316 | gfx::ImageSkiaRep(bitmap, |
| 317 | ui::GetScaleForScaleFactor(image_rep.scale_factor))); |
[email protected] | 1d8e0f3 | 2014-03-17 06:39:19 | [diff] [blame] | 318 | } |
| 319 | |
| 320 | for (std::map<std::pair<int, int>, gfx::ImageSkia>::iterator it = |
| 321 | image_skia_map.begin(); |
| 322 | it != image_skia_map.end(); |
| 323 | ++it) { |
| 324 | it->second.MakeThreadSafe(); |
| 325 | image_family.Add(it->second); |
| 326 | } |
| 327 | |
| 328 | callback.Run(image_family); |
| 329 | } |
| 330 | |
[email protected] | ec7de0c5a | 2012-11-16 07:40:47 | [diff] [blame] | 331 | } // namespace extensions |