blob: 61563286e21dfdfdb878dba44999228f8ad39257 [file] [log] [blame]
[email protected]326e6f02014-06-20 04:53:371// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]ec7de0c5a2012-11-16 07:40:472// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]326e6f02014-06-20 04:53:375#include "extensions/browser/image_loader.h"
[email protected]ec7de0c5a2012-11-16 07:40:476
[email protected]95fd2552013-07-04 21:19:247#include <map>
8#include <vector>
9
[email protected]ec7de0c5a2012-11-16 07:40:4710#include "base/callback.h"
[email protected]c8b8587b2012-11-21 23:23:3211#include "base/compiler_specific.h"
thestig94712702014-09-10 07:46:5912#include "base/files/file_util.h"
[email protected]3ea1b182013-02-08 22:38:4113#include "base/strings/string_number_conversions.h"
[email protected]ec7de0c5a2012-11-16 07:40:4714#include "base/threading/sequenced_worker_pool.h"
[email protected]ec7de0c5a2012-11-16 07:40:4715#include "content/public/browser/browser_thread.h"
[email protected]326e6f02014-06-20 04:53:3716#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]e4452d32013-11-15 23:07:4119#include "extensions/common/extension.h"
[email protected]ec7de0c5a2012-11-16 07:40:4720#include "skia/ext/image_operations.h"
21#include "ui/base/resource/resource_bundle.h"
[email protected]da87eec22013-05-14 09:25:2822#include "ui/gfx/codec/png_codec.h"
[email protected]1d8e0f32014-03-17 06:39:1923#include "ui/gfx/image/image_family.h"
[email protected]ec7de0c5a2012-11-16 07:40:4724#include "ui/gfx/image/image_skia.h"
[email protected]ec7de0c5a2012-11-16 07:40:4725
[email protected]ec7de0c5a2012-11-16 07:40:4726using content::BrowserThread;
27using extensions::Extension;
[email protected]326e6f02014-06-20 04:53:3728using extensions::ExtensionsBrowserClient;
[email protected]ec7de0c5a2012-11-16 07:40:4729using extensions::ImageLoader;
[email protected]1d5e58b2013-01-31 08:41:4030using extensions::Manifest;
[email protected]ec7de0c5a2012-11-16 07:40:4731
32namespace {
33
34bool 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]1d8e0f32014-03-17 06:39:1944 case ImageLoader::ImageRepresentation::NEVER_RESIZE:
45 return false;
[email protected]ec7de0c5a2012-11-16 07:40:4746 default:
47 NOTREACHED();
48 return false;
49 }
50}
51
52SkBitmap 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
66void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) {
[email protected]54ee8192014-03-29 17:37:2467 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]ec7de0c5a2012-11-16 07:40:4768
69 gfx::ImageSkia image(
70 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id));
71 image.MakeThreadSafe();
72 *bitmap = *image.bitmap();
73}
74
75void 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]650b2d52013-02-10 03:41:4581 base::FilePath path = image_info.resource.GetFilePath();
[email protected]82f84b92013-08-30 18:23:5082 if (path.empty() || !base::ReadFileToString(path, &file_contents)) {
[email protected]ec7de0c5a2012-11-16 07:40:4783 return;
84 }
85
[email protected]ec7de0c5a2012-11-16 07:40:4786 const unsigned char* data =
87 reinterpret_cast<const unsigned char*>(file_contents.data());
[email protected]ec7de0c5a2012-11-16 07:40:4788 // 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]da87eec22013-05-14 09:25:2894 gfx::PNGCodec::Decode(data, file_contents.length(), bitmap);
[email protected]ec7de0c5a2012-11-16 07:40:4795}
96
[email protected]1d8e0f32014-03-17 06:39:1997std::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]326e6f02014-06-20 04:53:37114 if (extension->location() == Manifest::COMPONENT) {
mukaiee458c92015-01-06 01:30:33115 const extensions::ComponentExtensionResourceManager* manager =
116 extensions::ExtensionsBrowserClient::Get()
117 ->GetComponentExtensionResourceManager();
[email protected]326e6f02014-06-20 04:53:37118 if (manager && manager->IsComponentExtensionResource(
119 extension->path(), it->resource.relative_path(), &resource_id)) {
120 LoadResourceOnUIThread(resource_id, &bitmaps[i]);
121 }
[email protected]1d8e0f32014-03-17 06:39:19122 }
123 }
124 return bitmaps;
125}
126
[email protected]ec7de0c5a2012-11-16 07:40:47127} // namespace
128
129namespace extensions {
130
131////////////////////////////////////////////////////////////////////////////////
132// ImageLoader::ImageRepresentation
133
134ImageLoader::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
145ImageLoader::ImageRepresentation::~ImageRepresentation() {
146}
147
148////////////////////////////////////////////////////////////////////////////////
149// ImageLoader::LoadResult
150
151struct 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
162ImageLoader::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
171ImageLoader::LoadResult::~LoadResult() {
172}
173
[email protected]f5bb7642013-11-23 19:03:53174namespace {
175
176// Need to be after ImageRepresentation and LoadResult are defined.
177std::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]ec7de0c5a2012-11-16 07:40:47212////////////////////////////////////////////////////////////////////////////////
213// ImageLoader
214
[email protected]d77020ce2013-10-08 16:00:57215ImageLoader::ImageLoader()
216 : weak_ptr_factory_(this) {
[email protected]ec7de0c5a2012-11-16 07:40:47217}
218
219ImageLoader::~ImageLoader() {
220}
221
222// static
[email protected]472522b2013-10-25 00:41:28223ImageLoader* ImageLoader::Get(content::BrowserContext* context) {
224 return ImageLoaderFactory::GetForBrowserContext(context);
[email protected]ec7de0c5a2012-11-16 07:40:47225}
226
[email protected]f5bb7642013-11-23 19:03:53227void ImageLoader::LoadImageAsync(const Extension* extension,
228 const ExtensionResource& resource,
229 const gfx::Size& max_size,
[email protected]1d8e0f32014-03-17 06:39:19230 const ImageLoaderImageCallback& callback) {
[email protected]ec7de0c5a2012-11-16 07:40:47231 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
240void ImageLoader::LoadImagesAsync(
241 const Extension* extension,
242 const std::vector<ImageRepresentation>& info_list,
[email protected]1d8e0f32014-03-17 06:39:19243 const ImageLoaderImageCallback& callback) {
[email protected]54ee8192014-03-29 17:37:24244 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]ec7de0c5a2012-11-16 07:40:47245 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
[email protected]f5bb7642013-11-23 19:03:53246 base::PostTaskAndReplyWithResult(
247 BrowserThread::GetBlockingPool(),
[email protected]ec7de0c5a2012-11-16 07:40:47248 FROM_HERE,
[email protected]1d8e0f32014-03-17 06:39:19249 base::Bind(LoadImagesOnBlockingPool,
250 info_list,
251 LoadResourceBitmaps(extension, info_list)),
252 base::Bind(
253 &ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(), callback));
254}
255
256void ImageLoader::LoadImageFamilyAsync(
257 const extensions::Extension* extension,
258 const std::vector<ImageRepresentation>& info_list,
259 const ImageLoaderImageFamilyCallback& callback) {
[email protected]54ee8192014-03-29 17:37:24260 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]1d8e0f32014-03-17 06:39:19261 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]f5bb7642013-11-23 19:03:53270 callback));
[email protected]ec7de0c5a2012-11-16 07:40:47271}
272
[email protected]1d8e0f32014-03-17 06:39:19273void ImageLoader::ReplyBack(const ImageLoaderImageCallback& callback,
[email protected]f5bb7642013-11-23 19:03:53274 const std::vector<LoadResult>& load_result) {
[email protected]54ee8192014-03-29 17:37:24275 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]ec7de0c5a2012-11-16 07:40:47276
277 gfx::ImageSkia image_skia;
278
[email protected]f5bb7642013-11-23 19:03:53279 for (std::vector<LoadResult>::const_iterator it = load_result.begin();
280 it != load_result.end(); ++it) {
[email protected]ec7de0c5a2012-11-16 07:40:47281 const SkBitmap& bitmap = it->bitmap;
282 const ImageRepresentation& image_rep = it->image_representation;
283
284 image_skia.AddRepresentation(gfx::ImageSkiaRep(
[email protected]50b66262013-09-24 03:25:48285 bitmap,
[email protected]3ea137b2014-05-22 09:27:35286 ui::GetScaleForScaleFactor(image_rep.scale_factor)));
[email protected]ec7de0c5a2012-11-16 07:40:47287 }
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]1d8e0f32014-03-17 06:39:19298void ImageLoader::ReplyBackWithImageFamily(
299 const ImageLoaderImageFamilyCallback& callback,
300 const std::vector<LoadResult>& load_result) {
[email protected]54ee8192014-03-29 17:37:24301 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]1d8e0f32014-03-17 06:39:19302
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]3ea137b2014-05-22 09:27:35316 gfx::ImageSkiaRep(bitmap,
317 ui::GetScaleForScaleFactor(image_rep.scale_factor)));
[email protected]1d8e0f32014-03-17 06:39:19318 }
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]ec7de0c5a2012-11-16 07:40:47331} // namespace extensions