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