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 <algorithm> 8 #include <cmath> 9 #include <vector> 10 11 #include "base/bind.h" 12 #include "base/bind_helpers.h" 13 #include "base/memory/ref_counted_memory.h" 14 #include "build/build_config.h" 15 #include "chrome/browser/favicon/favicon_service.h" 16 #include "components/favicon/core/browser/favicon_client.h" 17 #include "components/favicon/core/favicon_driver.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_skia.h" 23 #include "ui/gfx/image/image_util.h" 24 25 using favicon::FaviconURL; 26 27 namespace { 28 29 // Size (along each axis) of a touch icon. This currently corresponds to 30 // the apple touch icon for iPad. 31 const int kTouchIconSize = 144; 32 33 // Get the maximal icon size in pixels for a icon of type |icon_type| for the 34 // current platform. 35 int 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 53 bool 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. 62 bool DoUrlsAndIconsMatch( 63 const FaviconURL& favicon_url, 64 const std::vector<favicon_base::FaviconRawBitmapResult>& 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 79 std::string UrlWithoutFragment(const GURL& gurl) { 80 GURL::Replacements replacements; 81 replacements.ClearRef(); 82 return gurl.ReplaceComponents(replacements).spec(); 83 } 84 85 bool 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. 90 bool IsExpired(const favicon_base::FaviconRawBitmapResult& bitmap_result) { 91 return bitmap_result.expired; 92 } 93 94 // Return true if |bitmap_result| is valid. 95 bool IsValid(const favicon_base::FaviconRawBitmapResult& 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 favicon_base::GetFaviconScales(). 102 bool HasExpiredOrIncompleteResult( 103 int desired_size_in_dip, 104 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { 105 // Check if at least one of the bitmaps is expired. 106 std::vector<favicon_base::FaviconRawBitmapResult>::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 favicon_base::GetFaviconScales() 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<float> favicon_scales = favicon_base::GetFaviconScales(); 127 for (size_t i = 0; i < favicon_scales.size(); ++i) { 128 int edge_size_in_pixel = std::ceil(desired_size_in_dip * favicon_scales[i]); 129 std::vector<gfx::Size>::iterator it = std::find(favicon_sizes.begin(), 130 favicon_sizes.end(), gfx::Size(edge_size_in_pixel, edge_size_in_pixel)); 131 if (it == favicon_sizes.end()) 132 return true; 133 } 134 return false; 135 } 136 137 // Returns true if at least one of |bitmap_results| is valid. 138 bool HasValidResult( 139 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { 140 return std::find_if(bitmap_results.begin(), bitmap_results.end(), IsValid) != 141 bitmap_results.end(); 142 } 143 144 // Returns the index of the entry with the largest area that is not larger than 145 // |max_area|; -1 if there is no such match. 146 int GetLargestSizeIndex(const std::vector<gfx::Size>& sizes, int max_area) { 147 DCHECK(!sizes.empty()); 148 int ret = -1; 149 for (size_t i = 0; i < sizes.size(); ++i) { 150 int area = sizes[i].GetArea(); 151 if ((ret == -1 || sizes[ret].GetArea() < area) && area <= max_area) 152 ret = i; 153 } 154 return ret; 155 } 156 157 // Return the index of a size which is same as the given |size|, -1 returned if 158 // there is no such bitmap. 159 int GetIndexBySize(const std::vector<gfx::Size>& sizes, 160 const gfx::Size& size) { 161 DCHECK(!sizes.empty()); 162 std::vector<gfx::Size>::const_iterator i = 163 std::find(sizes.begin(), sizes.end(), size); 164 if (i == sizes.end()) 165 return -1; 166 167 return static_cast<int>(i - sizes.begin()); 168 } 169 170 // Compare function used for std::stable_sort to sort as descend. 171 bool CompareIconSize(const FaviconURL& b1, const FaviconURL& b2) { 172 int area1 = 0; 173 if (!b1.icon_sizes.empty()) 174 area1 = b1.icon_sizes.front().GetArea(); 175 176 int area2 = 0; 177 if (!b2.icon_sizes.empty()) 178 area2 = b2.icon_sizes.front().GetArea(); 179 180 return area1 > area2; 181 } 182 183 } // namespace 184 185 //////////////////////////////////////////////////////////////////////////////// 186 187 FaviconHandler::DownloadRequest::DownloadRequest() 188 : icon_type(favicon_base::INVALID_ICON) {} 189 190 FaviconHandler::DownloadRequest::~DownloadRequest() { 191 } 192 193 FaviconHandler::DownloadRequest::DownloadRequest( 194 const GURL& url, 195 const GURL& image_url, 196 favicon_base::IconType icon_type) 197 : url(url), image_url(image_url), icon_type(icon_type) {} 198 199 //////////////////////////////////////////////////////////////////////////////// 200 201 FaviconHandler::FaviconCandidate::FaviconCandidate() 202 : score(0), icon_type(favicon_base::INVALID_ICON) {} 203 204 FaviconHandler::FaviconCandidate::~FaviconCandidate() { 205 } 206 207 FaviconHandler::FaviconCandidate::FaviconCandidate( 208 const GURL& url, 209 const GURL& image_url, 210 const gfx::Image& image, 211 float score, 212 favicon_base::IconType icon_type) 213 : url(url), 214 image_url(image_url), 215 image(image), 216 score(score), 217 icon_type(icon_type) {} 218 219 //////////////////////////////////////////////////////////////////////////////// 220 221 FaviconHandler::FaviconHandler(FaviconClient* client, 222 FaviconDriver* driver, 223 Type icon_type, 224 bool download_largest_icon) 225 : got_favicon_from_history_(false), 226 favicon_expired_or_incomplete_(false), 227 icon_types_(icon_type == FAVICON 228 ? favicon_base::FAVICON 229 : favicon_base::TOUCH_ICON | 230 favicon_base::TOUCH_PRECOMPOSED_ICON), 231 download_largest_icon_(download_largest_icon), 232 client_(client), 233 driver_(driver) { 234 DCHECK(driver_); 235 } 236 237 FaviconHandler::~FaviconHandler() { 238 } 239 240 void FaviconHandler::FetchFavicon(const GURL& url) { 241 cancelable_task_tracker_.TryCancelAll(); 242 243 url_ = url; 244 245 favicon_expired_or_incomplete_ = got_favicon_from_history_ = false; 246 image_urls_.clear(); 247 248 // Request the favicon from the history service. In parallel to this the 249 // renderer is going to notify us (well WebContents) when the favicon url is 250 // available. 251 if (client_->GetFaviconService()) { 252 GetFaviconForURLFromFaviconService( 253 url_, 254 icon_types_, 255 base::Bind( 256 &FaviconHandler::OnFaviconDataForInitialURLFromFaviconService, 257 base::Unretained(this)), 258 &cancelable_task_tracker_); 259 } 260 } 261 262 bool FaviconHandler::UpdateFaviconCandidate(const GURL& url, 263 const GURL& image_url, 264 const gfx::Image& image, 265 float score, 266 favicon_base::IconType icon_type) { 267 bool replace_best_favicon_candidate = false; 268 bool exact_match = false; 269 if (download_largest_icon_) { 270 replace_best_favicon_candidate = 271 image.Size().GetArea() > 272 best_favicon_candidate_.image.Size().GetArea(); 273 274 gfx::Size largest = best_favicon_candidate_.image.Size(); 275 if (replace_best_favicon_candidate) 276 largest = image.Size(); 277 278 // exact match (stop downloading next icon) only if 279 // - current candidate is only candidate. 280 // - next candidate doesn't have sizes attributes, in this case, the rest 281 // candidates don't have sizes attribute either, stop downloading now, 282 // otherwise, all favicon without sizes attribute are downloaded. 283 // - next candidate has sizes attribute and it is not larger than largest. 284 exact_match = image_urls_.size() == 1 || 285 image_urls_[1].icon_sizes.empty() || 286 image_urls_[1].icon_sizes[0].GetArea() < largest.GetArea(); 287 } else { 288 exact_match = score == 1 || preferred_icon_size() == 0; 289 replace_best_favicon_candidate = 290 exact_match || 291 best_favicon_candidate_.icon_type == favicon_base::INVALID_ICON || 292 score > best_favicon_candidate_.score; 293 } 294 if (replace_best_favicon_candidate) { 295 best_favicon_candidate_ = FaviconCandidate( 296 url, image_url, image, score, icon_type); 297 } 298 return exact_match; 299 } 300 301 void FaviconHandler::SetFavicon(const GURL& url, 302 const GURL& icon_url, 303 const gfx::Image& image, 304 favicon_base::IconType icon_type) { 305 if (client_->GetFaviconService() && ShouldSaveFavicon(url)) 306 SetHistoryFavicons(url, icon_url, icon_type, image); 307 308 if (UrlMatches(url, url_) && icon_type == favicon_base::FAVICON) { 309 if (!PageChangedSinceFaviconWasRequested()) 310 SetFaviconOnActivePage(icon_url, image); 311 } 312 } 313 314 void FaviconHandler::SetFaviconOnActivePage(const std::vector< 315 favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) { 316 gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs( 317 favicon_bitmap_results, 318 favicon_base::GetFaviconScales(), 319 preferred_icon_size()); 320 // The history service sends back results for a single icon URL, so it does 321 // not matter which result we get the |icon_url| from. 322 const GURL icon_url = favicon_bitmap_results.empty() ? 323 GURL() : favicon_bitmap_results[0].icon_url; 324 SetFaviconOnActivePage(icon_url, resized_image); 325 } 326 327 void FaviconHandler::SetFaviconOnActivePage(const GURL& icon_url, 328 const gfx::Image& image) { 329 // No matter what happens, we need to mark the favicon as being set. 330 driver_->SetActiveFaviconValidity(true); 331 332 bool icon_url_changed = driver_->GetActiveFaviconURL() != icon_url; 333 driver_->SetActiveFaviconURL(icon_url); 334 335 if (image.IsEmpty()) 336 return; 337 338 gfx::Image image_with_adjusted_colorspace = image; 339 favicon_base::SetFaviconColorSpace(&image_with_adjusted_colorspace); 340 341 driver_->SetActiveFaviconImage(image_with_adjusted_colorspace); 342 NotifyFaviconUpdated(icon_url_changed); 343 } 344 345 void FaviconHandler::OnUpdateFaviconURL( 346 const std::vector<FaviconURL>& candidates) { 347 image_urls_.clear(); 348 best_favicon_candidate_ = FaviconCandidate(); 349 for (std::vector<FaviconURL>::const_iterator i = candidates.begin(); 350 i != candidates.end(); ++i) { 351 if (!i->icon_url.is_empty() && (i->icon_type & icon_types_)) 352 image_urls_.push_back(*i); 353 } 354 355 if (!client_->GetFaviconService()) 356 return; 357 358 if (download_largest_icon_) 359 SortAndPruneImageUrls(); 360 361 // TODO(davemoore) Should clear on empty url. Currently we ignore it. 362 // This appears to be what FF does as well. 363 if (!image_urls_.empty()) 364 ProcessCurrentUrl(); 365 } 366 367 void FaviconHandler::ProcessCurrentUrl() { 368 DCHECK(!image_urls_.empty()); 369 370 // current_candidate() may return NULL if download_largest_icon_ is true and 371 // all the sizes are larger than the max. 372 if (PageChangedSinceFaviconWasRequested() || !current_candidate()) 373 return; 374 375 if (current_candidate()->icon_type == favicon_base::FAVICON) { 376 if (!favicon_expired_or_incomplete_ && 377 driver_->GetActiveFaviconValidity() && 378 DoUrlAndIconMatch(*current_candidate(), 379 driver_->GetActiveFaviconURL(), 380 favicon_base::FAVICON)) 381 return; 382 } else if (!favicon_expired_or_incomplete_ && got_favicon_from_history_ && 383 HasValidResult(history_results_) && 384 DoUrlsAndIconsMatch(*current_candidate(), history_results_)) { 385 return; 386 } 387 388 if (got_favicon_from_history_) 389 DownloadFaviconOrAskFaviconService(driver_->GetActiveURL(), 390 current_candidate()->icon_url, 391 current_candidate()->icon_type); 392 } 393 394 void FaviconHandler::OnDidDownloadFavicon( 395 int id, 396 const GURL& image_url, 397 const std::vector<SkBitmap>& bitmaps, 398 const std::vector<gfx::Size>& original_bitmap_sizes) { 399 DownloadRequests::iterator i = download_requests_.find(id); 400 if (i == download_requests_.end()) { 401 // Currently WebContents notifies us of ANY downloads so that it is 402 // possible to get here. 403 return; 404 } 405 406 if (current_candidate() && 407 DoUrlAndIconMatch(*current_candidate(), image_url, i->second.icon_type)) { 408 bool request_next_icon = true; 409 float score = 0.0f; 410 gfx::ImageSkia image_skia; 411 if (download_largest_icon_ && !bitmaps.empty()) { 412 int index = -1; 413 int max_size = GetMaximalIconSize(i->second.icon_type); 414 // Use the largest bitmap if FaviconURL doesn't have sizes attribute. 415 if (current_candidate()->icon_sizes.empty()) { 416 index = GetLargestSizeIndex(original_bitmap_sizes, max_size * max_size); 417 } else { 418 index = GetIndexBySize(original_bitmap_sizes, 419 current_candidate()->icon_sizes[0]); 420 // Find largest bitmap if there is no one exactly matched. 421 if (index == -1) { 422 index = GetLargestSizeIndex(original_bitmap_sizes, 423 max_size * max_size); 424 } 425 } 426 if (index != -1) 427 image_skia = gfx::ImageSkia(gfx::ImageSkiaRep(bitmaps[index], 1)); 428 } else { 429 image_skia = CreateFaviconImageSkia(bitmaps, 430 original_bitmap_sizes, 431 preferred_icon_size(), 432 &score); 433 } 434 435 if (!image_skia.isNull()) { 436 gfx::Image image(image_skia); 437 // The downloaded icon is still valid when there is no FaviconURL update 438 // during the downloading. 439 if (!bitmaps.empty()) { 440 request_next_icon = !UpdateFaviconCandidate( 441 i->second.url, image_url, image, score, i->second.icon_type); 442 } 443 } 444 if (request_next_icon && !PageChangedSinceFaviconWasRequested() && 445 image_urls_.size() > 1) { 446 // Remove the first member of image_urls_ and process the remaining. 447 image_urls_.erase(image_urls_.begin()); 448 ProcessCurrentUrl(); 449 } else if (best_favicon_candidate_.icon_type != 450 favicon_base::INVALID_ICON) { 451 // No more icons to request, set the favicon from the candidate. 452 SetFavicon(best_favicon_candidate_.url, 453 best_favicon_candidate_.image_url, 454 best_favicon_candidate_.image, 455 best_favicon_candidate_.icon_type); 456 // Reset candidate. 457 image_urls_.clear(); 458 best_favicon_candidate_ = FaviconCandidate(); 459 } 460 } 461 download_requests_.erase(i); 462 } 463 464 bool FaviconHandler::PageChangedSinceFaviconWasRequested() { 465 if (UrlMatches(driver_->GetActiveURL(), url_) && url_.is_valid()) { 466 return false; 467 } 468 // If the URL has changed out from under us (as will happen with redirects) 469 // return true. 470 return true; 471 } 472 473 int FaviconHandler::DownloadFavicon(const GURL& image_url, 474 int max_bitmap_size) { 475 if (!image_url.is_valid()) { 476 NOTREACHED(); 477 return 0; 478 } 479 return driver_->StartDownload(image_url, max_bitmap_size); 480 } 481 482 void FaviconHandler::UpdateFaviconMappingAndFetch( 483 const GURL& page_url, 484 const GURL& icon_url, 485 favicon_base::IconType icon_type, 486 const favicon_base::FaviconResultsCallback& callback, 487 base::CancelableTaskTracker* tracker) { 488 // TODO(pkotwicz): pass in all of |image_urls_| to 489 // UpdateFaviconMappingsAndFetch(). 490 std::vector<GURL> icon_urls; 491 icon_urls.push_back(icon_url); 492 client_->GetFaviconService()->UpdateFaviconMappingsAndFetch( 493 page_url, icon_urls, icon_type, preferred_icon_size(), callback, tracker); 494 } 495 496 void FaviconHandler::GetFaviconFromFaviconService( 497 const GURL& icon_url, 498 favicon_base::IconType icon_type, 499 const favicon_base::FaviconResultsCallback& callback, 500 base::CancelableTaskTracker* tracker) { 501 client_->GetFaviconService()->GetFavicon( 502 icon_url, icon_type, preferred_icon_size(), callback, tracker); 503 } 504 505 void FaviconHandler::GetFaviconForURLFromFaviconService( 506 const GURL& page_url, 507 int icon_types, 508 const favicon_base::FaviconResultsCallback& callback, 509 base::CancelableTaskTracker* tracker) { 510 client_->GetFaviconService()->GetFaviconForPageURL( 511 page_url, 512 icon_types, 513 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