image_loader.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
26using content::BrowserThread;
27using extensions::Extension;
28using extensions::ExtensionsBrowserClient;
29using extensions::ImageLoader;
30using extensions::Manifest;
31
32namespace {
33
34bool 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
52SkBitmap 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
66void 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
75void 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
97std::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
129namespace extensions {
130
131////////////////////////////////////////////////////////////////////////////////
132// ImageLoader::ImageRepresentation
133
134ImageLoader::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
145ImageLoader::ImageRepresentation::~ImageRepresentation() {
146}
147
148////////////////////////////////////////////////////////////////////////////////
149// ImageLoader::LoadResult
150
151struct 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
162ImageLoader::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
171ImageLoader::LoadResult::~LoadResult() {
172}
173
174namespace {
175
176// Need to be after ImageRepresentation and LoadResult are defined.
177std::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
215ImageLoader::ImageLoader()
216    : weak_ptr_factory_(this) {
217}
218
219ImageLoader::~ImageLoader() {
220}
221
222// static
223ImageLoader* ImageLoader::Get(content::BrowserContext* context) {
224  return ImageLoaderFactory::GetForBrowserContext(context);
225}
226
227void 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
240void 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
256void 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
273void 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
298void 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