Home | History | Annotate | Download | only in browser
      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/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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    171 ImageLoader::LoadResult::~LoadResult() {
    172 }
    173 
    174 namespace {
    175 
    176 // Need to be after ImageRepresentation and LoadResult are defined.
    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 
    215 ImageLoader::ImageLoader()
    216     : weak_ptr_factory_(this) {
    217 }
    218 
    219 ImageLoader::~ImageLoader() {
    220 }
    221 
    222 // static
    223 ImageLoader* ImageLoader::Get(content::BrowserContext* context) {
    224   return ImageLoaderFactory::GetForBrowserContext(context);
    225 }
    226 
    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 
    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 
    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 
    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 
    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