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_skia.h"
30 #include "ui/keyboard/keyboard_util.h"
33 using content::BrowserThread
;
34 using extensions::Extension
;
35 using extensions::ImageLoader
;
36 using extensions::Manifest
;
40 bool 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();
56 SkBitmap
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
,
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());
70 void LoadResourceOnUIThread(int resource_id
, SkBitmap
* bitmap
) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
74 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
));
75 image
.MakeThreadSafe();
76 *bitmap
= *image
.bitmap();
79 void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation
& image_info
,
81 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
83 // Read the file from disk.
84 std::string file_contents
;
85 base::FilePath path
= image_info
.resource
.GetFilePath();
86 if (path
.empty() || !base::ReadFileToString(path
, &file_contents
)) {
90 const unsigned char* data
=
91 reinterpret_cast<const unsigned char*>(file_contents
.data());
92 // 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.
98 gfx::PNGCodec::Decode(data
, file_contents
.length(), bitmap
);
101 // Add the resources from |entries| (there are |size| of them) to
102 // |path_to_resource_id| after normalizing separators.
103 void AddComponentResourceEntries(
104 std::map
<base::FilePath
, int>* path_to_resource_id
,
105 const GritResourceMap
* entries
,
107 for (size_t i
= 0; i
< size
; ++i
) {
108 base::FilePath resource_path
= base::FilePath().AppendASCII(
110 resource_path
= resource_path
.NormalizePathSeparators();
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
;
120 namespace extensions
{
122 ////////////////////////////////////////////////////////////////////////////////
123 // ImageLoader::ImageRepresentation
125 ImageLoader::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
) {
136 ImageLoader::ImageRepresentation::~ImageRepresentation() {
139 ////////////////////////////////////////////////////////////////////////////////
140 // ImageLoader::LoadResult
142 struct ImageLoader::LoadResult
{
143 LoadResult(const SkBitmap
& bitmap
,
144 const gfx::Size
& original_size
,
145 const ImageRepresentation
& image_representation
);
149 gfx::Size original_size
;
150 ImageRepresentation image_representation
;
153 ImageLoader::LoadResult::LoadResult(
154 const SkBitmap
& bitmap
,
155 const gfx::Size
& original_size
,
156 const ImageLoader::ImageRepresentation
& image_representation
)
158 original_size(original_size
),
159 image_representation(image_representation
) {
162 ImageLoader::LoadResult::~LoadResult() {
167 // Need to be after ImageRepresentation and LoadResult are defined.
168 std::vector
<ImageLoader::LoadResult
> LoadImagesOnBlockingPool(
169 const std::vector
<ImageLoader::ImageRepresentation
>& info_list
,
170 const std::vector
<SkBitmap
>& bitmaps
) {
171 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
172 std::vector
<ImageLoader::LoadResult
> load_result
;
174 for (size_t i
= 0; i
< info_list
.size(); ++i
) {
175 const ImageLoader::ImageRepresentation
& image
= info_list
[i
];
177 // If we don't have a path there isn't anything we can do, just skip it.
178 if (image
.resource
.relative_path().empty())
182 if (bitmaps
[i
].isNull())
183 LoadImageOnBlockingPool(image
, &bitmap
);
187 // If the image failed to load, skip it.
188 if (bitmap
.isNull() || bitmap
.empty())
191 gfx::Size
original_size(bitmap
.width(), bitmap
.height());
192 bitmap
= ResizeIfNeeded(bitmap
, image
);
194 load_result
.push_back(
195 ImageLoader::LoadResult(bitmap
, original_size
, image
));
203 ////////////////////////////////////////////////////////////////////////////////
206 ImageLoader::ImageLoader()
207 : weak_ptr_factory_(this) {
210 ImageLoader::~ImageLoader() {
214 ImageLoader
* ImageLoader::Get(content::BrowserContext
* context
) {
215 return ImageLoaderFactory::GetForBrowserContext(context
);
218 // A map from a resource path to the resource ID. Used only by
219 // IsComponentExtensionResource below.
220 static base::LazyInstance
<std::map
<base::FilePath
, int> > path_to_resource_id
=
221 LAZY_INSTANCE_INITIALIZER
;
224 bool ImageLoader::IsComponentExtensionResource(
225 const base::FilePath
& extension_path
,
226 const base::FilePath
& resource_path
,
228 static const GritResourceMap kExtraComponentExtensionResources
[] = {
229 {"web_store/webstore_icon_128.png", IDR_WEBSTORE_ICON
},
230 {"web_store/webstore_icon_16.png", IDR_WEBSTORE_ICON_16
},
231 {"chrome_app/product_logo_128.png", IDR_PRODUCT_LOGO_128
},
232 {"chrome_app/product_logo_16.png", IDR_PRODUCT_LOGO_16
},
233 #if defined(ENABLE_SETTINGS_APP)
234 {"settings_app/settings_app_icon_128.png", IDR_SETTINGS_APP_ICON_128
},
235 {"settings_app/settings_app_icon_16.png", IDR_SETTINGS_APP_ICON_16
},
236 {"settings_app/settings_app_icon_32.png", IDR_SETTINGS_APP_ICON_32
},
237 {"settings_app/settings_app_icon_48.png", IDR_SETTINGS_APP_ICON_48
},
241 if (path_to_resource_id
.Get().empty()) {
242 AddComponentResourceEntries(
243 path_to_resource_id
.Pointer(),
244 kComponentExtensionResources
,
245 kComponentExtensionResourcesSize
);
246 AddComponentResourceEntries(
247 path_to_resource_id
.Pointer(),
248 kExtraComponentExtensionResources
,
249 arraysize(kExtraComponentExtensionResources
));
250 #if defined(USE_AURA)
251 if (keyboard::IsKeyboardEnabled()) {
253 const GritResourceMap
* keyboard_resources
=
254 keyboard::GetKeyboardExtensionResources(&size
);
255 AddComponentResourceEntries(
256 path_to_resource_id
.Pointer(), keyboard_resources
, size
);
261 base::FilePath directory_path
= extension_path
;
262 base::FilePath resources_dir
;
263 base::FilePath relative_path
;
264 if (!PathService::Get(chrome::DIR_RESOURCES
, &resources_dir
) ||
265 !resources_dir
.AppendRelativePath(directory_path
, &relative_path
)) {
268 relative_path
= relative_path
.Append(resource_path
);
269 relative_path
= relative_path
.NormalizePathSeparators();
271 std::map
<base::FilePath
, int>::const_iterator entry
=
272 path_to_resource_id
.Get().find(relative_path
);
273 if (entry
!= path_to_resource_id
.Get().end())
274 *resource_id
= entry
->second
;
276 return entry
!= path_to_resource_id
.Get().end();
279 void ImageLoader::LoadImageAsync(const Extension
* extension
,
280 const ExtensionResource
& resource
,
281 const gfx::Size
& max_size
,
282 const ImageLoaderCallback
& callback
) {
283 std::vector
<ImageRepresentation
> info_list
;
284 info_list
.push_back(ImageRepresentation(
286 ImageRepresentation::RESIZE_WHEN_LARGER
,
288 ui::SCALE_FACTOR_100P
));
289 LoadImagesAsync(extension
, info_list
, callback
);
292 void ImageLoader::LoadImagesAsync(
293 const Extension
* extension
,
294 const std::vector
<ImageRepresentation
>& info_list
,
295 const ImageLoaderCallback
& callback
) {
296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
298 // Loading an image from the cache and loading resources have to happen
299 // on the UI thread. So do those two things first, and pass the rest of the
300 // work of as a blocking pool task.
302 std::vector
<SkBitmap
> bitmaps
;
303 bitmaps
.resize(info_list
.size());
306 for (std::vector
<ImageRepresentation
>::const_iterator it
= info_list
.begin();
307 it
!= info_list
.end(); ++it
, ++i
) {
308 DCHECK(it
->resource
.relative_path().empty() ||
309 extension
->path() == it
->resource
.extension_root());
312 if (extension
->location() == Manifest::COMPONENT
&&
313 IsComponentExtensionResource(extension
->path(),
314 it
->resource
.relative_path(),
316 LoadResourceOnUIThread(resource_id
, &bitmaps
[i
]);
317 if (bitmaps
[i
].isNull()) {
318 // bshe's log for http://crbug.com/314872
319 LOG(ERROR
) << "Component extension icon for " << extension
->name()
321 LOG(ERROR
) << "Extension icon resource id = " << resource_id
322 << "; desired_size = " << it
->desired_size
.ToString()
323 << "; scale_factor = " << it
->scale_factor
;
328 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
329 base::PostTaskAndReplyWithResult(
330 BrowserThread::GetBlockingPool(),
332 base::Bind(LoadImagesOnBlockingPool
, info_list
, bitmaps
),
333 base::Bind(&ImageLoader::ReplyBack
, weak_ptr_factory_
.GetWeakPtr(),
337 void ImageLoader::ReplyBack(const ImageLoaderCallback
& callback
,
338 const std::vector
<LoadResult
>& load_result
) {
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
341 gfx::ImageSkia image_skia
;
343 for (std::vector
<LoadResult
>::const_iterator it
= load_result
.begin();
344 it
!= load_result
.end(); ++it
) {
345 const SkBitmap
& bitmap
= it
->bitmap
;
346 const ImageRepresentation
& image_rep
= it
->image_representation
;
348 image_skia
.AddRepresentation(gfx::ImageSkiaRep(
350 ui::GetImageScale(image_rep
.scale_factor
)));
354 if (!image_skia
.isNull()) {
355 image_skia
.MakeThreadSafe();
356 image
= gfx::Image(image_skia
);
362 } // namespace extensions