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