blob: 2c8f77cb4dad2d0d140c0d5116cb523ea9018799 [file] [log] [blame]
[email protected]ec7de0c5a2012-11-16 07:40:471// 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/image_loader.h"
6
[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"
[email protected]ec7de0c5a2012-11-16 07:40:4712#include "base/file_util.h"
[email protected]95fd2552013-07-04 21:19:2413#include "base/lazy_instance.h"
[email protected]ec7de0c5a2012-11-16 07:40:4714#include "base/path_service.h"
[email protected]3ea1b182013-02-08 22:38:4115#include "base/strings/string_number_conversions.h"
[email protected]ec7de0c5a2012-11-16 07:40:4716#include "base/threading/sequenced_worker_pool.h"
17#include "chrome/browser/extensions/image_loader_factory.h"
18#include "chrome/common/chrome_paths.h"
19#include "chrome/common/extensions/extension.h"
20#include "content/public/browser/browser_thread.h"
21#include "grit/chrome_unscaled_resources.h"
22#include "grit/component_extension_resources_map.h"
23#include "grit/theme_resources.h"
24#include "skia/ext/image_operations.h"
25#include "ui/base/resource/resource_bundle.h"
[email protected]da87eec22013-05-14 09:25:2826#include "ui/gfx/codec/png_codec.h"
[email protected]ec7de0c5a2012-11-16 07:40:4727#include "ui/gfx/image/image_skia.h"
[email protected]ec7de0c5a2012-11-16 07:40:4728
[email protected]72600222013-04-18 11:24:0129#if defined(USE_AURA)
[email protected]95fd2552013-07-04 21:19:2430#include "ui/keyboard/keyboard_util.h"
[email protected]72600222013-04-18 11:24:0131#endif
32
[email protected]ec7de0c5a2012-11-16 07:40:4733using content::BrowserThread;
34using extensions::Extension;
35using extensions::ImageLoader;
[email protected]1d5e58b2013-01-31 08:41:4036using extensions::Manifest;
[email protected]ec7de0c5a2012-11-16 07:40:4737
38namespace {
39
40bool ShouldResizeImageRepresentation(
41 ImageLoader::ImageRepresentation::ResizeCondition resize_method,
42 const gfx::Size& decoded_size,
43 const gfx::Size& desired_size) {
44 switch (resize_method) {
45 case ImageLoader::ImageRepresentation::ALWAYS_RESIZE:
46 return decoded_size != desired_size;
47 case ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER:
48 return decoded_size.width() > desired_size.width() ||
49 decoded_size.height() > desired_size.height();
50 default:
51 NOTREACHED();
52 return false;
53 }
54}
55
56SkBitmap ResizeIfNeeded(const SkBitmap& bitmap,
57 const ImageLoader::ImageRepresentation& image_info) {
58 gfx::Size original_size(bitmap.width(), bitmap.height());
59 if (ShouldResizeImageRepresentation(image_info.resize_condition,
60 original_size,
61 image_info.desired_size)) {
62 return skia::ImageOperations::Resize(
63 bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
64 image_info.desired_size.width(), image_info.desired_size.height());
65 }
66
67 return bitmap;
68}
69
70void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
72
73 gfx::ImageSkia image(
74 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id));
75 image.MakeThreadSafe();
76 *bitmap = *image.bitmap();
77}
78
79void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation& image_info,
80 SkBitmap* bitmap) {
81 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
82
83 // Read the file from disk.
84 std::string file_contents;
[email protected]650b2d52013-02-10 03:41:4585 base::FilePath path = image_info.resource.GetFilePath();
[email protected]82f84b92013-08-30 18:23:5086 if (path.empty() || !base::ReadFileToString(path, &file_contents)) {
[email protected]ec7de0c5a2012-11-16 07:40:4787 return;
88 }
89
[email protected]ec7de0c5a2012-11-16 07:40:4790 const unsigned char* data =
91 reinterpret_cast<const unsigned char*>(file_contents.data());
[email protected]ec7de0c5a2012-11-16 07:40:4792 // Note: This class only decodes bitmaps from extension resources. Chrome
93 // doesn't (for security reasons) directly load extension resources provided
94 // by the extension author, but instead decodes them in a separate
95 // locked-down utility process. Only if the decoding succeeds is the image
96 // saved from memory to disk and subsequently used in the Chrome UI.
97 // Chrome is therefore decoding bitmaps here that were generated by Chrome.
[email protected]da87eec22013-05-14 09:25:2898 gfx::PNGCodec::Decode(data, file_contents.length(), bitmap);
[email protected]ec7de0c5a2012-11-16 07:40:4799}
100
[email protected]95fd2552013-07-04 21:19:24101// Add the resources from |entries| (there are |size| of them) to
102// |path_to_resource_id| after normalizing separators.
103void AddComponentResourceEntries(
104 std::map<base::FilePath, int>* path_to_resource_id,
105 const GritResourceMap* entries,
106 size_t size) {
107 for (size_t i = 0; i < size; ++i) {
108 base::FilePath resource_path = base::FilePath().AppendASCII(
109 entries[i].name);
110 resource_path = resource_path.NormalizePathSeparators();
111
112 DCHECK(path_to_resource_id->find(resource_path) ==
113 path_to_resource_id->end());
114 (*path_to_resource_id)[resource_path] = entries[i].value;
115 }
116}
117
[email protected]ec7de0c5a2012-11-16 07:40:47118} // namespace
119
120namespace extensions {
121
122////////////////////////////////////////////////////////////////////////////////
123// ImageLoader::ImageRepresentation
124
125ImageLoader::ImageRepresentation::ImageRepresentation(
126 const ExtensionResource& resource,
127 ResizeCondition resize_condition,
128 const gfx::Size& desired_size,
129 ui::ScaleFactor scale_factor)
130 : resource(resource),
131 resize_condition(resize_condition),
132 desired_size(desired_size),
133 scale_factor(scale_factor) {
134}
135
136ImageLoader::ImageRepresentation::~ImageRepresentation() {
137}
138
139////////////////////////////////////////////////////////////////////////////////
140// ImageLoader::LoadResult
141
142struct ImageLoader::LoadResult {
143 LoadResult(const SkBitmap& bitmap,
144 const gfx::Size& original_size,
145 const ImageRepresentation& image_representation);
146 ~LoadResult();
147
148 SkBitmap bitmap;
149 gfx::Size original_size;
150 ImageRepresentation image_representation;
151};
152
153ImageLoader::LoadResult::LoadResult(
154 const SkBitmap& bitmap,
155 const gfx::Size& original_size,
156 const ImageLoader::ImageRepresentation& image_representation)
157 : bitmap(bitmap),
158 original_size(original_size),
159 image_representation(image_representation) {
160}
161
162ImageLoader::LoadResult::~LoadResult() {
163}
164
165////////////////////////////////////////////////////////////////////////////////
166// ImageLoader
167
[email protected]c8b8587b2012-11-21 23:23:32168ImageLoader::ImageLoader()
[email protected]9c009092013-05-01 03:14:09169 : weak_ptr_factory_(this) {
[email protected]ec7de0c5a2012-11-16 07:40:47170}
171
172ImageLoader::~ImageLoader() {
173}
174
175// static
176ImageLoader* ImageLoader::Get(Profile* profile) {
177 return ImageLoaderFactory::GetForProfile(profile);
178}
179
[email protected]95fd2552013-07-04 21:19:24180// A map from a resource path to the resource ID. Used only by
181// IsComponentExtensionResource below.
182static base::LazyInstance<std::map<base::FilePath, int> > path_to_resource_id =
183 LAZY_INSTANCE_INITIALIZER;
184
[email protected]ec7de0c5a2012-11-16 07:40:47185// static
[email protected]93ac047a2012-12-13 02:53:49186bool ImageLoader::IsComponentExtensionResource(
[email protected]650b2d52013-02-10 03:41:45187 const base::FilePath& extension_path,
188 const base::FilePath& resource_path,
[email protected]93ac047a2012-12-13 02:53:49189 int* resource_id) {
[email protected]ec7de0c5a2012-11-16 07:40:47190 static const GritResourceMap kExtraComponentExtensionResources[] = {
191 {"web_store/webstore_icon_128.png", IDR_WEBSTORE_ICON},
192 {"web_store/webstore_icon_16.png", IDR_WEBSTORE_ICON_16},
193 {"chrome_app/product_logo_128.png", IDR_PRODUCT_LOGO_128},
194 {"chrome_app/product_logo_16.png", IDR_PRODUCT_LOGO_16},
[email protected]56c43e82012-12-13 09:29:40195#if defined(ENABLE_SETTINGS_APP)
[email protected]ec7de0c5a2012-11-16 07:40:47196 {"settings_app/settings_app_icon_128.png", IDR_SETTINGS_APP_ICON_128},
197 {"settings_app/settings_app_icon_16.png", IDR_SETTINGS_APP_ICON_16},
[email protected]56c43e82012-12-13 09:29:40198 {"settings_app/settings_app_icon_32.png", IDR_SETTINGS_APP_ICON_32},
199 {"settings_app/settings_app_icon_48.png", IDR_SETTINGS_APP_ICON_48},
200#endif
[email protected]22041a12013-07-04 19:08:34201 };
[email protected]95fd2552013-07-04 21:19:24202
203 if (path_to_resource_id.Get().empty()) {
204 AddComponentResourceEntries(
205 path_to_resource_id.Pointer(),
206 kComponentExtensionResources,
207 kComponentExtensionResourcesSize);
208 AddComponentResourceEntries(
209 path_to_resource_id.Pointer(),
210 kExtraComponentExtensionResources,
211 arraysize(kExtraComponentExtensionResources));
212#if defined(USE_AURA)
213 if (keyboard::IsKeyboardEnabled()) {
214 size_t size;
215 const GritResourceMap* keyboard_resources =
216 keyboard::GetKeyboardExtensionResources(&size);
217 AddComponentResourceEntries(
218 path_to_resource_id.Pointer(), keyboard_resources, size);
219 }
220#endif
221 }
[email protected]ec7de0c5a2012-11-16 07:40:47222
[email protected]650b2d52013-02-10 03:41:45223 base::FilePath directory_path = extension_path;
224 base::FilePath resources_dir;
225 base::FilePath relative_path;
[email protected]ec7de0c5a2012-11-16 07:40:47226 if (!PathService::Get(chrome::DIR_RESOURCES, &resources_dir) ||
227 !resources_dir.AppendRelativePath(directory_path, &relative_path)) {
228 return false;
229 }
230 relative_path = relative_path.Append(resource_path);
231 relative_path = relative_path.NormalizePathSeparators();
232
[email protected]95fd2552013-07-04 21:19:24233 std::map<base::FilePath, int>::const_iterator entry =
234 path_to_resource_id.Get().find(relative_path);
235 if (entry != path_to_resource_id.Get().end())
236 *resource_id = entry->second;
[email protected]ec7de0c5a2012-11-16 07:40:47237
[email protected]95fd2552013-07-04 21:19:24238 return entry != path_to_resource_id.Get().end();
[email protected]ec7de0c5a2012-11-16 07:40:47239}
240
241void ImageLoader::LoadImageAsync(
242 const Extension* extension,
243 const ExtensionResource& resource,
244 const gfx::Size& max_size,
245 const base::Callback<void(const gfx::Image&)>& callback) {
246 std::vector<ImageRepresentation> info_list;
247 info_list.push_back(ImageRepresentation(
248 resource,
249 ImageRepresentation::RESIZE_WHEN_LARGER,
250 max_size,
251 ui::SCALE_FACTOR_100P));
252 LoadImagesAsync(extension, info_list, callback);
253}
254
255void ImageLoader::LoadImagesAsync(
256 const Extension* extension,
257 const std::vector<ImageRepresentation>& info_list,
258 const base::Callback<void(const gfx::Image&)>& callback) {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
260
261 // Loading an image from the cache and loading resources have to happen
262 // on the UI thread. So do those two things first, and pass the rest of the
263 // work of as a blocking pool task.
264
265 std::vector<SkBitmap> bitmaps;
266 bitmaps.resize(info_list.size());
267
268 int i = 0;
269 for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin();
270 it != info_list.end(); ++it, ++i) {
271 DCHECK(it->resource.relative_path().empty() ||
272 extension->path() == it->resource.extension_root());
273
274 int resource_id;
[email protected]1d5e58b2013-01-31 08:41:40275 if (extension->location() == Manifest::COMPONENT &&
[email protected]93ac047a2012-12-13 02:53:49276 IsComponentExtensionResource(extension->path(),
277 it->resource.relative_path(),
[email protected]ec7de0c5a2012-11-16 07:40:47278 &resource_id)) {
279 LoadResourceOnUIThread(resource_id, &bitmaps[i]);
280 }
281 }
282
283 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
[email protected]c8b8587b2012-11-21 23:23:32284 std::vector<LoadResult>* load_result = new std::vector<LoadResult>;
285 BrowserThread::PostBlockingPoolTaskAndReply(
[email protected]ec7de0c5a2012-11-16 07:40:47286 FROM_HERE,
[email protected]c8b8587b2012-11-21 23:23:32287 base::Bind(LoadImagesOnBlockingPool, info_list, bitmaps, load_result),
288 base::Bind(&ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(),
289 base::Owned(load_result), callback));
[email protected]ec7de0c5a2012-11-16 07:40:47290}
291
[email protected]c8b8587b2012-11-21 23:23:32292// static
[email protected]ec7de0c5a2012-11-16 07:40:47293void ImageLoader::LoadImagesOnBlockingPool(
294 const std::vector<ImageRepresentation>& info_list,
295 const std::vector<SkBitmap>& bitmaps,
[email protected]c8b8587b2012-11-21 23:23:32296 std::vector<LoadResult>* load_result) {
[email protected]ec7de0c5a2012-11-16 07:40:47297 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
298
[email protected]ec7de0c5a2012-11-16 07:40:47299 int i = 0;
300 for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin();
301 it != info_list.end(); ++it, ++i) {
302 // If we don't have a path there isn't anything we can do, just skip it.
303 if (it->resource.relative_path().empty())
304 continue;
305
306 SkBitmap bitmap;
307 if (!bitmaps[i].isNull()) {
308 bitmap = bitmaps[i];
309 } else {
310 LoadImageOnBlockingPool(*it, &bitmap);
311 }
312
313 // If the image failed to load, skip it.
314 if (bitmap.isNull() || bitmap.empty())
315 continue;
316
317 gfx::Size original_size(bitmap.width(), bitmap.height());
318 bitmap = ResizeIfNeeded(bitmap, *it);
319
[email protected]c8b8587b2012-11-21 23:23:32320 load_result->push_back(LoadResult(bitmap, original_size, *it));
[email protected]ec7de0c5a2012-11-16 07:40:47321 }
[email protected]ec7de0c5a2012-11-16 07:40:47322}
323
324void ImageLoader::ReplyBack(
[email protected]c8b8587b2012-11-21 23:23:32325 const std::vector<LoadResult>* load_result,
[email protected]ec7de0c5a2012-11-16 07:40:47326 const base::Callback<void(const gfx::Image&)>& callback) {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328
329 gfx::ImageSkia image_skia;
330
[email protected]c8b8587b2012-11-21 23:23:32331 for (std::vector<LoadResult>::const_iterator it = load_result->begin();
332 it != load_result->end(); ++it) {
[email protected]ec7de0c5a2012-11-16 07:40:47333 const SkBitmap& bitmap = it->bitmap;
334 const ImageRepresentation& image_rep = it->image_representation;
335
336 image_skia.AddRepresentation(gfx::ImageSkiaRep(
[email protected]50b66262013-09-24 03:25:48337 bitmap,
338 ui::GetImageScale(image_rep.scale_factor)));
[email protected]ec7de0c5a2012-11-16 07:40:47339 }
340
341 gfx::Image image;
342 if (!image_skia.isNull()) {
343 image_skia.MakeThreadSafe();
344 image = gfx::Image(image_skia);
345 }
346
347 callback.Run(image);
348}
349
350} // namespace extensions