Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: components/favicon/core/favicon_handler.cc

Issue 2799273002: Add support to process favicons from Web Manifests (Closed)
Patch Set: Addressed comments. Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698