Home | History | Annotate | Download | only in renderer
      1 // Copyright (c) 2013 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 "content/renderer/image_loading_helper.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "content/child/image_decoder.h"
     10 #include "content/common/image_messages.h"
     11 #include "content/public/common/url_constants.h"
     12 #include "content/public/renderer/render_view.h"
     13 #include "content/renderer/fetchers/multi_resolution_image_resource_fetcher.h"
     14 #include "net/base/data_url.h"
     15 #include "skia/ext/image_operations.h"
     16 #include "third_party/WebKit/public/platform/WebURLRequest.h"
     17 #include "third_party/WebKit/public/platform/WebVector.h"
     18 #include "third_party/WebKit/public/web/WebFrame.h"
     19 #include "third_party/WebKit/public/web/WebView.h"
     20 #include "ui/gfx/favicon_size.h"
     21 #include "ui/gfx/size.h"
     22 #include "ui/gfx/skbitmap_operations.h"
     23 #include "webkit/glue/webkit_glue.h"
     24 
     25 using WebKit::WebFrame;
     26 using WebKit::WebVector;
     27 using WebKit::WebURL;
     28 using WebKit::WebURLRequest;
     29 
     30 namespace {
     31 
     32 //  Proportionally resizes the |image| to fit in a box of size
     33 // |max_image_size|.
     34 SkBitmap ResizeImage(const SkBitmap& image, uint32_t max_image_size) {
     35   if (max_image_size == 0)
     36     return image;
     37   uint32_t max_dimension = std::max(image.width(), image.height());
     38   if (max_dimension <= max_image_size)
     39     return image;
     40   // Proportionally resize the minimal image to fit in a box of size
     41   // max_image_size.
     42   return skia::ImageOperations::Resize(
     43       image,
     44       skia::ImageOperations::RESIZE_BEST,
     45       static_cast<uint64_t>(image.width()) * max_image_size / max_dimension,
     46       static_cast<uint64_t>(image.height()) * max_image_size / max_dimension);
     47 }
     48 
     49 // Filters the array of bitmaps, removing all images that do not fit in a box of
     50 // size |max_image_size|. Returns the result if it is not empty. Otherwise,
     51 // find the smallest image in the array and resize it proportionally to fit
     52 // in a box of size |max_image_size|.
     53 std::vector<SkBitmap> FilterAndResizeImageForMaximalSize(
     54     const std::vector<SkBitmap>& images,
     55     uint32_t max_image_size) {
     56   if (!images.size() || max_image_size == 0)
     57     return images;
     58   std::vector<SkBitmap> result;
     59   const SkBitmap* min_image = NULL;
     60   uint32_t min_image_size = std::numeric_limits<uint32_t>::max();
     61   // Filter the images by |max_image_size|, and also identify the smallest image
     62   // in case all the images are bigger than |max_image_size|.
     63   for (std::vector<SkBitmap>::const_iterator it = images.begin();
     64        it != images.end();
     65        ++it) {
     66     const SkBitmap& image = *it;
     67     uint32_t current_size = std::max(it->width(), it->height());
     68     if (current_size < min_image_size) {
     69       min_image = &image;
     70       min_image_size = current_size;
     71     }
     72     if (static_cast<uint32_t>(image.width()) <= max_image_size &&
     73         static_cast<uint32_t>(image.height()) <= max_image_size) {
     74       result.push_back(image);
     75     }
     76   }
     77   DCHECK(min_image);
     78   if (result.size())
     79     return result;
     80   // Proportionally resize the minimal image to fit in a box of size
     81   // max_image_size.
     82   result.push_back(ResizeImage(*min_image, max_image_size));
     83   return result;
     84 }
     85 
     86 }  // namespace
     87 
     88 namespace content {
     89 
     90 ImageLoadingHelper::ImageLoadingHelper(RenderView* render_view)
     91     : RenderViewObserver(render_view) {
     92 }
     93 
     94 ImageLoadingHelper::~ImageLoadingHelper() {
     95 }
     96 
     97 void ImageLoadingHelper::OnDownloadImage(int id,
     98                                          const GURL& image_url,
     99                                          bool is_favicon,
    100                                          uint32_t preferred_image_size,
    101                                          uint32_t max_image_size) {
    102   std::vector<SkBitmap> result_images;
    103   if (image_url.SchemeIs(chrome::kDataScheme)) {
    104     SkBitmap data_image = ImageFromDataUrl(image_url);
    105     if (!data_image.empty())
    106       result_images.push_back(ResizeImage(data_image, max_image_size));
    107   } else {
    108     if (DownloadImage(
    109             id, image_url, is_favicon, preferred_image_size, max_image_size)) {
    110       // Will complete asynchronously via ImageLoadingHelper::DidDownloadImage
    111       return;
    112     }
    113   }
    114 
    115   Send(new ImageHostMsg_DidDownloadImage(routing_id(),
    116                                          id,
    117                                          0,
    118                                          image_url,
    119                                          preferred_image_size,
    120                                          result_images));
    121 }
    122 
    123 bool ImageLoadingHelper::DownloadImage(int id,
    124                                        const GURL& image_url,
    125                                        bool is_favicon,
    126                                        uint32_t preferred_image_size,
    127                                        uint32_t max_image_size) {
    128   // Make sure webview was not shut down.
    129   if (!render_view()->GetWebView())
    130     return false;
    131   // Create an image resource fetcher and assign it with a call back object.
    132   image_fetchers_.push_back(new MultiResolutionImageResourceFetcher(
    133       image_url,
    134       render_view()->GetWebView()->mainFrame(),
    135       id,
    136       is_favicon ? WebURLRequest::TargetIsFavicon :
    137                    WebURLRequest::TargetIsImage,
    138       base::Bind(&ImageLoadingHelper::DidDownloadImage,
    139                  base::Unretained(this),
    140                  preferred_image_size,
    141                  max_image_size)));
    142   return true;
    143 }
    144 
    145 void ImageLoadingHelper::DidDownloadImage(
    146     uint32_t preferred_image_size,
    147     uint32_t max_image_size,
    148     MultiResolutionImageResourceFetcher* fetcher,
    149     const std::vector<SkBitmap>& images) {
    150   // Notify requester of image download status.
    151   Send(new ImageHostMsg_DidDownloadImage(
    152       routing_id(),
    153       fetcher->id(),
    154       fetcher->http_status_code(),
    155       fetcher->image_url(),
    156       preferred_image_size,
    157       FilterAndResizeImageForMaximalSize(images, max_image_size)));
    158 
    159   // Remove the image fetcher from our pending list. We're in the callback from
    160   // MultiResolutionImageResourceFetcher, best to delay deletion.
    161   ImageResourceFetcherList::iterator iter =
    162       std::find(image_fetchers_.begin(), image_fetchers_.end(), fetcher);
    163   if (iter != image_fetchers_.end()) {
    164     image_fetchers_.weak_erase(iter);
    165     base::MessageLoop::current()->DeleteSoon(FROM_HERE, fetcher);
    166   }
    167 }
    168 
    169 SkBitmap ImageLoadingHelper::ImageFromDataUrl(const GURL& url) const {
    170   std::string mime_type, char_set, data;
    171   if (net::DataURL::Parse(url, &mime_type, &char_set, &data) && !data.empty()) {
    172     // Decode the image using WebKit's image decoder.
    173     ImageDecoder decoder(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize));
    174     const unsigned char* src_data =
    175         reinterpret_cast<const unsigned char*>(&data[0]);
    176 
    177     return decoder.Decode(src_data, data.size());
    178   }
    179   return SkBitmap();
    180 }
    181 
    182 bool ImageLoadingHelper::OnMessageReceived(const IPC::Message& message) {
    183   bool handled = true;
    184   IPC_BEGIN_MESSAGE_MAP(ImageLoadingHelper, message)
    185     IPC_MESSAGE_HANDLER(ImageMsg_DownloadImage, OnDownloadImage)
    186     IPC_MESSAGE_UNHANDLED(handled = false)
    187   IPC_END_MESSAGE_MAP()
    188 
    189   return handled;
    190 }
    191 
    192 }  // namespace content
    193