favicon_handler.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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.
4
5#include "chrome/browser/favicon/favicon_handler.h"
6
7#include "build/build_config.h"
8
9#include <algorithm>
10#include <vector>
11
12#include "base/bind.h"
13#include "base/bind_helpers.h"
14#include "base/memory/ref_counted_memory.h"
15#include "chrome/browser/favicon/favicon_service.h"
16#include "chrome/browser/favicon/favicon_service_factory.h"
17#include "chrome/browser/favicon/favicon_util.h"
18#include "components/favicon_base/select_favicon_frames.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_skia.h"
23#include "ui/gfx/image/image_util.h"
24
25using favicon::FaviconURL;
26
27namespace {
28
29// Size (along each axis) of a touch icon. This currently corresponds to
30// the apple touch icon for iPad.
31const int kTouchIconSize = 144;
32
33// Get the maximal icon size in pixels for a icon of type |icon_type| for the
34// current platform.
35int GetMaximalIconSize(favicon_base::IconType icon_type) {
36  switch (icon_type) {
37    case favicon_base::FAVICON:
38#if defined(OS_ANDROID)
39      return 192;
40#else
41      return gfx::ImageSkia::GetMaxSupportedScale() * gfx::kFaviconSize;
42#endif
43    case favicon_base::TOUCH_ICON:
44    case favicon_base::TOUCH_PRECOMPOSED_ICON:
45      return kTouchIconSize;
46    case favicon_base::INVALID_ICON:
47      return 0;
48  }
49  NOTREACHED();
50  return 0;
51}
52
53bool DoUrlAndIconMatch(const FaviconURL& favicon_url,
54                       const GURL& url,
55                       favicon_base::IconType icon_type) {
56  return favicon_url.icon_url == url && favicon_url.icon_type == icon_type;
57}
58
59// Returns true if all of the icon URLs and icon types in |bitmap_results| are
60// identical and if they match the icon URL and icon type in |favicon_url|.
61// Returns false if |bitmap_results| is empty.
62bool DoUrlsAndIconsMatch(
63    const FaviconURL& favicon_url,
64    const std::vector<favicon_base::FaviconBitmapResult>& bitmap_results) {
65  if (bitmap_results.empty())
66    return false;
67
68  const favicon_base::IconType icon_type = favicon_url.icon_type;
69
70  for (size_t i = 0; i < bitmap_results.size(); ++i) {
71    if (favicon_url.icon_url != bitmap_results[i].icon_url ||
72        icon_type != bitmap_results[i].icon_type) {
73      return false;
74    }
75  }
76  return true;
77}
78
79std::string UrlWithoutFragment(const GURL& gurl) {
80  GURL::Replacements replacements;
81  replacements.ClearRef();
82  return gurl.ReplaceComponents(replacements).spec();
83}
84
85bool UrlMatches(const GURL& gurl_a, const GURL& gurl_b) {
86  return UrlWithoutFragment(gurl_a) == UrlWithoutFragment(gurl_b);
87}
88
89// Return true if |bitmap_result| is expired.
90bool IsExpired(const favicon_base::FaviconBitmapResult& bitmap_result) {
91  return bitmap_result.expired;
92}
93
94// Return true if |bitmap_result| is valid.
95bool IsValid(const favicon_base::FaviconBitmapResult& bitmap_result) {
96  return bitmap_result.is_valid();
97}
98
99// Returns true if at least one of the bitmaps in |bitmap_results| is expired or
100// if |bitmap_results| is missing favicons for |desired_size_in_dip| and one of
101// the scale factors in FaviconUtil::GetFaviconScaleFactors().
102bool HasExpiredOrIncompleteResult(
103    int desired_size_in_dip,
104    const std::vector<favicon_base::FaviconBitmapResult>& bitmap_results) {
105  // Check if at least one of the bitmaps is expired.
106  std::vector<favicon_base::FaviconBitmapResult>::const_iterator it =
107      std::find_if(bitmap_results.begin(), bitmap_results.end(), IsExpired);
108  if (it != bitmap_results.end())
109    return true;
110
111  // Any favicon size is good if the desired size is 0.
112  if (desired_size_in_dip == 0)
113    return false;
114
115  // Check if the favicon for at least one of the scale factors is missing.
116  // |bitmap_results| should always be complete for data inserted by
117  // FaviconHandler as the FaviconHandler stores favicons resized to all
118  // of FaviconUtil::GetFaviconScaleFactors() into the history backend.
119  // Examples of when |bitmap_results| can be incomplete:
120  // - Favicons inserted into the history backend by sync.
121  // - Favicons for imported bookmarks.
122  std::vector<gfx::Size> favicon_sizes;
123  for (size_t i = 0; i < bitmap_results.size(); ++i)
124    favicon_sizes.push_back(bitmap_results[i].pixel_size);
125
126  std::vector<ui::ScaleFactor> scale_factors =
127      FaviconUtil::GetFaviconScaleFactors();
128  for (size_t i = 0; i < scale_factors.size(); ++i) {
129    int edge_size_in_pixel = floor(
130        desired_size_in_dip * ui::GetImageScale(scale_factors[i]));
131    std::vector<gfx::Size>::iterator it = std::find(favicon_sizes.begin(),
132        favicon_sizes.end(), gfx::Size(edge_size_in_pixel, edge_size_in_pixel));
133    if (it == favicon_sizes.end())
134      return true;
135  }
136  return false;
137}
138
139// Returns true if at least one of |bitmap_results| is valid.
140bool HasValidResult(
141    const std::vector<favicon_base::FaviconBitmapResult>& bitmap_results) {
142  return std::find_if(bitmap_results.begin(), bitmap_results.end(), IsValid) !=
143      bitmap_results.end();
144}
145
146// Returns the index of the entry with the largest area that is not larger than
147// |max_area|; -1 if there is no such match.
148int GetLargestSizeIndex(const std::vector<gfx::Size>& sizes, int max_area) {
149  DCHECK(!sizes.empty());
150  int ret = -1;
151  for (size_t i = 0; i < sizes.size(); ++i) {
152    int area = sizes[i].GetArea();
153    if ((ret == -1 || sizes[ret].GetArea() < area) && area <= max_area)
154      ret = i;
155  }
156  return ret;
157}
158
159// Return the index of a size which is same as the given |size|, -1 returned if
160// there is no such bitmap.
161int GetIndexBySize(const std::vector<gfx::Size>& sizes,
162                   const gfx::Size& size) {
163  DCHECK(!sizes.empty());
164  std::vector<gfx::Size>::const_iterator i =
165      std::find(sizes.begin(), sizes.end(), size);
166  if (i == sizes.end())
167    return -1;
168
169  return static_cast<int>(i - sizes.begin());
170}
171
172// Compare function used for std::stable_sort to sort as descend.
173bool CompareIconSize(const FaviconURL& b1, const FaviconURL& b2) {
174  int area1 = 0;
175  if (!b1.icon_sizes.empty())
176    area1 = b1.icon_sizes.front().GetArea();
177
178  int area2 = 0;
179  if (!b2.icon_sizes.empty())
180    area2 = b2.icon_sizes.front().GetArea();
181
182  return area1 > area2;
183}
184
185}  // namespace
186
187////////////////////////////////////////////////////////////////////////////////
188
189FaviconHandler::DownloadRequest::DownloadRequest()
190    : icon_type(favicon_base::INVALID_ICON) {}
191
192FaviconHandler::DownloadRequest::~DownloadRequest() {
193}
194
195FaviconHandler::DownloadRequest::DownloadRequest(
196    const GURL& url,
197    const GURL& image_url,
198    favicon_base::IconType icon_type)
199    : url(url), image_url(image_url), icon_type(icon_type) {}
200
201////////////////////////////////////////////////////////////////////////////////
202
203FaviconHandler::FaviconCandidate::FaviconCandidate()
204    : score(0), icon_type(favicon_base::INVALID_ICON) {}
205
206FaviconHandler::FaviconCandidate::~FaviconCandidate() {
207}
208
209FaviconHandler::FaviconCandidate::FaviconCandidate(
210    const GURL& url,
211    const GURL& image_url,
212    const gfx::Image& image,
213    float score,
214    favicon_base::IconType icon_type)
215    : url(url),
216      image_url(image_url),
217      image(image),
218      score(score),
219      icon_type(icon_type) {}
220
221////////////////////////////////////////////////////////////////////////////////
222
223FaviconHandler::FaviconHandler(FaviconClient* client,
224                               FaviconDriver* driver,
225                               Type icon_type,
226                               bool download_largest_icon)
227    : got_favicon_from_history_(false),
228      favicon_expired_or_incomplete_(false),
229      icon_types_(icon_type == FAVICON
230                      ? favicon_base::FAVICON
231                      : favicon_base::TOUCH_ICON |
232                            favicon_base::TOUCH_PRECOMPOSED_ICON),
233      download_largest_icon_(download_largest_icon),
234      client_(client),
235      driver_(driver) {
236  DCHECK(driver_);
237}
238
239FaviconHandler::~FaviconHandler() {
240}
241
242void FaviconHandler::FetchFavicon(const GURL& url) {
243  cancelable_task_tracker_.TryCancelAll();
244
245  url_ = url;
246
247  favicon_expired_or_incomplete_ = got_favicon_from_history_ = false;
248  image_urls_.clear();
249
250  // Request the favicon from the history service. In parallel to this the
251  // renderer is going to notify us (well WebContents) when the favicon url is
252  // available.
253  if (client_->GetFaviconService()) {
254    GetFaviconForURLFromFaviconService(
255        url_,
256        icon_types_,
257        base::Bind(
258            &FaviconHandler::OnFaviconDataForInitialURLFromFaviconService,
259            base::Unretained(this)),
260        &cancelable_task_tracker_);
261  }
262}
263
264bool FaviconHandler::UpdateFaviconCandidate(const GURL& url,
265                                            const GURL& image_url,
266                                            const gfx::Image& image,
267                                            float score,
268                                            favicon_base::IconType icon_type) {
269  bool replace_best_favicon_candidate = false;
270  bool exact_match = false;
271  if (download_largest_icon_) {
272    replace_best_favicon_candidate =
273        image.Size().GetArea() >
274        best_favicon_candidate_.image.Size().GetArea();
275
276    gfx::Size largest = best_favicon_candidate_.image.Size();
277    if (replace_best_favicon_candidate)
278      largest = image.Size();
279
280    // exact match (stop downloading next icon) only if
281    // - current candidate is only candidate.
282    // - next candidate doesn't have sizes attributes, in this case, the rest
283    //   candidates don't have sizes attribute either, stop downloading now,
284    //   otherwise, all favicon without sizes attribute are downloaded.
285    // - next candidate has sizes attribute and it is not larger than largest.
286    exact_match = image_urls_.size() == 1 ||
287        image_urls_[1].icon_sizes.empty() ||
288        image_urls_[1].icon_sizes[0].GetArea() < largest.GetArea();
289  } else {
290    exact_match = score == 1 || preferred_icon_size() == 0;
291    replace_best_favicon_candidate =
292        exact_match ||
293        best_favicon_candidate_.icon_type == favicon_base::INVALID_ICON ||
294        score > best_favicon_candidate_.score;
295  }
296  if (replace_best_favicon_candidate) {
297    best_favicon_candidate_ = FaviconCandidate(
298        url, image_url, image, score, icon_type);
299  }
300  return exact_match;
301}
302
303void FaviconHandler::SetFavicon(const GURL& url,
304                                const GURL& icon_url,
305                                const gfx::Image& image,
306                                favicon_base::IconType icon_type) {
307  if (client_->GetFaviconService() && ShouldSaveFavicon(url))
308    SetHistoryFavicons(url, icon_url, icon_type, image);
309
310  if (UrlMatches(url, url_) && icon_type == favicon_base::FAVICON) {
311    if (!PageChangedSinceFaviconWasRequested())
312      SetFaviconOnActivePage(icon_url, image);
313  }
314}
315
316void FaviconHandler::SetFaviconOnActivePage(const std::vector<
317    favicon_base::FaviconBitmapResult>& favicon_bitmap_results) {
318  gfx::Image resized_image = FaviconUtil::SelectFaviconFramesFromPNGs(
319      favicon_bitmap_results,
320      FaviconUtil::GetFaviconScaleFactors(),
321      preferred_icon_size());
322  // The history service sends back results for a single icon URL, so it does
323  // not matter which result we get the |icon_url| from.
324  const GURL icon_url = favicon_bitmap_results.empty() ?
325      GURL() : favicon_bitmap_results[0].icon_url;
326  SetFaviconOnActivePage(icon_url, resized_image);
327}
328
329void FaviconHandler::SetFaviconOnActivePage(const GURL& icon_url,
330                                            const gfx::Image& image) {
331  // No matter what happens, we need to mark the favicon as being set.
332  driver_->SetActiveFaviconValidity(true);
333
334  bool icon_url_changed = driver_->GetActiveFaviconURL() != icon_url;
335  driver_->SetActiveFaviconURL(icon_url);
336
337  if (image.IsEmpty())
338    return;
339
340  gfx::Image image_with_adjusted_colorspace = image;
341  FaviconUtil::SetFaviconColorSpace(&image_with_adjusted_colorspace);
342
343  driver_->SetActiveFaviconImage(image_with_adjusted_colorspace);
344  NotifyFaviconUpdated(icon_url_changed);
345}
346
347void FaviconHandler::OnUpdateFaviconURL(
348    const std::vector<FaviconURL>& candidates) {
349  image_urls_.clear();
350  best_favicon_candidate_ = FaviconCandidate();
351  for (std::vector<FaviconURL>::const_iterator i = candidates.begin();
352       i != candidates.end(); ++i) {
353    if (!i->icon_url.is_empty() && (i->icon_type & icon_types_))
354      image_urls_.push_back(*i);
355  }
356
357  if (!client_->GetFaviconService())
358    return;
359
360  if (download_largest_icon_)
361    SortAndPruneImageUrls();
362
363  // TODO(davemoore) Should clear on empty url. Currently we ignore it.
364  // This appears to be what FF does as well.
365  if (!image_urls_.empty())
366    ProcessCurrentUrl();
367}
368
369void FaviconHandler::ProcessCurrentUrl() {
370  DCHECK(!image_urls_.empty());
371
372  // current_candidate() may return NULL if download_largest_icon_ is true and
373  // all the sizes are larger than the max.
374  if (PageChangedSinceFaviconWasRequested() || !current_candidate())
375    return;
376
377  if (current_candidate()->icon_type == favicon_base::FAVICON) {
378    if (!favicon_expired_or_incomplete_ &&
379        driver_->GetActiveFaviconValidity() &&
380        DoUrlAndIconMatch(*current_candidate(),
381                          driver_->GetActiveFaviconURL(),
382                          favicon_base::FAVICON))
383      return;
384  } else if (!favicon_expired_or_incomplete_ && got_favicon_from_history_ &&
385             HasValidResult(history_results_) &&
386             DoUrlsAndIconsMatch(*current_candidate(), history_results_)) {
387    return;
388  }
389
390  if (got_favicon_from_history_)
391    DownloadFaviconOrAskFaviconService(driver_->GetActiveURL(),
392                                       current_candidate()->icon_url,
393                                       current_candidate()->icon_type);
394}
395
396void FaviconHandler::OnDidDownloadFavicon(
397    int id,
398    const GURL& image_url,
399    const std::vector<SkBitmap>& bitmaps,
400    const std::vector<gfx::Size>& original_bitmap_sizes) {
401  DownloadRequests::iterator i = download_requests_.find(id);
402  if (i == download_requests_.end()) {
403    // Currently WebContents notifies us of ANY downloads so that it is
404    // possible to get here.
405    return;
406  }
407
408  if (current_candidate() &&
409      DoUrlAndIconMatch(*current_candidate(), image_url, i->second.icon_type)) {
410    bool request_next_icon = true;
411    float score = 0.0f;
412    gfx::ImageSkia image_skia;
413    if (download_largest_icon_ && !bitmaps.empty()) {
414      int index = -1;
415      int max_size = GetMaximalIconSize(i->second.icon_type);
416      // Use the largest bitmap if FaviconURL doesn't have sizes attribute.
417      if (current_candidate()->icon_sizes.empty()) {
418        index = GetLargestSizeIndex(original_bitmap_sizes, max_size * max_size);
419      } else {
420        index = GetIndexBySize(original_bitmap_sizes,
421                               current_candidate()->icon_sizes[0]);
422        // Find largest bitmap if there is no one exactly matched.
423        if (index == -1) {
424          index = GetLargestSizeIndex(original_bitmap_sizes,
425                                      max_size * max_size);
426        }
427      }
428      if (index != -1)
429        image_skia = gfx::ImageSkia(gfx::ImageSkiaRep(bitmaps[index], 1));
430    } else {
431      std::vector<ui::ScaleFactor> scale_factors =
432          FaviconUtil::GetFaviconScaleFactors();
433      image_skia = SelectFaviconFrames(bitmaps,
434                                       original_bitmap_sizes,
435                                       scale_factors,
436                                       preferred_icon_size(),
437                                       &score);
438    }
439
440    if (!image_skia.isNull()) {
441      gfx::Image image(image_skia);
442      // The downloaded icon is still valid when there is no FaviconURL update
443      // during the downloading.
444      if (!bitmaps.empty()) {
445        request_next_icon = !UpdateFaviconCandidate(
446            i->second.url, image_url, image, score, i->second.icon_type);
447      }
448    }
449    if (request_next_icon && !PageChangedSinceFaviconWasRequested() &&
450        image_urls_.size() > 1) {
451      // Remove the first member of image_urls_ and process the remaining.
452      image_urls_.erase(image_urls_.begin());
453      ProcessCurrentUrl();
454    } else if (best_favicon_candidate_.icon_type !=
455               favicon_base::INVALID_ICON) {
456      // No more icons to request, set the favicon from the candidate.
457      SetFavicon(best_favicon_candidate_.url,
458                 best_favicon_candidate_.image_url,
459                 best_favicon_candidate_.image,
460                 best_favicon_candidate_.icon_type);
461      // Reset candidate.
462      image_urls_.clear();
463      best_favicon_candidate_ = FaviconCandidate();
464    }
465  }
466  download_requests_.erase(i);
467}
468
469bool FaviconHandler::PageChangedSinceFaviconWasRequested() {
470  if (UrlMatches(driver_->GetActiveURL(), url_) && url_.is_valid()) {
471    return false;
472  }
473  // If the URL has changed out from under us (as will happen with redirects)
474  // return true.
475  return true;
476}
477
478int FaviconHandler::DownloadFavicon(const GURL& image_url,
479                                    int max_bitmap_size) {
480  if (!image_url.is_valid()) {
481    NOTREACHED();
482    return 0;
483  }
484  return driver_->StartDownload(image_url, max_bitmap_size);
485}
486
487void FaviconHandler::UpdateFaviconMappingAndFetch(
488    const GURL& page_url,
489    const GURL& icon_url,
490    favicon_base::IconType icon_type,
491    const favicon_base::FaviconResultsCallback& callback,
492    base::CancelableTaskTracker* tracker) {
493  // TODO(pkotwicz): pass in all of |image_urls_| to
494  // UpdateFaviconMappingsAndFetch().
495  std::vector<GURL> icon_urls;
496  icon_urls.push_back(icon_url);
497  client_->GetFaviconService()->UpdateFaviconMappingsAndFetch(
498      page_url, icon_urls, icon_type, preferred_icon_size(), callback, tracker);
499}
500
501void FaviconHandler::GetFaviconFromFaviconService(
502    const GURL& icon_url,
503    favicon_base::IconType icon_type,
504    const favicon_base::FaviconResultsCallback& callback,
505    base::CancelableTaskTracker* tracker) {
506  client_->GetFaviconService()->GetFavicon(
507      icon_url, icon_type, preferred_icon_size(), callback, tracker);
508}
509
510void FaviconHandler::GetFaviconForURLFromFaviconService(
511    const GURL& page_url,
512    int icon_types,
513    const favicon_base::FaviconResultsCallback& callback,
514    base::CancelableTaskTracker* tracker) {
515  client_->GetFaviconService()->GetFaviconForURL(
516      FaviconService::FaviconForURLParams(
517          page_url, icon_types, preferred_icon_size()),
518      callback,
519      tracker);
520}
521
522void FaviconHandler::SetHistoryFavicons(const GURL& page_url,
523                                        const GURL& icon_url,
524                                        favicon_base::IconType icon_type,
525                                        const gfx::Image& image) {
526  client_->GetFaviconService()->SetFavicons(
527      page_url, icon_url, icon_type, image);
528}
529
530bool FaviconHandler::ShouldSaveFavicon(const GURL& url) {
531  if (!driver_->IsOffTheRecord())
532    return true;
533
534  // Otherwise store the favicon if the page is bookmarked.
535  return client_->IsBookmarked(url);
536}
537
538void FaviconHandler::NotifyFaviconUpdated(bool icon_url_changed) {
539  driver_->NotifyFaviconUpdated(icon_url_changed);
540}
541
542void FaviconHandler::OnFaviconDataForInitialURLFromFaviconService(
543    const std::vector<favicon_base::FaviconBitmapResult>&
544        favicon_bitmap_results) {
545  if (PageChangedSinceFaviconWasRequested())
546    return;
547  got_favicon_from_history_ = true;
548  history_results_ = favicon_bitmap_results;
549  bool has_results = !favicon_bitmap_results.empty();
550  favicon_expired_or_incomplete_ = has_results && HasExpiredOrIncompleteResult(
551      preferred_icon_size(), favicon_bitmap_results);
552  if (has_results && icon_types_ == favicon_base::FAVICON &&
553      !driver_->GetActiveFaviconValidity() &&
554      (!current_candidate() ||
555       DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results))) {
556    if (HasValidResult(favicon_bitmap_results)) {
557      // The db knows the favicon (although it may be out of date) and the entry
558      // doesn't have an icon. Set the favicon now, and if the favicon turns out
559      // to be expired (or the wrong url) we'll fetch later on. This way the
560      // user doesn't see a flash of the default favicon.
561      SetFaviconOnActivePage(favicon_bitmap_results);
562    } else {
563      // If |favicon_bitmap_results| does not have any valid results, treat the
564      // favicon as if it's expired.
565      // TODO(pkotwicz): Do something better.
566      favicon_expired_or_incomplete_ = true;
567    }
568  }
569  if (has_results && !favicon_expired_or_incomplete_) {
570    if (current_candidate() &&
571        !DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results)) {
572      // Mapping in the database is wrong. DownloadFavIconOrAskHistory will
573      // update the mapping for this url and download the favicon if we don't
574      // already have it.
575      DownloadFaviconOrAskFaviconService(driver_->GetActiveURL(),
576                                         current_candidate()->icon_url,
577                                         current_candidate()->icon_type);
578    }
579  } else if (current_candidate()) {
580    // We know the official url for the favicon, but either don't have the
581    // favicon or it's expired. Continue on to DownloadFaviconOrAskHistory to
582    // either download or check history again.
583    DownloadFaviconOrAskFaviconService(driver_->GetActiveURL(),
584                                       current_candidate()->icon_url,
585                                       current_candidate()->icon_type);
586  }
587  // else we haven't got the icon url. When we get it we'll ask the
588  // renderer to download the icon.
589}
590
591void FaviconHandler::DownloadFaviconOrAskFaviconService(
592    const GURL& page_url,
593    const GURL& icon_url,
594    favicon_base::IconType icon_type) {
595  if (favicon_expired_or_incomplete_) {
596    // We have the mapping, but the favicon is out of date. Download it now.
597    ScheduleDownload(page_url, icon_url, icon_type);
598  } else if (client_->GetFaviconService()) {
599    // We don't know the favicon, but we may have previously downloaded the
600    // favicon for another page that shares the same favicon. Ask for the
601    // favicon given the favicon URL.
602    if (driver_->IsOffTheRecord()) {
603      GetFaviconFromFaviconService(
604          icon_url, icon_type,
605          base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)),
606          &cancelable_task_tracker_);
607    } else {
608      // Ask the history service for the icon. This does two things:
609      // 1. Attempts to fetch the favicon data from the database.
610      // 2. If the favicon exists in the database, this updates the database to
611      //    include the mapping between the page url and the favicon url.
612      // This is asynchronous. The history service will call back when done.
613      UpdateFaviconMappingAndFetch(
614          page_url, icon_url, icon_type,
615          base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)),
616          &cancelable_task_tracker_);
617    }
618  }
619}
620
621void FaviconHandler::OnFaviconData(const std::vector<
622    favicon_base::FaviconBitmapResult>& favicon_bitmap_results) {
623  if (PageChangedSinceFaviconWasRequested())
624    return;
625
626  bool has_results = !favicon_bitmap_results.empty();
627  bool has_expired_or_incomplete_result = HasExpiredOrIncompleteResult(
628      preferred_icon_size(), favicon_bitmap_results);
629
630  if (has_results && icon_types_ == favicon_base::FAVICON) {
631    if (HasValidResult(favicon_bitmap_results)) {
632      // There is a favicon, set it now. If expired we'll download the current
633      // one again, but at least the user will get some icon instead of the
634      // default and most likely the current one is fine anyway.
635      SetFaviconOnActivePage(favicon_bitmap_results);
636    }
637    if (has_expired_or_incomplete_result) {
638      // The favicon is out of date. Request the current one.
639      ScheduleDownload(driver_->GetActiveURL(),
640                       driver_->GetActiveFaviconURL(),
641                       favicon_base::FAVICON);
642    }
643  } else if (current_candidate() &&
644      (!has_results || has_expired_or_incomplete_result ||
645       !(DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results)))) {
646    // We don't know the favicon, it is out of date or its type is not same as
647    // one got from page. Request the current one.
648    ScheduleDownload(driver_->GetActiveURL(),
649                     current_candidate()->icon_url,
650                     current_candidate()->icon_type);
651  }
652  history_results_ = favicon_bitmap_results;
653}
654
655int FaviconHandler::ScheduleDownload(const GURL& url,
656                                     const GURL& image_url,
657                                     favicon_base::IconType icon_type) {
658  // A max bitmap size is specified to avoid receiving huge bitmaps in
659  // OnDidDownloadFavicon(). See FaviconDriver::StartDownload()
660  // for more details about the max bitmap size.
661  const int download_id = DownloadFavicon(image_url,
662                                          GetMaximalIconSize(icon_type));
663  if (download_id) {
664    // Download ids should be unique.
665    DCHECK(download_requests_.find(download_id) == download_requests_.end());
666    download_requests_[download_id] =
667        DownloadRequest(url, image_url, icon_type);
668  }
669
670  return download_id;
671}
672
673void FaviconHandler::SortAndPruneImageUrls() {
674  for (std::vector<FaviconURL>::iterator i = image_urls_.begin();
675       i != image_urls_.end();) {
676    if (i->icon_sizes.empty()) {
677      ++i;
678      continue;
679    }
680    int max_size = GetMaximalIconSize(i->icon_type);
681    int index = GetLargestSizeIndex(i->icon_sizes, max_size * max_size);
682    if (index == -1) {
683      i = image_urls_.erase(i);
684    } else {
685      gfx::Size largest = i->icon_sizes[index];
686      i->icon_sizes.clear();
687      i->icon_sizes.push_back(largest);
688      ++i;
689    }
690  }
691  std::stable_sort(image_urls_.begin(), image_urls_.end(),
692                   CompareIconSize);
693}
694