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