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.
5 #include "chrome/browser/extensions/image_loader.h"
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"
30 #if defined(OS_CHROMEOS)
31 #include "ui/file_manager/file_manager_resource_util.h"
35 #include "ui/keyboard/keyboard_util.h"
38 using content::BrowserThread
;
39 using extensions::Extension
;
40 using extensions::ImageLoader
;
41 using extensions::Manifest
;
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
:
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
,
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());
77 void LoadResourceOnUIThread(int resource_id
, SkBitmap
* bitmap
) {
78 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
81 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
));
82 image
.MakeThreadSafe();
83 *bitmap
= *image
.bitmap();
86 void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation
& image_info
,
88 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
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
)) {
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
);
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
,
114 for (size_t i
= 0; i
< size
; ++i
) {
115 base::FilePath resource_path
= base::FilePath().AppendASCII(
117 resource_path
= resource_path
.NormalizePathSeparators();
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
;
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());
134 for (std::vector
<ImageLoader::ImageRepresentation
>::const_iterator
135 it
= info_list
.begin();
136 it
!= info_list
.end();
138 DCHECK(it
->resource
.relative_path().empty() ||
139 extension
->path() == it
->resource
.extension_root());
142 if (extension
->location() == Manifest::COMPONENT
&&
143 ImageLoader::IsComponentExtensionResource(
144 extension
->path(), it
->resource
.relative_path(), &resource_id
)) {
145 LoadResourceOnUIThread(resource_id
, &bitmaps
[i
]);
153 namespace extensions
{
155 ////////////////////////////////////////////////////////////////////////////////
156 // ImageLoader::ImageRepresentation
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
) {
169 ImageLoader::ImageRepresentation::~ImageRepresentation() {
172 ////////////////////////////////////////////////////////////////////////////////
173 // ImageLoader::LoadResult
175 struct ImageLoader::LoadResult
{
176 LoadResult(const SkBitmap
& bitmap
,
177 const gfx::Size
& original_size
,
178 const ImageRepresentation
& image_representation
);
182 gfx::Size original_size
;
183 ImageRepresentation image_representation
;
186 ImageLoader::LoadResult::LoadResult(
187 const SkBitmap
& bitmap
,
188 const gfx::Size
& original_size
,
189 const ImageLoader::ImageRepresentation
& image_representation
)
191 original_size(original_size
),
192 image_representation(image_representation
) {
195 ImageLoader::LoadResult::~LoadResult() {
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
;
207 for (size_t i
= 0; i
< info_list
.size(); ++i
) {
208 const ImageLoader::ImageRepresentation
& image
= info_list
[i
];
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())
215 if (bitmaps
[i
].isNull())
216 LoadImageOnBlockingPool(image
, &bitmap
);
220 // If the image failed to load, skip it.
221 if (bitmap
.isNull() || bitmap
.empty())
224 gfx::Size
original_size(bitmap
.width(), bitmap
.height());
225 bitmap
= ResizeIfNeeded(bitmap
, image
);
227 load_result
.push_back(
228 ImageLoader::LoadResult(bitmap
, original_size
, image
));
236 ////////////////////////////////////////////////////////////////////////////////
239 ImageLoader::ImageLoader()
240 : weak_ptr_factory_(this) {
243 ImageLoader::~ImageLoader() {
247 ImageLoader
* ImageLoader::Get(content::BrowserContext
* context
) {
248 return ImageLoaderFactory::GetForBrowserContext(context
);
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
;
257 bool ImageLoader::IsComponentExtensionResource(
258 const base::FilePath
& extension_path
,
259 const base::FilePath
& resource_path
,
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
},
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
);
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(),
298 keyboard_resource_size
);
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
)) {
309 relative_path
= relative_path
.Append(resource_path
);
310 relative_path
= relative_path
.NormalizePathSeparators();
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
;
317 return entry
!= path_to_resource_id
.Get().end();
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(
327 ImageRepresentation::RESIZE_WHEN_LARGER
,
329 ui::SCALE_FACTOR_100P
));
330 LoadImagesAsync(extension
, info_list
, callback
);
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(),
342 base::Bind(LoadImagesOnBlockingPool
,
344 LoadResourceBitmaps(extension
, info_list
)),
346 &ImageLoader::ReplyBack
, weak_ptr_factory_
.GetWeakPtr(), callback
));
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(),
358 base::Bind(LoadImagesOnBlockingPool
,
360 LoadResourceBitmaps(extension
, info_list
)),
361 base::Bind(&ImageLoader::ReplyBackWithImageFamily
,
362 weak_ptr_factory_
.GetWeakPtr(),
366 void ImageLoader::ReplyBack(const ImageLoaderImageCallback
& callback
,
367 const std::vector
<LoadResult
>& load_result
) {
368 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
370 gfx::ImageSkia image_skia
;
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
;
377 image_skia
.AddRepresentation(gfx::ImageSkiaRep(
379 ui::GetImageScale(image_rep
.scale_factor
)));
383 if (!image_skia
.isNull()) {
384 image_skia
.MakeThreadSafe();
385 image
= gfx::Image(image_skia
);
391 void ImageLoader::ReplyBackWithImageFamily(
392 const ImageLoaderImageFamilyCallback
& callback
,
393 const std::vector
<LoadResult
>& load_result
) {
394 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
396 std::map
<std::pair
<int, int>, gfx::ImageSkia
> image_skia_map
;
397 gfx::ImageFamily image_family
;
399 for (std::vector
<LoadResult
>::const_iterator it
= load_result
.begin();
400 it
!= load_result
.end();
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
, ui::GetImageScale(image_rep
.scale_factor
)));
412 for (std::map
<std::pair
<int, int>, gfx::ImageSkia
>::iterator it
=
413 image_skia_map
.begin();
414 it
!= image_skia_map
.end();
416 it
->second
.MakeThreadSafe();
417 image_family
.Add(it
->second
);
420 callback
.Run(image_family
);
423 } // namespace extensions