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/image_loader.h" | |
6 | |
7 #include <map> | |
8 #include <vector> | |
9 | |
10 #include "base/callback.h" | |
11 #include "base/compiler_specific.h" | |
12 #include "base/file_util.h" | |
13 #include "base/lazy_instance.h" | |
14 #include "base/path_service.h" | |
15 #include "base/strings/string_number_conversions.h" | |
16 #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 "content/public/browser/browser_thread.h" | |
20 #include "extensions/common/extension.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" | |
26 #include "ui/gfx/codec/png_codec.h" | |
27 #include "ui/gfx/image/image_family.h" | |
28 #include "ui/gfx/image/image_skia.h" | |
29 | |
30 #if defined(OS_CHROMEOS) | |
31 #include "ui/file_manager/file_manager_resource_util.h" | |
32 #endif | |
33 | |
34 #if defined(USE_AURA) | |
35 #include "ui/keyboard/keyboard_util.h" | |
36 #endif | |
37 | |
38 using content::BrowserThread; | |
39 using extensions::Extension; | |
40 using extensions::ImageLoader; | |
41 using extensions::Manifest; | |
42 | |
43 namespace { | |
44 | |
45 bool ShouldResizeImageRepresentation( | |
46 ImageLoader::ImageRepresentation::ResizeCondition resize_method, | |
47 const gfx::Size& decoded_size, | |
48 const gfx::Size& desired_size) { | |
49 switch (resize_method) { | |
50 case ImageLoader::ImageRepresentation::ALWAYS_RESIZE: | |
51 return decoded_size != desired_size; | |
52 case ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER: | |
53 return decoded_size.width() > desired_size.width() || | |
54 decoded_size.height() > desired_size.height(); | |
55 case ImageLoader::ImageRepresentation::NEVER_RESIZE: | |
56 return false; | |
57 default: | |
58 NOTREACHED(); | |
59 return false; | |
60 } | |
61 } | |
62 | |
63 SkBitmap ResizeIfNeeded(const SkBitmap& bitmap, | |
64 const ImageLoader::ImageRepresentation& image_info) { | |
65 gfx::Size original_size(bitmap.width(), bitmap.height()); | |
66 if (ShouldResizeImageRepresentation(image_info.resize_condition, | |
67 original_size, | |
68 image_info.desired_size)) { | |
69 return skia::ImageOperations::Resize( | |
70 bitmap, skia::ImageOperations::RESIZE_LANCZOS3, | |
71 image_info.desired_size.width(), image_info.desired_size.height()); | |
72 } | |
73 | |
74 return bitmap; | |
75 } | |
76 | |
77 void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) { | |
78 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
79 | |
80 gfx::ImageSkia image( | |
81 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id)); | |
82 image.MakeThreadSafe(); | |
83 *bitmap = *image.bitmap(); | |
84 } | |
85 | |
86 void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation& image_info, | |
87 SkBitmap* bitmap) { | |
88 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
89 | |
90 // Read the file from disk. | |
91 std::string file_contents; | |
92 base::FilePath path = image_info.resource.GetFilePath(); | |
93 if (path.empty() || !base::ReadFileToString(path, &file_contents)) { | |
94 return; | |
95 } | |
96 | |
97 const unsigned char* data = | |
98 reinterpret_cast<const unsigned char*>(file_contents.data()); | |
99 // Note: This class only decodes bitmaps from extension resources. Chrome | |
100 // doesn't (for security reasons) directly load extension resources provided | |
101 // by the extension author, but instead decodes them in a separate | |
102 // locked-down utility process. Only if the decoding succeeds is the image | |
103 // saved from memory to disk and subsequently used in the Chrome UI. | |
104 // Chrome is therefore decoding bitmaps here that were generated by Chrome. | |
105 gfx::PNGCodec::Decode(data, file_contents.length(), bitmap); | |
106 } | |
107 | |
108 // Add the resources from |entries| (there are |size| of them) to | |
109 // |path_to_resource_id| after normalizing separators. | |
110 void AddComponentResourceEntries( | |
111 std::map<base::FilePath, int>* path_to_resource_id, | |
112 const GritResourceMap* entries, | |
113 size_t size) { | |
114 for (size_t i = 0; i < size; ++i) { | |
115 base::FilePath resource_path = base::FilePath().AppendASCII( | |
116 entries[i].name); | |
117 resource_path = resource_path.NormalizePathSeparators(); | |
118 | |
119 DCHECK(path_to_resource_id->find(resource_path) == | |
120 path_to_resource_id->end()); | |
121 (*path_to_resource_id)[resource_path] = entries[i].value; | |
122 } | |
123 } | |
124 | |
125 std::vector<SkBitmap> LoadResourceBitmaps( | |
126 const Extension* extension, | |
127 const std::vector<ImageLoader::ImageRepresentation>& info_list) { | |
128 // Loading resources has to happen on the UI thread. So do this first, and | |
129 // pass the rest of the work off as a blocking pool task. | |
130 std::vector<SkBitmap> bitmaps; | |
131 bitmaps.resize(info_list.size()); | |
132 | |
133 int i = 0; | |
134 for (std::vector<ImageLoader::ImageRepresentation>::const_iterator | |
135 it = info_list.begin(); | |
136 it != info_list.end(); | |
137 ++it, ++i) { | |
138 DCHECK(it->resource.relative_path().empty() || | |
139 extension->path() == it->resource.extension_root()); | |
140 | |
141 int resource_id; | |
142 if (extension->location() == Manifest::COMPONENT && | |
143 ImageLoader::IsComponentExtensionResource( | |
144 extension->path(), it->resource.relative_path(), &resource_id)) { | |
145 LoadResourceOnUIThread(resource_id, &bitmaps[i]); | |
146 } | |
147 } | |
148 return bitmaps; | |
149 } | |
150 | |
151 } // namespace | |
152 | |
153 namespace extensions { | |
154 | |
155 //////////////////////////////////////////////////////////////////////////////// | |
156 // ImageLoader::ImageRepresentation | |
157 | |
158 ImageLoader::ImageRepresentation::ImageRepresentation( | |
159 const ExtensionResource& resource, | |
160 ResizeCondition resize_condition, | |
161 const gfx::Size& desired_size, | |
162 ui::ScaleFactor scale_factor) | |
163 : resource(resource), | |
164 resize_condition(resize_condition), | |
165 desired_size(desired_size), | |
166 scale_factor(scale_factor) { | |
167 } | |
168 | |
169 ImageLoader::ImageRepresentation::~ImageRepresentation() { | |
170 } | |
171 | |
172 //////////////////////////////////////////////////////////////////////////////// | |
173 // ImageLoader::LoadResult | |
174 | |
175 struct ImageLoader::LoadResult { | |
176 LoadResult(const SkBitmap& bitmap, | |
177 const gfx::Size& original_size, | |
178 const ImageRepresentation& image_representation); | |
179 ~LoadResult(); | |
180 | |
181 SkBitmap bitmap; | |
182 gfx::Size original_size; | |
183 ImageRepresentation image_representation; | |
184 }; | |
185 | |
186 ImageLoader::LoadResult::LoadResult( | |
187 const SkBitmap& bitmap, | |
188 const gfx::Size& original_size, | |
189 const ImageLoader::ImageRepresentation& image_representation) | |
190 : bitmap(bitmap), | |
191 original_size(original_size), | |
192 image_representation(image_representation) { | |
193 } | |
194 | |
195 ImageLoader::LoadResult::~LoadResult() { | |
196 } | |
197 | |
198 namespace { | |
199 | |
200 // Need to be after ImageRepresentation and LoadResult are defined. | |
201 std::vector<ImageLoader::LoadResult> LoadImagesOnBlockingPool( | |
202 const std::vector<ImageLoader::ImageRepresentation>& info_list, | |
203 const std::vector<SkBitmap>& bitmaps) { | |
204 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
205 std::vector<ImageLoader::LoadResult> load_result; | |
206 | |
207 for (size_t i = 0; i < info_list.size(); ++i) { | |
208 const ImageLoader::ImageRepresentation& image = info_list[i]; | |
209 | |
210 // If we don't have a path there isn't anything we can do, just skip it. | |
211 if (image.resource.relative_path().empty()) | |
212 continue; | |
213 | |
214 SkBitmap bitmap; | |
215 if (bitmaps[i].isNull()) | |
216 LoadImageOnBlockingPool(image, &bitmap); | |
217 else | |
218 bitmap = bitmaps[i]; | |
219 | |
220 // If the image failed to load, skip it. | |
221 if (bitmap.isNull() || bitmap.empty()) | |
222 continue; | |
223 | |
224 gfx::Size original_size(bitmap.width(), bitmap.height()); | |
225 bitmap = ResizeIfNeeded(bitmap, image); | |
226 | |
227 load_result.push_back( | |
228 ImageLoader::LoadResult(bitmap, original_size, image)); | |
229 } | |
230 | |
231 return load_result; | |
232 } | |
233 | |
234 } // namespace | |
235 | |
236 //////////////////////////////////////////////////////////////////////////////// | |
237 // ImageLoader | |
238 | |
239 ImageLoader::ImageLoader() | |
240 : weak_ptr_factory_(this) { | |
241 } | |
242 | |
243 ImageLoader::~ImageLoader() { | |
244 } | |
245 | |
246 // static | |
247 ImageLoader* ImageLoader::Get(content::BrowserContext* context) { | |
248 return ImageLoaderFactory::GetForBrowserContext(context); | |
249 } | |
250 | |
251 // A map from a resource path to the resource ID. Used only by | |
252 // IsComponentExtensionResource below. | |
253 static base::LazyInstance<std::map<base::FilePath, int> > path_to_resource_id = | |
254 LAZY_INSTANCE_INITIALIZER; | |
255 | |
256 // static | |
257 bool ImageLoader::IsComponentExtensionResource( | |
258 const base::FilePath& extension_path, | |
259 const base::FilePath& resource_path, | |
260 int* resource_id) { | |
261 static const GritResourceMap kExtraComponentExtensionResources[] = { | |
262 {"web_store/webstore_icon_128.png", IDR_WEBSTORE_ICON}, | |
263 {"web_store/webstore_icon_16.png", IDR_WEBSTORE_ICON_16}, | |
264 {"chrome_app/product_logo_128.png", IDR_PRODUCT_LOGO_128}, | |
265 {"chrome_app/product_logo_16.png", IDR_PRODUCT_LOGO_16}, | |
266 #if defined(ENABLE_SETTINGS_APP) | |
267 {"settings_app/settings_app_icon_128.png", IDR_SETTINGS_APP_ICON_128}, | |
268 {"settings_app/settings_app_icon_16.png", IDR_SETTINGS_APP_ICON_16}, | |
269 {"settings_app/settings_app_icon_32.png", IDR_SETTINGS_APP_ICON_32}, | |
270 {"settings_app/settings_app_icon_48.png", IDR_SETTINGS_APP_ICON_48}, | |
271 #endif | |
272 }; | |
273 | |
274 if (path_to_resource_id.Get().empty()) { | |
275 AddComponentResourceEntries( | |
276 path_to_resource_id.Pointer(), | |
277 kComponentExtensionResources, | |
278 kComponentExtensionResourcesSize); | |
279 AddComponentResourceEntries( | |
280 path_to_resource_id.Pointer(), | |
281 kExtraComponentExtensionResources, | |
282 arraysize(kExtraComponentExtensionResources)); | |
283 #if defined(OS_CHROMEOS) | |
284 size_t file_manager_resource_size; | |
285 const GritResourceMap* file_manager_resources = | |
286 file_manager::GetFileManagerResources(&file_manager_resource_size); | |
287 AddComponentResourceEntries( | |
288 path_to_resource_id.Pointer(), | |
289 file_manager_resources, | |
290 file_manager_resource_size); | |
291 | |
292 size_t keyboard_resource_size; | |
293 const GritResourceMap* keyboard_resources = | |
294 keyboard::GetKeyboardExtensionResources(&keyboard_resource_size); | |
295 AddComponentResourceEntries( | |
296 path_to_resource_id.Pointer(), | |
297 keyboard_resources, | |
298 keyboard_resource_size); | |
299 #endif | |
300 } | |
301 | |
302 base::FilePath directory_path = extension_path; | |
303 base::FilePath resources_dir; | |
304 base::FilePath relative_path; | |
305 if (!PathService::Get(chrome::DIR_RESOURCES, &resources_dir) || | |
306 !resources_dir.AppendRelativePath(directory_path, &relative_path)) { | |
307 return false; | |
308 } | |
309 relative_path = relative_path.Append(resource_path); | |
310 relative_path = relative_path.NormalizePathSeparators(); | |
311 | |
312 std::map<base::FilePath, int>::const_iterator entry = | |
313 path_to_resource_id.Get().find(relative_path); | |
314 if (entry != path_to_resource_id.Get().end()) | |
315 *resource_id = entry->second; | |
316 | |
317 return entry != path_to_resource_id.Get().end(); | |
318 } | |
319 | |
320 void ImageLoader::LoadImageAsync(const Extension* extension, | |
321 const ExtensionResource& resource, | |
322 const gfx::Size& max_size, | |
323 const ImageLoaderImageCallback& callback) { | |
324 std::vector<ImageRepresentation> info_list; | |
325 info_list.push_back(ImageRepresentation( | |
326 resource, | |
327 ImageRepresentation::RESIZE_WHEN_LARGER, | |
328 max_size, | |
329 ui::SCALE_FACTOR_100P)); | |
330 LoadImagesAsync(extension, info_list, callback); | |
331 } | |
332 | |
333 void ImageLoader::LoadImagesAsync( | |
334 const Extension* extension, | |
335 const std::vector<ImageRepresentation>& info_list, | |
336 const ImageLoaderImageCallback& callback) { | |
337 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
338 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
339 base::PostTaskAndReplyWithResult( | |
340 BrowserThread::GetBlockingPool(), | |
341 FROM_HERE, | |
342 base::Bind(LoadImagesOnBlockingPool, | |
343 info_list, | |
344 LoadResourceBitmaps(extension, info_list)), | |
345 base::Bind( | |
346 &ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(), callback)); | |
347 } | |
348 | |
349 void ImageLoader::LoadImageFamilyAsync( | |
350 const extensions::Extension* extension, | |
351 const std::vector<ImageRepresentation>& info_list, | |
352 const ImageLoaderImageFamilyCallback& callback) { | |
353 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
354 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
355 base::PostTaskAndReplyWithResult( | |
356 BrowserThread::GetBlockingPool(), | |
357 FROM_HERE, | |
358 base::Bind(LoadImagesOnBlockingPool, | |
359 info_list, | |
360 LoadResourceBitmaps(extension, info_list)), | |
361 base::Bind(&ImageLoader::ReplyBackWithImageFamily, | |
362 weak_ptr_factory_.GetWeakPtr(), | |
363 callback)); | |
364 } | |
365 | |
366 void ImageLoader::ReplyBack(const ImageLoaderImageCallback& callback, | |
367 const std::vector<LoadResult>& load_result) { | |
368 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
369 | |
370 gfx::ImageSkia image_skia; | |
371 | |
372 for (std::vector<LoadResult>::const_iterator it = load_result.begin(); | |
373 it != load_result.end(); ++it) { | |
374 const SkBitmap& bitmap = it->bitmap; | |
375 const ImageRepresentation& image_rep = it->image_representation; | |
376 | |
377 image_skia.AddRepresentation(gfx::ImageSkiaRep( | |
378 bitmap, | |
379 ui::GetScaleForScaleFactor(image_rep.scale_factor))); | |
380 } | |
381 | |
382 gfx::Image image; | |
383 if (!image_skia.isNull()) { | |
384 image_skia.MakeThreadSafe(); | |
385 image = gfx::Image(image_skia); | |
386 } | |
387 | |
388 callback.Run(image); | |
389 } | |
390 | |
391 void ImageLoader::ReplyBackWithImageFamily( | |
392 const ImageLoaderImageFamilyCallback& callback, | |
393 const std::vector<LoadResult>& load_result) { | |
394 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
395 | |
396 std::map<std::pair<int, int>, gfx::ImageSkia> image_skia_map; | |
397 gfx::ImageFamily image_family; | |
398 | |
399 for (std::vector<LoadResult>::const_iterator it = load_result.begin(); | |
400 it != load_result.end(); | |
401 ++it) { | |
402 const SkBitmap& bitmap = it->bitmap; | |
403 const ImageRepresentation& image_rep = it->image_representation; | |
404 const std::pair<int, int> key = std::make_pair( | |
405 image_rep.desired_size.width(), image_rep.desired_size.height()); | |
406 // Create a new ImageSkia for this width/height, or add a representation to | |
407 // an existing ImageSkia with the same width/height. | |
408 image_skia_map[key].AddRepresentation( | |
409 gfx::ImageSkiaRep(bitmap, | |
410 ui::GetScaleForScaleFactor(image_rep.scale_factor))); | |
411 } | |
412 | |
413 for (std::map<std::pair<int, int>, gfx::ImageSkia>::iterator it = | |
414 image_skia_map.begin(); | |
415 it != image_skia_map.end(); | |
416 ++it) { | |
417 it->second.MakeThreadSafe(); | |
418 image_family.Add(it->second); | |
419 } | |
420 | |
421 callback.Run(image_family); | |
422 } | |
423 | |
424 } // namespace extensions | |
OLD | NEW |