Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / extensions / image_loader.cc
blob8ccfef65d1b844f3fcc9098ac535f44a5870bea2
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"
7 #include <map>
8 #include <vector>
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"
29 #if defined(USE_AURA)
30 #include "ui/keyboard/keyboard_util.h"
31 #endif
33 using content::BrowserThread;
34 using extensions::Extension;
35 using extensions::ImageLoader;
36 using extensions::Manifest;
38 namespace {
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();
50 default:
51 NOTREACHED();
52 return false;
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,
60 original_size,
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());
67 return bitmap;
70 void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73 gfx::ImageSkia image(
74 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id));
75 image.MakeThreadSafe();
76 *bitmap = *image.bitmap();
79 void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation& image_info,
80 SkBitmap* bitmap) {
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)) {
87 return;
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,
106 size_t size) {
107 for (size_t i = 0; i < size; ++i) {
108 base::FilePath resource_path = base::FilePath().AppendASCII(
109 entries[i].name);
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;
118 } // namespace
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);
146 ~LoadResult();
148 SkBitmap bitmap;
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)
157 : bitmap(bitmap),
158 original_size(original_size),
159 image_representation(image_representation) {
162 ImageLoader::LoadResult::~LoadResult() {
165 namespace {
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())
179 continue;
181 SkBitmap bitmap;
182 if (bitmaps[i].isNull())
183 LoadImageOnBlockingPool(image, &bitmap);
184 else
185 bitmap = bitmaps[i];
187 // If the image failed to load, skip it.
188 if (bitmap.isNull() || bitmap.empty())
189 continue;
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));
198 return load_result;
201 } // namespace
203 ////////////////////////////////////////////////////////////////////////////////
204 // ImageLoader
206 ImageLoader::ImageLoader()
207 : weak_ptr_factory_(this) {
210 ImageLoader::~ImageLoader() {
213 // static
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;
223 // static
224 bool ImageLoader::IsComponentExtensionResource(
225 const base::FilePath& extension_path,
226 const base::FilePath& resource_path,
227 int* resource_id) {
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},
238 #endif
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()) {
252 size_t size;
253 const GritResourceMap* keyboard_resources =
254 keyboard::GetKeyboardExtensionResources(&size);
255 AddComponentResourceEntries(
256 path_to_resource_id.Pointer(), keyboard_resources, size);
258 #endif
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)) {
266 return false;
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(
285 resource,
286 ImageRepresentation::RESIZE_WHEN_LARGER,
287 max_size,
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());
305 int i = 0;
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());
311 int resource_id;
312 if (extension->location() == Manifest::COMPONENT &&
313 IsComponentExtensionResource(extension->path(),
314 it->resource.relative_path(),
315 &resource_id)) {
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()
320 << " is null.";
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(),
331 FROM_HERE,
332 base::Bind(LoadImagesOnBlockingPool, info_list, bitmaps),
333 base::Bind(&ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(),
334 callback));
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(
349 bitmap,
350 ui::GetImageScale(image_rep.scale_factor)));
353 gfx::Image image;
354 if (!image_skia.isNull()) {
355 image_skia.MakeThreadSafe();
356 image = gfx::Image(image_skia);
359 callback.Run(image);
362 } // namespace extensions