• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "extensions/browser/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/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"
25 
26 using content::BrowserThread;
27 using extensions::Extension;
28 using extensions::ExtensionsBrowserClient;
29 using extensions::ImageLoader;
30 using extensions::Manifest;
31 
32 namespace {
33 
ShouldResizeImageRepresentation(ImageLoader::ImageRepresentation::ResizeCondition resize_method,const gfx::Size & decoded_size,const gfx::Size & desired_size)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:
45       return false;
46     default:
47       NOTREACHED();
48       return false;
49   }
50 }
51 
ResizeIfNeeded(const SkBitmap & bitmap,const ImageLoader::ImageRepresentation & image_info)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,
56                                       original_size,
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());
61   }
62 
63   return bitmap;
64 }
65 
LoadResourceOnUIThread(int resource_id,SkBitmap * bitmap)66 void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) {
67   DCHECK_CURRENTLY_ON(BrowserThread::UI);
68 
69   gfx::ImageSkia image(
70       *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id));
71   image.MakeThreadSafe();
72   *bitmap = *image.bitmap();
73 }
74 
LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation & image_info,SkBitmap * bitmap)75 void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation& image_info,
76                              SkBitmap* bitmap) {
77   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
78 
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)) {
83     return;
84   }
85 
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);
95 }
96 
LoadResourceBitmaps(const Extension * extension,const std::vector<ImageLoader::ImageRepresentation> & info_list)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());
104 
105   int i = 0;
106   for (std::vector<ImageLoader::ImageRepresentation>::const_iterator
107            it = info_list.begin();
108        it != info_list.end();
109        ++it, ++i) {
110     DCHECK(it->resource.relative_path().empty() ||
111            extension->path() == it->resource.extension_root());
112 
113     int resource_id;
114     if (extension->location() == Manifest::COMPONENT) {
115       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]);
121       }
122     }
123   }
124   return bitmaps;
125 }
126 
127 }  // namespace
128 
129 namespace extensions {
130 
131 ////////////////////////////////////////////////////////////////////////////////
132 // ImageLoader::ImageRepresentation
133 
ImageRepresentation(const ExtensionResource & resource,ResizeCondition resize_condition,const gfx::Size & desired_size,ui::ScaleFactor scale_factor)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) {
143 }
144 
~ImageRepresentation()145 ImageLoader::ImageRepresentation::~ImageRepresentation() {
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 // ImageLoader::LoadResult
150 
151 struct ImageLoader::LoadResult  {
152   LoadResult(const SkBitmap& bitmap,
153              const gfx::Size& original_size,
154              const ImageRepresentation& image_representation);
155   ~LoadResult();
156 
157   SkBitmap bitmap;
158   gfx::Size original_size;
159   ImageRepresentation image_representation;
160 };
161 
LoadResult(const SkBitmap & bitmap,const gfx::Size & original_size,const ImageLoader::ImageRepresentation & image_representation)162 ImageLoader::LoadResult::LoadResult(
163     const SkBitmap& bitmap,
164     const gfx::Size& original_size,
165     const ImageLoader::ImageRepresentation& image_representation)
166     : bitmap(bitmap),
167       original_size(original_size),
168       image_representation(image_representation) {
169 }
170 
~LoadResult()171 ImageLoader::LoadResult::~LoadResult() {
172 }
173 
174 namespace {
175 
176 // Need to be after ImageRepresentation and LoadResult are defined.
LoadImagesOnBlockingPool(const std::vector<ImageLoader::ImageRepresentation> & info_list,const std::vector<SkBitmap> & bitmaps)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;
182 
183   for (size_t i = 0; i < info_list.size(); ++i) {
184     const ImageLoader::ImageRepresentation& image = info_list[i];
185 
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())
188       continue;
189 
190     SkBitmap bitmap;
191     if (bitmaps[i].isNull())
192       LoadImageOnBlockingPool(image, &bitmap);
193     else
194       bitmap = bitmaps[i];
195 
196     // If the image failed to load, skip it.
197     if (bitmap.isNull() || bitmap.empty())
198       continue;
199 
200     gfx::Size original_size(bitmap.width(), bitmap.height());
201     bitmap = ResizeIfNeeded(bitmap, image);
202 
203     load_result.push_back(
204         ImageLoader::LoadResult(bitmap, original_size, image));
205   }
206 
207   return load_result;
208 }
209 
210 }  // namespace
211 
212 ////////////////////////////////////////////////////////////////////////////////
213 // ImageLoader
214 
ImageLoader()215 ImageLoader::ImageLoader()
216     : weak_ptr_factory_(this) {
217 }
218 
~ImageLoader()219 ImageLoader::~ImageLoader() {
220 }
221 
222 // static
Get(content::BrowserContext * context)223 ImageLoader* ImageLoader::Get(content::BrowserContext* context) {
224   return ImageLoaderFactory::GetForBrowserContext(context);
225 }
226 
LoadImageAsync(const Extension * extension,const ExtensionResource & resource,const gfx::Size & max_size,const ImageLoaderImageCallback & callback)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(
233       resource,
234       ImageRepresentation::RESIZE_WHEN_LARGER,
235       max_size,
236       ui::SCALE_FACTOR_100P));
237   LoadImagesAsync(extension, info_list, callback);
238 }
239 
LoadImagesAsync(const Extension * extension,const std::vector<ImageRepresentation> & info_list,const ImageLoaderImageCallback & 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(),
248       FROM_HERE,
249       base::Bind(LoadImagesOnBlockingPool,
250                  info_list,
251                  LoadResourceBitmaps(extension, info_list)),
252       base::Bind(
253           &ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(), callback));
254 }
255 
LoadImageFamilyAsync(const extensions::Extension * extension,const std::vector<ImageRepresentation> & info_list,const ImageLoaderImageFamilyCallback & 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(),
264       FROM_HERE,
265       base::Bind(LoadImagesOnBlockingPool,
266                  info_list,
267                  LoadResourceBitmaps(extension, info_list)),
268       base::Bind(&ImageLoader::ReplyBackWithImageFamily,
269                  weak_ptr_factory_.GetWeakPtr(),
270                  callback));
271 }
272 
ReplyBack(const ImageLoaderImageCallback & callback,const std::vector<LoadResult> & load_result)273 void ImageLoader::ReplyBack(const ImageLoaderImageCallback& callback,
274                             const std::vector<LoadResult>& load_result) {
275   DCHECK_CURRENTLY_ON(BrowserThread::UI);
276 
277   gfx::ImageSkia image_skia;
278 
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;
283 
284     image_skia.AddRepresentation(gfx::ImageSkiaRep(
285         bitmap,
286         ui::GetScaleForScaleFactor(image_rep.scale_factor)));
287   }
288 
289   gfx::Image image;
290   if (!image_skia.isNull()) {
291     image_skia.MakeThreadSafe();
292     image = gfx::Image(image_skia);
293   }
294 
295   callback.Run(image);
296 }
297 
ReplyBackWithImageFamily(const ImageLoaderImageFamilyCallback & callback,const std::vector<LoadResult> & load_result)298 void ImageLoader::ReplyBackWithImageFamily(
299     const ImageLoaderImageFamilyCallback& callback,
300     const std::vector<LoadResult>& load_result) {
301   DCHECK_CURRENTLY_ON(BrowserThread::UI);
302 
303   std::map<std::pair<int, int>, gfx::ImageSkia> image_skia_map;
304   gfx::ImageFamily image_family;
305 
306   for (std::vector<LoadResult>::const_iterator it = load_result.begin();
307        it != load_result.end();
308        ++it) {
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)));
318   }
319 
320   for (std::map<std::pair<int, int>, gfx::ImageSkia>::iterator it =
321            image_skia_map.begin();
322        it != image_skia_map.end();
323        ++it) {
324     it->second.MakeThreadSafe();
325     image_family.Add(it->second);
326   }
327 
328   callback.Run(image_family);
329 }
330 
331 }  // namespace extensions
332