OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/favicon/core/favicon_handler.h" | 5 #include "components/favicon/core/favicon_handler.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <utility> | 9 #include <utility> |
10 #include <vector> | 10 #include <vector> |
11 | 11 |
12 #include "base/bind.h" | 12 #include "base/bind.h" |
13 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
| 14 #include "base/feature_list.h" |
14 #include "base/memory/ref_counted_memory.h" | 15 #include "base/memory/ref_counted_memory.h" |
15 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
16 #include "build/build_config.h" | 17 #include "build/build_config.h" |
17 #include "components/favicon/core/favicon_service.h" | 18 #include "components/favicon/core/favicon_service.h" |
18 #include "components/favicon_base/favicon_util.h" | 19 #include "components/favicon_base/favicon_util.h" |
19 #include "components/favicon_base/select_favicon_frames.h" | 20 #include "components/favicon_base/select_favicon_frames.h" |
20 #include "skia/ext/image_operations.h" | 21 #include "skia/ext/image_operations.h" |
21 #include "ui/gfx/codec/png_codec.h" | 22 #include "ui/gfx/codec/png_codec.h" |
22 #include "ui/gfx/image/image_skia.h" | 23 #include "ui/gfx/image/image_skia.h" |
23 #include "ui/gfx/image/image_util.h" | 24 #include "ui/gfx/image/image_util.h" |
24 | 25 |
25 namespace favicon { | 26 namespace favicon { |
| 27 |
| 28 const base::Feature kFaviconsFromWebManifest{"FaviconsFromWebManifest", |
| 29 base::FEATURE_DISABLED_BY_DEFAULT}; |
| 30 |
26 namespace { | 31 namespace { |
27 | 32 |
28 const int kNonTouchLargestIconSize = 192; | 33 const int kNonTouchLargestIconSize = 192; |
29 | 34 |
30 // Size (along each axis) of a touch icon. This currently corresponds to | 35 // Size (along each axis) of a touch icon. This currently corresponds to |
31 // the apple touch icon for iPad. | 36 // the apple touch icon for iPad. |
32 const int kTouchIconSize = 144; | 37 const int kTouchIconSize = 144; |
33 | 38 |
34 // Returns true if all of the icon URLs and icon types in |bitmap_results| are | 39 // Returns true if all of the icon URLs and icon types in |bitmap_results| are |
35 // identical and if they match |icon_url| and |icon_type|. Returns false if | 40 // identical and if they match |icon_url| and |icon_type|. Returns false if |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 | 226 |
222 void FaviconHandler::FetchFavicon(const GURL& url) { | 227 void FaviconHandler::FetchFavicon(const GURL& url) { |
223 cancelable_task_tracker_for_page_url_.TryCancelAll(); | 228 cancelable_task_tracker_for_page_url_.TryCancelAll(); |
224 cancelable_task_tracker_for_candidates_.TryCancelAll(); | 229 cancelable_task_tracker_for_candidates_.TryCancelAll(); |
225 | 230 |
226 url_ = url; | 231 url_ = url; |
227 | 232 |
228 initial_history_result_expired_or_incomplete_ = false; | 233 initial_history_result_expired_or_incomplete_ = false; |
229 redownload_icons_ = false; | 234 redownload_icons_ = false; |
230 got_favicon_from_history_ = false; | 235 got_favicon_from_history_ = false; |
| 236 manifest_download_request_.Cancel(); |
231 image_download_request_.Cancel(); | 237 image_download_request_.Cancel(); |
| 238 manifest_url_.reset(); |
232 candidates_.clear(); | 239 candidates_.clear(); |
233 notification_icon_url_ = GURL(); | 240 notification_icon_url_ = GURL(); |
234 notification_icon_type_ = favicon_base::INVALID_ICON; | 241 notification_icon_type_ = favicon_base::INVALID_ICON; |
235 num_image_download_requests_ = 0; | 242 num_image_download_requests_ = 0; |
236 current_candidate_index_ = 0u; | 243 current_candidate_index_ = 0u; |
237 best_favicon_ = DownloadedFavicon(); | 244 best_favicon_ = DownloadedFavicon(); |
238 | 245 |
239 // Request the favicon from the history service. In parallel to this the | 246 // Request the favicon from the history service. In parallel to this the |
240 // renderer is going to notify us (well WebContents) when the favicon url is | 247 // renderer is going to notify us (well WebContents) when the favicon url is |
241 // available. | 248 // available. |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 delegate_->OnFaviconUpdated(url_, handler_type_, icon_url, | 316 delegate_->OnFaviconUpdated(url_, handler_type_, icon_url, |
310 icon_url != notification_icon_url_, | 317 icon_url != notification_icon_url_, |
311 image_with_adjusted_colorspace); | 318 image_with_adjusted_colorspace); |
312 | 319 |
313 notification_icon_url_ = icon_url; | 320 notification_icon_url_ = icon_url; |
314 notification_icon_type_ = icon_type; | 321 notification_icon_type_ = icon_type; |
315 } | 322 } |
316 | 323 |
317 void FaviconHandler::OnUpdateCandidates( | 324 void FaviconHandler::OnUpdateCandidates( |
318 const GURL& page_url, | 325 const GURL& page_url, |
319 const std::vector<FaviconURL>& candidates) { | 326 const std::vector<FaviconURL>& candidates, |
| 327 const base::Optional<GURL>& manifest_url) { |
| 328 DCHECK_EQ(page_url, url_); |
320 if (page_url != url_) | 329 if (page_url != url_) |
321 return; | 330 return; |
322 | 331 |
| 332 if (base::FeatureList::IsEnabled(kFaviconsFromWebManifest)) { |
| 333 // |candidates| or |manifest_url| could have been modified via Javascript. |
| 334 if (manifest_url_ && manifest_url_ == manifest_url) |
| 335 return; |
| 336 |
| 337 manifest_url_ = manifest_url; |
| 338 } |
| 339 |
| 340 non_manifest_original_candidates_ = candidates; |
| 341 |
| 342 // Check if the manifest was previously blacklisted (e.g. returned a 404) and |
| 343 // ignore the manifest URL if that's the case. |
| 344 if (manifest_url_ && service_->WasUnableToDownloadFavicon(*manifest_url_)) { |
| 345 DVLOG(1) << "Skip failed Manifest: " << *manifest_url_; |
| 346 manifest_url_.reset(); |
| 347 } |
| 348 |
| 349 if (manifest_url_) { |
| 350 cancelable_task_tracker_for_candidates_.TryCancelAll(); |
| 351 // See if there is a cached favicon for the manifest. |
| 352 GetFaviconAndUpdateMappingsUnlessIncognito( |
| 353 /*icon_url=*/*manifest_url_, favicon_base::FAVICON, |
| 354 base::Bind(&FaviconHandler::OnFaviconDataForManifestFromFaviconService, |
| 355 base::Unretained(this))); |
| 356 } else { |
| 357 // If no manifest available, proceed with the regular candidates only. |
| 358 OnGotFinalIconURLCandidates(candidates); |
| 359 } |
| 360 } |
| 361 |
| 362 void FaviconHandler::OnFaviconDataForManifestFromFaviconService( |
| 363 const std::vector<favicon_base::FaviconRawBitmapResult>& |
| 364 favicon_bitmap_results) { |
| 365 bool has_valid_result = HasValidResult(favicon_bitmap_results); |
| 366 bool has_expired_or_incomplete_result = |
| 367 !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(), |
| 368 favicon_bitmap_results); |
| 369 |
| 370 if (has_valid_result) { |
| 371 // There is a valid favicon. Notify any observers. It is useful to notify |
| 372 // the observers even if the favicon is expired or incomplete (incorrect |
| 373 // size) because temporarily showing the user an expired favicon or |
| 374 // streched favicon is preferable to showing the user the default favicon. |
| 375 NotifyFaviconUpdated(favicon_bitmap_results); |
| 376 } |
| 377 |
| 378 if (has_expired_or_incomplete_result) { |
| 379 manifest_download_request_.Reset(base::Bind( |
| 380 &FaviconHandler::OnDidDownloadManifest, base::Unretained(this))); |
| 381 delegate_->DownloadManifest(*manifest_url_, |
| 382 manifest_download_request_.callback()); |
| 383 } |
| 384 } |
| 385 |
| 386 void FaviconHandler::OnDidDownloadManifest( |
| 387 int status_code, |
| 388 const std::vector<FaviconURL>& candidates) { |
| 389 // Mark manifest download as finished. |
| 390 manifest_download_request_.Cancel(); |
| 391 |
| 392 if (!candidates.empty()) { |
| 393 OnGotFinalIconURLCandidates(candidates); |
| 394 return; |
| 395 } |
| 396 |
| 397 // If either the downloading of the manifest failed, OR the manifest contains |
| 398 // no icons, proceed with the list of icons listed in the HTML. |
| 399 DVLOG(1) << "Could not fetch Manifest icons from " << *manifest_url_ |
| 400 << ", falling back to inlined ones, which are " |
| 401 << non_manifest_original_candidates_.size(); |
| 402 |
| 403 // Don't blacklist 5xx errors. |
| 404 if (status_code / 100 != 5) |
| 405 service_->UnableToDownloadFavicon(*manifest_url_); |
| 406 |
| 407 manifest_url_.reset(); |
| 408 OnGotFinalIconURLCandidates(non_manifest_original_candidates_); |
| 409 } |
| 410 |
| 411 void FaviconHandler::OnGotFinalIconURLCandidates( |
| 412 const std::vector<FaviconURL>& candidates) { |
323 std::vector<FaviconCandidate> sorted_candidates; | 413 std::vector<FaviconCandidate> sorted_candidates; |
324 const std::vector<int> desired_pixel_sizes = | 414 const std::vector<int> desired_pixel_sizes = |
325 GetDesiredPixelSizes(handler_type_); | 415 GetDesiredPixelSizes(handler_type_); |
326 for (const FaviconURL& candidate : candidates) { | 416 for (const FaviconURL& candidate : candidates) { |
327 if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) { | 417 if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) { |
328 sorted_candidates.push_back( | 418 sorted_candidates.push_back( |
329 FaviconCandidate::FromFaviconURL(candidate, desired_pixel_sizes)); | 419 FaviconCandidate::FromFaviconURL(candidate, desired_pixel_sizes)); |
330 } | 420 } |
331 } | 421 } |
332 | 422 |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 // Process the next candidate. | 521 // Process the next candidate. |
432 ++current_candidate_index_; | 522 ++current_candidate_index_; |
433 DownloadCurrentCandidateOrAskFaviconService(); | 523 DownloadCurrentCandidateOrAskFaviconService(); |
434 } else { | 524 } else { |
435 // OnDidDownloadFavicon() can only be called after requesting a download, so | 525 // OnDidDownloadFavicon() can only be called after requesting a download, so |
436 // |num_image_download_requests_| can never be 0. | 526 // |num_image_download_requests_| can never be 0. |
437 RecordDownloadAttemptsForHandlerType(handler_type_, | 527 RecordDownloadAttemptsForHandlerType(handler_type_, |
438 num_image_download_requests_); | 528 num_image_download_requests_); |
439 // We have either found the ideal candidate or run out of candidates. | 529 // We have either found the ideal candidate or run out of candidates. |
440 if (best_favicon_.candidate.icon_type != favicon_base::INVALID_ICON) { | 530 if (best_favicon_.candidate.icon_type != favicon_base::INVALID_ICON) { |
441 // No more icons to request, set the favicon from the candidate. | 531 // No more icons to request, set the favicon from the candidate. Note that |
442 SetFavicon(best_favicon_.candidate.icon_url, best_favicon_.image, | 532 // manifest URLs override icon URLs if available. |
443 best_favicon_.candidate.icon_type); | 533 SetFavicon(manifest_url_.value_or(best_favicon_.candidate.icon_url), |
| 534 best_favicon_.image, best_favicon_.candidate.icon_type); |
444 } | 535 } |
445 // Clear download related state. | 536 // Clear download related state. |
446 current_candidate_index_ = candidates_.size(); | 537 current_candidate_index_ = candidates_.size(); |
447 num_image_download_requests_ = 0; | 538 num_image_download_requests_ = 0; |
448 best_favicon_ = DownloadedFavicon(); | 539 best_favicon_ = DownloadedFavicon(); |
449 } | 540 } |
450 } | 541 } |
451 | 542 |
452 const std::vector<GURL> FaviconHandler::GetIconURLs() const { | 543 const std::vector<GURL> FaviconHandler::GetIconURLs() const { |
453 std::vector<GURL> icon_urls; | 544 std::vector<GURL> icon_urls; |
454 for (const FaviconCandidate& candidate : candidates_) | 545 for (const FaviconCandidate& candidate : candidates_) |
455 icon_urls.push_back(candidate.icon_url); | 546 icon_urls.push_back(candidate.icon_url); |
456 return icon_urls; | 547 return icon_urls; |
457 } | 548 } |
458 | 549 |
459 bool FaviconHandler::HasPendingTasksForTest() { | 550 bool FaviconHandler::HasPendingTasksForTest() { |
460 return !image_download_request_.IsCancelled() || | 551 return !image_download_request_.IsCancelled() || |
| 552 !manifest_download_request_.IsCancelled() || |
461 cancelable_task_tracker_for_page_url_.HasTrackedTasks() || | 553 cancelable_task_tracker_for_page_url_.HasTrackedTasks() || |
462 cancelable_task_tracker_for_candidates_.HasTrackedTasks(); | 554 cancelable_task_tracker_for_candidates_.HasTrackedTasks(); |
463 } | 555 } |
464 | 556 |
465 bool FaviconHandler::ShouldSaveFavicon() { | 557 bool FaviconHandler::ShouldSaveFavicon() { |
466 if (!delegate_->IsOffTheRecord()) | 558 if (!delegate_->IsOffTheRecord()) |
467 return true; | 559 return true; |
468 | 560 |
469 // Always save favicon if the page is bookmarked. | 561 // Always save favicon if the page is bookmarked. |
470 return delegate_->IsBookmarked(url_); | 562 return delegate_->IsBookmarked(url_); |
471 } | 563 } |
472 | 564 |
473 void FaviconHandler::OnFaviconDataForInitialURLFromFaviconService( | 565 void FaviconHandler::OnFaviconDataForInitialURLFromFaviconService( |
474 const std::vector<favicon_base::FaviconRawBitmapResult>& | 566 const std::vector<favicon_base::FaviconRawBitmapResult>& |
475 favicon_bitmap_results) { | 567 favicon_bitmap_results) { |
476 got_favicon_from_history_ = true; | 568 got_favicon_from_history_ = true; |
477 bool has_valid_result = HasValidResult(favicon_bitmap_results); | 569 bool has_valid_result = HasValidResult(favicon_bitmap_results); |
478 initial_history_result_expired_or_incomplete_ = | 570 initial_history_result_expired_or_incomplete_ = |
479 !has_valid_result || | 571 !has_valid_result || |
480 HasExpiredOrIncompleteResult(preferred_icon_size(), | 572 HasExpiredOrIncompleteResult(preferred_icon_size(), |
481 favicon_bitmap_results); | 573 favicon_bitmap_results); |
482 redownload_icons_ = initial_history_result_expired_or_incomplete_ && | 574 redownload_icons_ = initial_history_result_expired_or_incomplete_ && |
483 !favicon_bitmap_results.empty(); | 575 !favicon_bitmap_results.empty(); |
484 | 576 |
485 if (has_valid_result && (!current_candidate() || | 577 if (has_valid_result) { |
486 DoUrlsAndIconsMatch(current_candidate()->icon_url, | 578 if (!current_candidate() || |
487 current_candidate()->icon_type, | 579 DoUrlsAndIconsMatch(current_candidate()->icon_url, |
488 favicon_bitmap_results))) { | 580 current_candidate()->icon_type, |
489 // The db knows the favicon (although it may be out of date) and the entry | 581 favicon_bitmap_results) || |
490 // doesn't have an icon. Set the favicon now, and if the favicon turns out | 582 (manifest_url_ && |
491 // to be expired (or the wrong url) we'll fetch later on. This way the | 583 DoUrlsAndIconsMatch(*manifest_url_, favicon_base::FAVICON, |
492 // user doesn't see a flash of the default favicon. | 584 favicon_bitmap_results))) { |
493 NotifyFaviconUpdated(favicon_bitmap_results); | 585 // The db knows the favicon (although it may be out of date) and the entry |
| 586 // doesn't have an icon. Set the favicon now, and if the favicon turns out |
| 587 // to be expired (or the wrong url) we'll fetch later on. This way the |
| 588 // user doesn't see a flash of the default favicon. |
| 589 NotifyFaviconUpdated(favicon_bitmap_results); |
| 590 } |
494 } | 591 } |
495 | 592 |
496 if (current_candidate()) | 593 if (current_candidate()) |
497 OnGotInitialHistoryDataAndIconURLCandidates(); | 594 OnGotInitialHistoryDataAndIconURLCandidates(); |
498 } | 595 } |
499 | 596 |
500 void FaviconHandler::DownloadCurrentCandidateOrAskFaviconService() { | 597 void FaviconHandler::DownloadCurrentCandidateOrAskFaviconService() { |
501 GURL icon_url = current_candidate()->icon_url; | 598 const GURL icon_url = current_candidate()->icon_url; |
502 favicon_base::IconType icon_type = current_candidate()->icon_type; | 599 const favicon_base::IconType icon_type = current_candidate()->icon_type; |
503 | 600 // If the icons listed in a manifest are being processed, skip the cache |
504 if (redownload_icons_) { | 601 // lookup for |icon_url| since the manifest's URL is used for caching anyway, |
| 602 // and this lookup has happened earlier. |
| 603 if (redownload_icons_ || manifest_url_) { |
505 // We have the mapping, but the favicon is out of date. Download it now. | 604 // We have the mapping, but the favicon is out of date. Download it now. |
506 ScheduleImageDownload(icon_url, icon_type); | 605 ScheduleImageDownload(icon_url, icon_type); |
507 } else { | 606 } else { |
508 // We don't know the favicon, but we may have previously downloaded the | 607 GetFaviconAndUpdateMappingsUnlessIncognito( |
509 // favicon for another page that shares the same favicon. Ask for the | 608 icon_url, icon_type, |
510 // favicon given the favicon URL. | 609 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this))); |
511 if (delegate_->IsOffTheRecord()) { | |
512 service_->GetFavicon( | |
513 icon_url, icon_type, preferred_icon_size(), | |
514 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)), | |
515 &cancelable_task_tracker_for_candidates_); | |
516 } else { | |
517 // Ask the history service for the icon. This does two things: | |
518 // 1. Attempts to fetch the favicon data from the database. | |
519 // 2. If the favicon exists in the database, this updates the database to | |
520 // include the mapping between the page url and the favicon url. | |
521 // This is asynchronous. The history service will call back when done. | |
522 service_->UpdateFaviconMappingsAndFetch( | |
523 url_, icon_url, icon_type, preferred_icon_size(), | |
524 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)), | |
525 &cancelable_task_tracker_for_candidates_); | |
526 } | |
527 } | 610 } |
528 } | 611 } |
529 | 612 |
| 613 void FaviconHandler::GetFaviconAndUpdateMappingsUnlessIncognito( |
| 614 const GURL& icon_url, |
| 615 favicon_base::IconType icon_type, |
| 616 const favicon_base::FaviconResultsCallback& callback) { |
| 617 // We don't know the favicon, but we may have previously downloaded the |
| 618 // favicon for another page that shares the same favicon. Ask for the |
| 619 // favicon given the favicon URL. |
| 620 if (delegate_->IsOffTheRecord()) { |
| 621 service_->GetFavicon(icon_url, icon_type, preferred_icon_size(), callback, |
| 622 &cancelable_task_tracker_for_candidates_); |
| 623 } else { |
| 624 // Ask the history service for the icon. This does two things: |
| 625 // 1. Attempts to fetch the favicon data from the database. |
| 626 // 2. If the favicon exists in the database, this updates the database to |
| 627 // include the mapping between the page url and the favicon url. |
| 628 // This is asynchronous. The history service will call back when done. |
| 629 service_->UpdateFaviconMappingsAndFetch( |
| 630 url_, icon_url, icon_type, preferred_icon_size(), callback, |
| 631 &cancelable_task_tracker_for_candidates_); |
| 632 } |
| 633 } |
| 634 |
530 void FaviconHandler::OnFaviconData(const std::vector< | 635 void FaviconHandler::OnFaviconData(const std::vector< |
531 favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) { | 636 favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) { |
532 bool has_valid_result = HasValidResult(favicon_bitmap_results); | 637 bool has_valid_result = HasValidResult(favicon_bitmap_results); |
533 bool has_expired_or_incomplete_result = | 638 bool has_expired_or_incomplete_result = |
534 !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(), | 639 !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(), |
535 favicon_bitmap_results); | 640 favicon_bitmap_results); |
536 | 641 |
537 if (has_valid_result) { | 642 if (has_valid_result) { |
538 // There is a valid favicon. Notify any observers. It is useful to notify | 643 // There is a valid favicon. Notify any observers. It is useful to notify |
539 // the observers even if the favicon is expired or incomplete (incorrect | 644 // the observers even if the favicon is expired or incomplete (incorrect |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
571 // A max bitmap size is specified to avoid receiving huge bitmaps in | 676 // A max bitmap size is specified to avoid receiving huge bitmaps in |
572 // OnDidDownloadFavicon(). See FaviconDriver::StartDownload() | 677 // OnDidDownloadFavicon(). See FaviconDriver::StartDownload() |
573 // for more details about the max bitmap size. | 678 // for more details about the max bitmap size. |
574 const int download_id = | 679 const int download_id = |
575 delegate_->DownloadImage(image_url, GetMaximalIconSize(handler_type_), | 680 delegate_->DownloadImage(image_url, GetMaximalIconSize(handler_type_), |
576 image_download_request_.callback()); | 681 image_download_request_.callback()); |
577 DCHECK_NE(download_id, 0); | 682 DCHECK_NE(download_id, 0); |
578 } | 683 } |
579 | 684 |
580 } // namespace favicon | 685 } // namespace favicon |
OLD | NEW |