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 "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"
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() {
165 ////////////////////////////////////////////////////////////////////////////////
168 ImageLoader::ImageLoader()
169 : weak_ptr_factory_(this) {
172 ImageLoader::~ImageLoader() {
176 ImageLoader* ImageLoader::Get(content::BrowserContext* context) {
177 return ImageLoaderFactory::GetForBrowserContext(context);
180 // A map from a resource path to the resource ID. Used only by
181 // IsComponentExtensionResource below.
182 static base::LazyInstance<std::map<base::FilePath, int> > path_to_resource_id =
183 LAZY_INSTANCE_INITIALIZER;
186 bool ImageLoader::IsComponentExtensionResource(
187 const base::FilePath& extension_path,
188 const base::FilePath& resource_path,
190 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},
195 #if defined(ENABLE_SETTINGS_APP)
196 {"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},
198 {"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},
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()) {
215 const GritResourceMap* keyboard_resources =
216 keyboard::GetKeyboardExtensionResources(&size);
217 AddComponentResourceEntries(
218 path_to_resource_id.Pointer(), keyboard_resources, size);
223 base::FilePath directory_path = extension_path;
224 base::FilePath resources_dir;
225 base::FilePath relative_path;
226 if (!PathService::Get(chrome::DIR_RESOURCES, &resources_dir) ||
227 !resources_dir.AppendRelativePath(directory_path, &relative_path)) {
230 relative_path = relative_path.Append(resource_path);
231 relative_path = relative_path.NormalizePathSeparators();
233 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;
238 return entry != path_to_resource_id.Get().end();
241 void 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(
249 ImageRepresentation::RESIZE_WHEN_LARGER,
251 ui::SCALE_FACTOR_100P));
252 LoadImagesAsync(extension, info_list, callback);
255 void 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));
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.
265 std::vector<SkBitmap> bitmaps;
266 bitmaps.resize(info_list.size());
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());
275 if (extension->location() == Manifest::COMPONENT &&
276 IsComponentExtensionResource(extension->path(),
277 it->resource.relative_path(),
279 LoadResourceOnUIThread(resource_id, &bitmaps[i]);
283 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
284 std::vector<LoadResult>* load_result = new std::vector<LoadResult>;
285 BrowserThread::PostBlockingPoolTaskAndReply(
287 base::Bind(LoadImagesOnBlockingPool, info_list, bitmaps, load_result),
288 base::Bind(&ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(),
289 base::Owned(load_result), callback));
293 void ImageLoader::LoadImagesOnBlockingPool(
294 const std::vector<ImageRepresentation>& info_list,
295 const std::vector<SkBitmap>& bitmaps,
296 std::vector<LoadResult>* load_result) {
297 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
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())
307 if (!bitmaps[i].isNull()) {
310 LoadImageOnBlockingPool(*it, &bitmap);
313 // If the image failed to load, skip it.
314 if (bitmap.isNull() || bitmap.empty())
317 gfx::Size original_size(bitmap.width(), bitmap.height());
318 bitmap = ResizeIfNeeded(bitmap, *it);
320 load_result->push_back(LoadResult(bitmap, original_size, *it));
324 void ImageLoader::ReplyBack(
325 const std::vector<LoadResult>* load_result,
326 const base::Callback<void(const gfx::Image&)>& callback) {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
329 gfx::ImageSkia image_skia;
331 for (std::vector<LoadResult>::const_iterator it = load_result->begin();
332 it != load_result->end(); ++it) {
333 const SkBitmap& bitmap = it->bitmap;
334 const ImageRepresentation& image_rep = it->image_representation;
336 image_skia.AddRepresentation(gfx::ImageSkiaRep(
338 ui::GetImageScale(image_rep.scale_factor)));
342 if (!image_skia.isNull()) {
343 image_skia.MakeThreadSafe();
344 image = gfx::Image(image_skia);
350 } // namespace extensions