Remove all |gfx::Image(new SkBitmap(...))| antipatterns.
[chromium-blink-merge.git] / chrome / browser / favicon / favicon_handler.cc
blob52e429c28a6886e33307cd5758e0e3bb29ea12f3
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/favicon/favicon_handler.h"
7 #include "build/build_config.h"
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/memory/ref_counted_memory.h"
14 #include "chrome/browser/bookmarks/bookmark_model.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/icon_messages.h"
17 #include "content/public/browser/favicon_status.h"
18 #include "content/public/browser/navigation_entry.h"
19 #include "skia/ext/image_operations.h"
20 #include "ui/gfx/codec/png_codec.h"
21 #include "ui/gfx/image/image.h"
22 #include "ui/gfx/image/image_util.h"
24 using content::NavigationEntry;
26 namespace {
28 // Returns history::IconType the given icon_type corresponds to.
29 history::IconType ToHistoryIconType(FaviconURL::IconType icon_type) {
30 switch (icon_type) {
31 case FaviconURL::FAVICON:
32 return history::FAVICON;
33 case FaviconURL::TOUCH_ICON:
34 return history::TOUCH_ICON;
35 case FaviconURL::TOUCH_PRECOMPOSED_ICON:
36 return history::TOUCH_PRECOMPOSED_ICON;
37 case FaviconURL::INVALID_ICON:
38 return history::INVALID_ICON;
40 NOTREACHED();
41 // Shouldn't reach here, just make compiler happy.
42 return history::INVALID_ICON;
45 bool DoUrlAndIconMatch(const FaviconURL& favicon_url,
46 const GURL& url,
47 history::IconType icon_type) {
48 return favicon_url.icon_url == url &&
49 favicon_url.icon_type == static_cast<FaviconURL::IconType>(icon_type);
52 } // namespace
54 FaviconHandler::DownloadRequest::DownloadRequest()
55 : icon_type(history::INVALID_ICON) {
58 FaviconHandler::DownloadRequest::~DownloadRequest() {
61 FaviconHandler::DownloadRequest::DownloadRequest(
62 const GURL& url,
63 const GURL& image_url,
64 const FaviconTabHelper::ImageDownloadCallback& callback,
65 history::IconType icon_type)
66 : url(url),
67 image_url(image_url),
68 callback(callback),
69 icon_type(icon_type) {
72 FaviconHandler::FaviconHandler(Profile* profile,
73 FaviconHandlerDelegate* delegate,
74 Type icon_type)
75 : got_favicon_from_history_(false),
76 favicon_expired_(false),
77 icon_types_(icon_type == FAVICON ? history::FAVICON :
78 history::TOUCH_ICON | history::TOUCH_PRECOMPOSED_ICON),
79 current_url_index_(0),
80 profile_(profile),
81 delegate_(delegate) {
82 DCHECK(profile_);
83 DCHECK(delegate_);
86 FaviconHandler::~FaviconHandler() {
87 // Call pending download callbacks with error to allow caller to clean up.
88 for (DownloadRequests::iterator i = download_requests_.begin();
89 i != download_requests_.end(); ++i) {
90 if (!i->second.callback.is_null()) {
91 i->second.callback.Run(i->first, true, SkBitmap());
96 void FaviconHandler::FetchFavicon(const GURL& url) {
97 cancelable_consumer_.CancelAllRequests();
99 url_ = url;
101 favicon_expired_ = got_favicon_from_history_ = false;
102 current_url_index_ = 0;
103 urls_.clear();
105 // Request the favicon from the history service. In parallel to this the
106 // renderer is going to notify us (well TabContents) when the favicon url is
107 // available.
108 if (GetFaviconService()) {
109 GetFaviconForURL(url_, icon_types_, &cancelable_consumer_,
110 base::Bind(&FaviconHandler::OnFaviconDataForInitialURL,
111 base::Unretained(this)));
115 int FaviconHandler::DownloadImage(
116 const GURL& image_url,
117 int image_size,
118 history::IconType icon_type,
119 const FaviconTabHelper::ImageDownloadCallback& callback) {
120 return ScheduleDownload(GURL(), image_url, image_size, icon_type, callback);
123 FaviconService* FaviconHandler::GetFaviconService() {
124 return profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
127 void FaviconHandler::SetFavicon(
128 const GURL& url,
129 const GURL& image_url,
130 const gfx::Image& image,
131 history::IconType icon_type) {
132 const SkBitmap& bitmap = image;
133 const gfx::Image& sized_image = (preferred_icon_size() == 0 ||
134 (preferred_icon_size() == bitmap.width() &&
135 preferred_icon_size() == bitmap.height())) ?
136 image : ResizeFaviconIfNeeded(image);
138 if (GetFaviconService() && ShouldSaveFavicon(url)) {
139 std::vector<unsigned char> image_data;
140 if (gfx::PNGEncodedDataFromImage(sized_image, &image_data))
141 SetHistoryFavicon(url, image_url, image_data, icon_type);
144 if (url == url_ && icon_type == history::FAVICON) {
145 NavigationEntry* entry = GetEntry();
146 if (entry)
147 UpdateFavicon(entry, &sized_image);
151 void FaviconHandler::UpdateFavicon(NavigationEntry* entry,
152 scoped_refptr<RefCountedMemory> data) {
153 scoped_ptr<gfx::Image> image(gfx::ImageFromPNGEncodedData(data->front(),
154 data->size()));
155 UpdateFavicon(entry, image.get());
158 void FaviconHandler::UpdateFavicon(NavigationEntry* entry,
159 const gfx::Image* image) {
160 // No matter what happens, we need to mark the favicon as being set.
161 entry->GetFavicon().valid = true;
163 if (!image)
164 return;
166 entry->GetFavicon().bitmap = *image;
167 delegate_->NotifyFaviconUpdated();
170 void FaviconHandler::OnUpdateFaviconURL(
171 int32 page_id,
172 const std::vector<FaviconURL>& candidates) {
173 NavigationEntry* entry = GetEntry();
174 if (!entry)
175 return;
177 bool got_favicon_url_update = false;
178 for (std::vector<FaviconURL>::const_iterator i = candidates.begin();
179 i != candidates.end(); ++i) {
180 if (!i->icon_url.is_empty() && (i->icon_type & icon_types_)) {
181 if (!got_favicon_url_update) {
182 got_favicon_url_update = true;
183 urls_.clear();
184 current_url_index_ = 0;
186 urls_.push_back(*i);
190 // TODO(davemoore) Should clear on empty url. Currently we ignore it.
191 // This appears to be what FF does as well.
192 // No URL was added.
193 if (!got_favicon_url_update)
194 return;
196 if (!GetFaviconService())
197 return;
199 // For FAVICON.
200 if (current_candidate()->icon_type == FaviconURL::FAVICON) {
201 if (!favicon_expired_ && entry->GetFavicon().valid &&
202 DoUrlAndIconMatch(*current_candidate(), entry->GetFavicon().url,
203 history::FAVICON))
204 return;
206 entry->GetFavicon().url = current_candidate()->icon_url;
207 } else if (!favicon_expired_ && got_favicon_from_history_ &&
208 history_icon_.is_valid() &&
209 DoUrlAndIconMatch(
210 *current_candidate(),
211 history_icon_.icon_url, history_icon_.icon_type)) {
212 return;
215 if (got_favicon_from_history_)
216 DownloadFaviconOrAskHistory(entry->GetURL(), current_candidate()->icon_url,
217 ToHistoryIconType(current_candidate()->icon_type));
220 void FaviconHandler::OnDidDownloadFavicon(int id,
221 const GURL& image_url,
222 bool errored,
223 const gfx::Image& image) {
224 DownloadRequests::iterator i = download_requests_.find(id);
225 if (i == download_requests_.end()) {
226 // Currently TabContents notifies us of ANY downloads so that it is
227 // possible to get here.
228 return;
231 if (!i->second.callback.is_null()) {
232 i->second.callback.Run(id, errored, *(&image));
233 } else if (current_candidate() &&
234 DoUrlAndIconMatch(*current_candidate(), image_url,
235 i->second.icon_type)) {
236 // The downloaded icon is still valid when there is no FaviconURL update
237 // during the downloading.
238 if (!errored) {
239 SetFavicon(i->second.url, image_url, image, i->second.icon_type);
240 } else if (GetEntry() && ++current_url_index_ < urls_.size()) {
241 // Copies all candidate except first one and notifies the FaviconHandler,
242 // so the next candidate can be processed.
243 std::vector<FaviconURL> new_candidates(urls_.begin() + 1, urls_.end());
244 OnUpdateFaviconURL(0, new_candidates);
247 download_requests_.erase(i);
250 NavigationEntry* FaviconHandler::GetEntry() {
251 NavigationEntry* entry = delegate_->GetActiveEntry();
252 if (entry && entry->GetURL() == url_)
253 return entry;
255 // If the URL has changed out from under us (as will happen with redirects)
256 // return NULL.
257 return NULL;
260 int FaviconHandler::DownloadFavicon(const GURL& image_url, int image_size) {
261 if (!image_url.is_valid()) {
262 NOTREACHED();
263 return 0;
265 static int next_id = 1;
266 int id = next_id++;
267 delegate_->StartDownload(id, image_url, image_size);
268 return id;
271 void FaviconHandler::UpdateFaviconMappingAndFetch(
272 const GURL& page_url,
273 const GURL& icon_url,
274 history::IconType icon_type,
275 CancelableRequestConsumerBase* consumer,
276 const FaviconService::FaviconDataCallback& callback) {
277 GetFaviconService()->UpdateFaviconMappingAndFetch(page_url, icon_url,
278 icon_type, consumer, callback);
281 void FaviconHandler::GetFavicon(
282 const GURL& icon_url,
283 history::IconType icon_type,
284 CancelableRequestConsumerBase* consumer,
285 const FaviconService::FaviconDataCallback& callback) {
286 GetFaviconService()->GetFavicon(icon_url, icon_type, consumer, callback);
289 void FaviconHandler::GetFaviconForURL(
290 const GURL& page_url,
291 int icon_types,
292 CancelableRequestConsumerBase* consumer,
293 const FaviconService::FaviconDataCallback& callback) {
294 GetFaviconService()->GetFaviconForURL(page_url, icon_types, consumer,
295 callback);
298 void FaviconHandler::SetHistoryFavicon(
299 const GURL& page_url,
300 const GURL& icon_url,
301 const std::vector<unsigned char>& image_data,
302 history::IconType icon_type) {
303 GetFaviconService()->SetFavicon(page_url, icon_url, image_data, icon_type);
306 bool FaviconHandler::ShouldSaveFavicon(const GURL& url) {
307 if (!profile_->IsOffTheRecord())
308 return true;
310 // Otherwise store the favicon if the page is bookmarked.
311 BookmarkModel* bookmark_model = profile_->GetBookmarkModel();
312 return bookmark_model && bookmark_model->IsBookmarked(url);
315 void FaviconHandler::OnFaviconDataForInitialURL(
316 FaviconService::Handle handle,
317 history::FaviconData favicon) {
318 NavigationEntry* entry = GetEntry();
319 if (!entry)
320 return;
322 got_favicon_from_history_ = true;
323 history_icon_ = favicon;
325 favicon_expired_ = (favicon.known_icon && favicon.expired);
327 if (favicon.known_icon && favicon.icon_type == history::FAVICON &&
328 !entry->GetFavicon().valid &&
329 (!current_candidate() ||
330 DoUrlAndIconMatch(
331 *current_candidate(), favicon.icon_url, favicon.icon_type))) {
332 // The db knows the favicon (although it may be out of date) and the entry
333 // doesn't have an icon. Set the favicon now, and if the favicon turns out
334 // to be expired (or the wrong url) we'll fetch later on. This way the
335 // user doesn't see a flash of the default favicon.
336 entry->GetFavicon().url = favicon.icon_url;
337 if (favicon.is_valid())
338 UpdateFavicon(entry, favicon.image_data);
339 entry->GetFavicon().valid = true;
342 if (favicon.known_icon && !favicon.expired) {
343 if (current_candidate() &&
344 !DoUrlAndIconMatch(
345 *current_candidate(), favicon.icon_url, favicon.icon_type)) {
346 // Mapping in the database is wrong. DownloadFavIconOrAskHistory will
347 // update the mapping for this url and download the favicon if we don't
348 // already have it.
349 DownloadFaviconOrAskHistory(entry->GetURL(),
350 current_candidate()->icon_url,
351 static_cast<history::IconType>(current_candidate()->icon_type));
353 } else if (current_candidate()) {
354 // We know the official url for the favicon, by either don't have the
355 // favicon or its expired. Continue on to DownloadFaviconOrAskHistory to
356 // either download or check history again.
357 DownloadFaviconOrAskHistory(entry->GetURL(), current_candidate()->icon_url,
358 ToHistoryIconType(current_candidate()->icon_type));
360 // else we haven't got the icon url. When we get it we'll ask the
361 // renderer to download the icon.
364 void FaviconHandler::DownloadFaviconOrAskHistory(
365 const GURL& page_url,
366 const GURL& icon_url,
367 history::IconType icon_type) {
368 if (favicon_expired_) {
369 // We have the mapping, but the favicon is out of date. Download it now.
370 ScheduleDownload(page_url, icon_url, preferred_icon_size(), icon_type,
371 FaviconTabHelper::ImageDownloadCallback());
372 } else if (GetFaviconService()) {
373 // We don't know the favicon, but we may have previously downloaded the
374 // favicon for another page that shares the same favicon. Ask for the
375 // favicon given the favicon URL.
376 if (profile_->IsOffTheRecord()) {
377 GetFavicon(icon_url, icon_type, &cancelable_consumer_,
378 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)));
379 } else {
380 // Ask the history service for the icon. This does two things:
381 // 1. Attempts to fetch the favicon data from the database.
382 // 2. If the favicon exists in the database, this updates the database to
383 // include the mapping between the page url and the favicon url.
384 // This is asynchronous. The history service will call back when done.
385 // Issue the request and associate the current page ID with it.
386 UpdateFaviconMappingAndFetch(page_url, icon_url, icon_type,
387 &cancelable_consumer_,
388 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)));
393 void FaviconHandler::OnFaviconData(FaviconService::Handle handle,
394 history::FaviconData favicon) {
395 NavigationEntry* entry = GetEntry();
396 if (!entry)
397 return;
399 // No need to update the favicon url. By the time we get here
400 // UpdateFaviconURL will have set the favicon url.
401 if (favicon.icon_type == history::FAVICON) {
402 if (favicon.is_valid()) {
403 // There is a favicon, set it now. If expired we'll download the current
404 // one again, but at least the user will get some icon instead of the
405 // default and most likely the current one is fine anyway.
406 UpdateFavicon(entry, favicon.image_data);
408 if (!favicon.known_icon || favicon.expired) {
409 // We don't know the favicon, or it is out of date. Request the current
410 // one.
411 ScheduleDownload(entry->GetURL(), entry->GetFavicon().url,
412 preferred_icon_size(),
413 history::FAVICON,
414 FaviconTabHelper::ImageDownloadCallback());
416 } else if (current_candidate() && (!favicon.known_icon || favicon.expired ||
417 !(DoUrlAndIconMatch(
418 *current_candidate(), favicon.icon_url, favicon.icon_type)))) {
419 // We don't know the favicon, it is out of date or its type is not same as
420 // one got from page. Request the current one.
421 ScheduleDownload(entry->GetURL(), current_candidate()->icon_url,
422 preferred_icon_size(),
423 ToHistoryIconType(current_candidate()->icon_type),
424 FaviconTabHelper::ImageDownloadCallback());
426 history_icon_ = favicon;
429 int FaviconHandler::ScheduleDownload(
430 const GURL& url,
431 const GURL& image_url,
432 int image_size,
433 history::IconType icon_type,
434 const FaviconTabHelper::ImageDownloadCallback& callback) {
435 const int download_id = DownloadFavicon(image_url, image_size);
436 if (download_id) {
437 // Download ids should be unique.
438 DCHECK(download_requests_.find(download_id) == download_requests_.end());
439 download_requests_[download_id] =
440 DownloadRequest(url, image_url, callback, icon_type);
443 return download_id;
446 gfx::Image FaviconHandler::ResizeFaviconIfNeeded(const gfx::Image& image) {
447 // Get an SkBitmap from the gfx::Image.
448 SkBitmap bitmap = *image.ToSkBitmap();
449 int width = bitmap.width();
450 int height = bitmap.height();
451 if (width > 0 && height > 0) {
452 gfx::CalculateFaviconTargetSize(&width, &height);
453 return gfx::Image(skia::ImageOperations::Resize(
454 bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
455 width, height));
458 return image;