• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 using blink::WebFrame;
25 using blink::WebVector;
26 using blink::WebURL;
27 using blink::WebURLRequest;
28 
29 namespace {
30 
31 //  Proportionally resizes the |image| to fit in a box of size
32 // |max_image_size|.
ResizeImage(const SkBitmap & image,uint32_t max_image_size)33 SkBitmap 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.
FilterAndResizeImagesForMaximalSize(const std::vector<SkBitmap> & unfiltered,uint32_t max_image_size,std::vector<SkBitmap> * images,std::vector<gfx::Size> * original_image_sizes)53 void 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 
98 namespace content {
99 
ImageLoadingHelper(RenderFrame * render_frame)100 ImageLoadingHelper::ImageLoadingHelper(RenderFrame* render_frame)
101     : RenderFrameObserver(render_frame) {
102 }
103 
~ImageLoadingHelper()104 ImageLoadingHelper::~ImageLoadingHelper() {
105 }
106 
OnDownloadImage(int id,const GURL & image_url,bool is_favicon,uint32_t max_image_size)107 void 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 
DownloadImage(int id,const GURL & image_url,bool is_favicon,uint32_t max_image_size)135 bool 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 
DidDownloadImage(uint32_t max_image_size,MultiResolutionImageResourceFetcher * fetcher,const std::vector<SkBitmap> & images)152 void 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 
ImageFromDataUrl(const GURL & url) const180 SkBitmap 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 
OnMessageReceived(const IPC::Message & message)193 bool 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