- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / image_loader.cc
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.
4
5 #include "chrome/browser/extensions/image_loader.h"
6
7 #include <map>
8 #include <vector>
9
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"
28
29 #if defined(USE_AURA)
30 #include "ui/keyboard/keyboard_util.h"
31 #endif
32
33 using content::BrowserThread;
34 using extensions::Extension;
35 using extensions::ImageLoader;
36 using extensions::Manifest;
37
38 namespace {
39
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;
53   }
54 }
55
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());
65   }
66
67   return bitmap;
68 }
69
70 void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) {
71   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
72
73   gfx::ImageSkia image(
74       *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id));
75   image.MakeThreadSafe();
76   *bitmap = *image.bitmap();
77 }
78
79 void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation& image_info,
80                              SkBitmap* bitmap) {
81   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
82
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;
88   }
89
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);
99 }
100
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();
111
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;
115   }
116 }
117
118 }  // namespace
119
120 namespace extensions {
121
122 ////////////////////////////////////////////////////////////////////////////////
123 // ImageLoader::ImageRepresentation
124
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) {
134 }
135
136 ImageLoader::ImageRepresentation::~ImageRepresentation() {
137 }
138
139 ////////////////////////////////////////////////////////////////////////////////
140 // ImageLoader::LoadResult
141
142 struct ImageLoader::LoadResult  {
143   LoadResult(const SkBitmap& bitmap,
144              const gfx::Size& original_size,
145              const ImageRepresentation& image_representation);
146   ~LoadResult();
147
148   SkBitmap bitmap;
149   gfx::Size original_size;
150   ImageRepresentation image_representation;
151 };
152
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) {
160 }
161
162 ImageLoader::LoadResult::~LoadResult() {
163 }
164
165 ////////////////////////////////////////////////////////////////////////////////
166 // ImageLoader
167
168 ImageLoader::ImageLoader()
169     : weak_ptr_factory_(this) {
170 }
171
172 ImageLoader::~ImageLoader() {
173 }
174
175 // static
176 ImageLoader* ImageLoader::Get(content::BrowserContext* context) {
177   return ImageLoaderFactory::GetForBrowserContext(context);
178 }
179
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;
184
185 // static
186 bool ImageLoader::IsComponentExtensionResource(
187     const base::FilePath& extension_path,
188     const base::FilePath& resource_path,
189     int* resource_id) {
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},
200 #endif
201   };
202
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()) {
214       size_t size;
215       const GritResourceMap* keyboard_resources =
216           keyboard::GetKeyboardExtensionResources(&size);
217       AddComponentResourceEntries(
218           path_to_resource_id.Pointer(), keyboard_resources, size);
219     }
220 #endif
221   }
222
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)) {
228     return false;
229   }
230   relative_path = relative_path.Append(resource_path);
231   relative_path = relative_path.NormalizePathSeparators();
232
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;
237
238   return entry != path_to_resource_id.Get().end();
239 }
240
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(
248       resource,
249       ImageRepresentation::RESIZE_WHEN_LARGER,
250       max_size,
251       ui::SCALE_FACTOR_100P));
252   LoadImagesAsync(extension, info_list, callback);
253 }
254
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));
260
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.
264
265   std::vector<SkBitmap> bitmaps;
266   bitmaps.resize(info_list.size());
267
268   int i = 0;
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());
273
274     int resource_id;
275     if (extension->location() == Manifest::COMPONENT &&
276         IsComponentExtensionResource(extension->path(),
277                                      it->resource.relative_path(),
278                                      &resource_id)) {
279       LoadResourceOnUIThread(resource_id, &bitmaps[i]);
280     }
281   }
282
283   DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
284   std::vector<LoadResult>* load_result = new std::vector<LoadResult>;
285   BrowserThread::PostBlockingPoolTaskAndReply(
286       FROM_HERE,
287       base::Bind(LoadImagesOnBlockingPool, info_list, bitmaps, load_result),
288       base::Bind(&ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(),
289                  base::Owned(load_result), callback));
290 }
291
292 // static
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());
298
299   int i = 0;
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())
304       continue;
305
306     SkBitmap bitmap;
307     if (!bitmaps[i].isNull()) {
308       bitmap = bitmaps[i];
309     } else {
310       LoadImageOnBlockingPool(*it, &bitmap);
311     }
312
313     // If the image failed to load, skip it.
314     if (bitmap.isNull() || bitmap.empty())
315       continue;
316
317     gfx::Size original_size(bitmap.width(), bitmap.height());
318     bitmap = ResizeIfNeeded(bitmap, *it);
319
320     load_result->push_back(LoadResult(bitmap, original_size, *it));
321   }
322 }
323
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));
328
329   gfx::ImageSkia image_skia;
330
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;
335
336     image_skia.AddRepresentation(gfx::ImageSkiaRep(
337         bitmap,
338         ui::GetImageScale(image_rep.scale_factor)));
339   }
340
341   gfx::Image image;
342   if (!image_skia.isNull()) {
343     image_skia.MakeThreadSafe();
344     image = gfx::Image(image_skia);
345   }
346
347   callback.Run(image);
348 }
349
350 }  // namespace extensions