1 // Copyright 2014 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 "extensions/browser/image_loader.h"
10 #include "base/callback.h"
11 #include "base/compiler_specific.h"
12 #include "base/files/file_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "extensions/browser/component_extension_resource_manager.h"
17 #include "extensions/browser/extensions_browser_client.h"
18 #include "extensions/browser/image_loader_factory.h"
19 #include "extensions/common/extension.h"
20 #include "skia/ext/image_operations.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/gfx/codec/png_codec.h"
23 #include "ui/gfx/image/image_family.h"
24 #include "ui/gfx/image/image_skia.h"
26 using content::BrowserThread
;
27 using extensions::Extension
;
28 using extensions::ExtensionsBrowserClient
;
29 using extensions::ImageLoader
;
30 using extensions::Manifest
;
34 bool 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();
44 case ImageLoader::ImageRepresentation::NEVER_RESIZE
:
52 SkBitmap
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
,
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());
66 void LoadResourceOnUIThread(int resource_id
, SkBitmap
* bitmap
) {
67 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
70 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
));
71 image
.MakeThreadSafe();
72 *bitmap
= *image
.bitmap();
75 void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation
& image_info
,
77 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
79 // Read the file from disk.
80 std::string file_contents
;
81 base::FilePath path
= image_info
.resource
.GetFilePath();
82 if (path
.empty() || !base::ReadFileToString(path
, &file_contents
)) {
86 const unsigned char* data
=
87 reinterpret_cast<const unsigned char*>(file_contents
.data());
88 // 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.
94 gfx::PNGCodec::Decode(data
, file_contents
.length(), bitmap
);
97 std::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());
106 for (std::vector
<ImageLoader::ImageRepresentation
>::const_iterator
107 it
= info_list
.begin();
108 it
!= info_list
.end();
110 DCHECK(it
->resource
.relative_path().empty() ||
111 extension
->path() == it
->resource
.extension_root());
114 if (extension
->location() == Manifest::COMPONENT
) {
115 const extensions::ComponentExtensionResourceManager
* manager
=
116 extensions::ExtensionsBrowserClient::Get()
117 ->GetComponentExtensionResourceManager();
118 if (manager
&& manager
->IsComponentExtensionResource(
119 extension
->path(), it
->resource
.relative_path(), &resource_id
)) {
120 LoadResourceOnUIThread(resource_id
, &bitmaps
[i
]);
129 namespace extensions
{
131 ////////////////////////////////////////////////////////////////////////////////
132 // ImageLoader::ImageRepresentation
134 ImageLoader::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
) {
145 ImageLoader::ImageRepresentation::~ImageRepresentation() {
148 ////////////////////////////////////////////////////////////////////////////////
149 // ImageLoader::LoadResult
151 struct ImageLoader::LoadResult
{
152 LoadResult(const SkBitmap
& bitmap
,
153 const gfx::Size
& original_size
,
154 const ImageRepresentation
& image_representation
);
158 gfx::Size original_size
;
159 ImageRepresentation image_representation
;
162 ImageLoader::LoadResult::LoadResult(
163 const SkBitmap
& bitmap
,
164 const gfx::Size
& original_size
,
165 const ImageLoader::ImageRepresentation
& image_representation
)
167 original_size(original_size
),
168 image_representation(image_representation
) {
171 ImageLoader::LoadResult::~LoadResult() {
176 // Need to be after ImageRepresentation and LoadResult are defined.
177 std::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
;
183 for (size_t i
= 0; i
< info_list
.size(); ++i
) {
184 const ImageLoader::ImageRepresentation
& image
= info_list
[i
];
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())
191 if (bitmaps
[i
].isNull())
192 LoadImageOnBlockingPool(image
, &bitmap
);
196 // If the image failed to load, skip it.
197 if (bitmap
.isNull() || bitmap
.empty())
200 gfx::Size
original_size(bitmap
.width(), bitmap
.height());
201 bitmap
= ResizeIfNeeded(bitmap
, image
);
203 load_result
.push_back(
204 ImageLoader::LoadResult(bitmap
, original_size
, image
));
212 ////////////////////////////////////////////////////////////////////////////////
215 ImageLoader::ImageLoader()
216 : weak_ptr_factory_(this) {
219 ImageLoader::~ImageLoader() {
223 ImageLoader
* ImageLoader::Get(content::BrowserContext
* context
) {
224 return ImageLoaderFactory::GetForBrowserContext(context
);
227 void ImageLoader::LoadImageAsync(const Extension
* extension
,
228 const ExtensionResource
& resource
,
229 const gfx::Size
& max_size
,
230 const ImageLoaderImageCallback
& callback
) {
231 std::vector
<ImageRepresentation
> info_list
;
232 info_list
.push_back(ImageRepresentation(
234 ImageRepresentation::RESIZE_WHEN_LARGER
,
236 ui::SCALE_FACTOR_100P
));
237 LoadImagesAsync(extension
, info_list
, callback
);
240 void ImageLoader::LoadImagesAsync(
241 const Extension
* extension
,
242 const std::vector
<ImageRepresentation
>& info_list
,
243 const ImageLoaderImageCallback
& callback
) {
244 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
245 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
246 base::PostTaskAndReplyWithResult(
247 BrowserThread::GetBlockingPool(),
249 base::Bind(LoadImagesOnBlockingPool
,
251 LoadResourceBitmaps(extension
, info_list
)),
253 &ImageLoader::ReplyBack
, weak_ptr_factory_
.GetWeakPtr(), callback
));
256 void ImageLoader::LoadImageFamilyAsync(
257 const extensions::Extension
* extension
,
258 const std::vector
<ImageRepresentation
>& info_list
,
259 const ImageLoaderImageFamilyCallback
& callback
) {
260 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
261 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
262 base::PostTaskAndReplyWithResult(
263 BrowserThread::GetBlockingPool(),
265 base::Bind(LoadImagesOnBlockingPool
,
267 LoadResourceBitmaps(extension
, info_list
)),
268 base::Bind(&ImageLoader::ReplyBackWithImageFamily
,
269 weak_ptr_factory_
.GetWeakPtr(),
273 void ImageLoader::ReplyBack(const ImageLoaderImageCallback
& callback
,
274 const std::vector
<LoadResult
>& load_result
) {
275 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
277 gfx::ImageSkia image_skia
;
279 for (std::vector
<LoadResult
>::const_iterator it
= load_result
.begin();
280 it
!= load_result
.end(); ++it
) {
281 const SkBitmap
& bitmap
= it
->bitmap
;
282 const ImageRepresentation
& image_rep
= it
->image_representation
;
284 image_skia
.AddRepresentation(gfx::ImageSkiaRep(
286 ui::GetScaleForScaleFactor(image_rep
.scale_factor
)));
290 if (!image_skia
.isNull()) {
291 image_skia
.MakeThreadSafe();
292 image
= gfx::Image(image_skia
);
298 void ImageLoader::ReplyBackWithImageFamily(
299 const ImageLoaderImageFamilyCallback
& callback
,
300 const std::vector
<LoadResult
>& load_result
) {
301 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
303 std::map
<std::pair
<int, int>, gfx::ImageSkia
> image_skia_map
;
304 gfx::ImageFamily image_family
;
306 for (std::vector
<LoadResult
>::const_iterator it
= load_result
.begin();
307 it
!= load_result
.end();
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(
316 gfx::ImageSkiaRep(bitmap
,
317 ui::GetScaleForScaleFactor(image_rep
.scale_factor
)));
320 for (std::map
<std::pair
<int, int>, gfx::ImageSkia
>::iterator it
=
321 image_skia_map
.begin();
322 it
!= image_skia_map
.end();
324 it
->second
.MakeThreadSafe();
325 image_family
.Add(it
->second
);
328 callback
.Run(image_family
);
331 } // namespace extensions