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 = 144;
     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   return chrome::INVALID_ICON;
     51 }
     52 
     53 // Get the maximal icon size in pixels for a icon of type |icon_type| for the
     54 // current platform.
     55 int GetMaximalIconSize(chrome::IconType icon_type) {
     56   switch (icon_type) {
     57     case chrome::FAVICON:
     58 #if defined(OS_ANDROID)
     59       return 192;
     60 #else
     61       return gfx::ImageSkia::GetMaxSupportedScale() * gfx::kFaviconSize;
     62 #endif
     63     case chrome::TOUCH_ICON:
     64     case chrome::TOUCH_PRECOMPOSED_ICON:
     65       return kTouchIconSize;
     66     case chrome::INVALID_ICON:
     67       return 0;
     68   }
     69   NOTREACHED();
     70   return 0;
     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::GetImageScale(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   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     const std::vector<SkBitmap>& bitmaps,
    384     const std::vector<gfx::Size>& original_bitmap_sizes) {
    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,
    398                                          original_bitmap_sizes,
    399                                          scale_factors,
    400                                          preferred_icon_size(),
    401                                          &score));
    402 
    403     // The downloaded icon is still valid when there is no FaviconURL update
    404     // during the downloading.
    405     bool request_next_icon = true;
    406     if (!bitmaps.empty()) {
    407       request_next_icon = !UpdateFaviconCandidate(
    408           i->second.url, image_url, image, score, i->second.icon_type);
    409     }
    410     if (request_next_icon && GetEntry() && image_urls_.size() > 1) {
    411       // Remove the first member of image_urls_ and process the remaining.
    412       image_urls_.pop_front();
    413       ProcessCurrentUrl();
    414     } else if (favicon_candidate_.icon_type != chrome::INVALID_ICON) {
    415       // No more icons to request, set the favicon from the candidate.
    416       SetFavicon(favicon_candidate_.url,
    417                  favicon_candidate_.image_url,
    418                  favicon_candidate_.image,
    419                  favicon_candidate_.icon_type);
    420       // Reset candidate.
    421       image_urls_.clear();
    422       favicon_candidate_ = FaviconCandidate();
    423     }
    424   }
    425   download_requests_.erase(i);
    426 }
    427 
    428 NavigationEntry* FaviconHandler::GetEntry() {
    429   NavigationEntry* entry = delegate_->GetActiveEntry();
    430   if (entry && UrlMatches(entry->GetURL(), url_))
    431     return entry;
    432 
    433   // If the URL has changed out from under us (as will happen with redirects)
    434   // return NULL.
    435   return NULL;
    436 }
    437 
    438 int FaviconHandler::DownloadFavicon(const GURL& image_url,
    439                                     int max_bitmap_size) {
    440   if (!image_url.is_valid()) {
    441     NOTREACHED();
    442     return 0;
    443   }
    444   return delegate_->StartDownload(image_url, max_bitmap_size);
    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(page_url, icon_types,
    477                                           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::NotifyFaviconUpdated(bool icon_url_changed) {
    500   delegate_->NotifyFaviconUpdated(icon_url_changed);
    501 }
    502 
    503 void FaviconHandler::OnFaviconDataForInitialURL(
    504     const std::vector<chrome::FaviconBitmapResult>& favicon_bitmap_results) {
    505   NavigationEntry* entry = GetEntry();
    506   if (!entry)
    507     return;
    508 
    509   got_favicon_from_history_ = true;
    510   history_results_ = favicon_bitmap_results;
    511 
    512   bool has_results = !favicon_bitmap_results.empty();
    513   favicon_expired_or_incomplete_ = has_results && HasExpiredOrIncompleteResult(
    514       preferred_icon_size(), favicon_bitmap_results);
    515 
    516   if (has_results && icon_types_ == chrome::FAVICON &&
    517       !entry->GetFavicon().valid &&
    518       (!current_candidate() ||
    519        DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results))) {
    520     if (HasValidResult(favicon_bitmap_results)) {
    521       // The db knows the favicon (although it may be out of date) and the entry
    522       // doesn't have an icon. Set the favicon now, and if the favicon turns out
    523       // to be expired (or the wrong url) we'll fetch later on. This way the
    524       // user doesn't see a flash of the default favicon.
    525       UpdateFavicon(entry, favicon_bitmap_results);
    526     } else {
    527       // If |favicon_bitmap_results| does not have any valid results, treat the
    528       // favicon as if it's expired.
    529       // TODO(pkotwicz): Do something better.
    530       favicon_expired_or_incomplete_ = true;
    531     }
    532   }
    533 
    534   if (has_results && !favicon_expired_or_incomplete_) {
    535     if (current_candidate() &&
    536         !DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results)) {
    537       // Mapping in the database is wrong. DownloadFavIconOrAskHistory will
    538       // update the mapping for this url and download the favicon if we don't
    539       // already have it.
    540       DownloadFaviconOrAskHistory(entry->GetURL(),
    541           current_candidate()->icon_url,
    542           static_cast<chrome::IconType>(current_candidate()->icon_type));
    543     }
    544   } else if (current_candidate()) {
    545     // We know the official url for the favicon, by either don't have the
    546     // favicon or its expired. Continue on to DownloadFaviconOrAskHistory to
    547     // either download or check history again.
    548     DownloadFaviconOrAskHistory(entry->GetURL(), current_candidate()->icon_url,
    549         ToHistoryIconType(current_candidate()->icon_type));
    550   }
    551   // else we haven't got the icon url. When we get it we'll ask the
    552   // renderer to download the icon.
    553 }
    554 
    555 void FaviconHandler::DownloadFaviconOrAskHistory(
    556     const GURL& page_url,
    557     const GURL& icon_url,
    558     chrome::IconType icon_type) {
    559   if (favicon_expired_or_incomplete_) {
    560     // We have the mapping, but the favicon is out of date. Download it now.
    561     ScheduleDownload(page_url, icon_url, icon_type);
    562   } else if (GetFaviconService()) {
    563     // We don't know the favicon, but we may have previously downloaded the
    564     // favicon for another page that shares the same favicon. Ask for the
    565     // favicon given the favicon URL.
    566     if (profile_->IsOffTheRecord()) {
    567       GetFavicon(
    568           icon_url, icon_type,
    569           base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)),
    570           &cancelable_task_tracker_);
    571     } else {
    572       // Ask the history service for the icon. This does two things:
    573       // 1. Attempts to fetch the favicon data from the database.
    574       // 2. If the favicon exists in the database, this updates the database to
    575       //    include the mapping between the page url and the favicon url.
    576       // This is asynchronous. The history service will call back when done.
    577       // Issue the request and associate the current page ID with it.
    578       UpdateFaviconMappingAndFetch(
    579           page_url, icon_url, icon_type,
    580           base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)),
    581           &cancelable_task_tracker_);
    582     }
    583   }
    584 }
    585 
    586 void FaviconHandler::OnFaviconData(
    587     const std::vector<chrome::FaviconBitmapResult>& favicon_bitmap_results) {
    588   NavigationEntry* entry = GetEntry();
    589   if (!entry)
    590     return;
    591 
    592   bool has_results = !favicon_bitmap_results.empty();
    593   bool has_expired_or_incomplete_result = HasExpiredOrIncompleteResult(
    594       preferred_icon_size(), favicon_bitmap_results);
    595 
    596   if (has_results && icon_types_ == chrome::FAVICON) {
    597     if (HasValidResult(favicon_bitmap_results)) {
    598       // There is a favicon, set it now. If expired we'll download the current
    599       // one again, but at least the user will get some icon instead of the
    600       // default and most likely the current one is fine anyway.
    601       UpdateFavicon(entry, favicon_bitmap_results);
    602     }
    603     if (has_expired_or_incomplete_result) {
    604       // The favicon is out of date. Request the current one.
    605       ScheduleDownload(entry->GetURL(), entry->GetFavicon().url,
    606                        chrome::FAVICON);
    607     }
    608   } else if (current_candidate() &&
    609       (!has_results || has_expired_or_incomplete_result ||
    610        !(DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results)))) {
    611     // We don't know the favicon, it is out of date or its type is not same as
    612     // one got from page. Request the current one.
    613     ScheduleDownload(entry->GetURL(), current_candidate()->icon_url,
    614         ToHistoryIconType(current_candidate()->icon_type));
    615   }
    616   history_results_ = favicon_bitmap_results;
    617 }
    618 
    619 int FaviconHandler::ScheduleDownload(
    620     const GURL& url,
    621     const GURL& image_url,
    622     chrome::IconType icon_type) {
    623   // A max bitmap size is specified to avoid receiving huge bitmaps in
    624   // OnDidDownloadFavicon(). See FaviconHandlerDelegate::StartDownload()
    625   // for more details about the max bitmap size.
    626   const int download_id = DownloadFavicon(image_url,
    627       GetMaximalIconSize(icon_type));
    628   if (download_id) {
    629     // Download ids should be unique.
    630     DCHECK(download_requests_.find(download_id) == download_requests_.end());
    631     download_requests_[download_id] =
    632         DownloadRequest(url, image_url, icon_type);
    633   }
    634 
    635   return download_id;
    636 }
    637