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