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 = ℑ 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