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

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

Issue 2799273002: Add support to process favicons from Web Manifests (Closed)
Patch Set: Reverted fieldtrial_testing_config.json 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) {
pkotwicz 2017/05/12 06:13:29 I found two bugs :( Scenario for Bug #1 1) OnUpda
mastiz 2017/05/12 13:31:33 I will work on this, I think you're right bugs exi
mastiz 2017/05/15 14:07:00 I added the two corresponding tests which now pass
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.
pkotwicz 2017/05/12 06:13:29 Nit: You can move the if() statement on line 334 o
mastiz 2017/05/12 13:31:33 Done.
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();
pkotwicz 2017/05/12 06:13:29 For the sake of sanity, should we cancel |manifest
mastiz 2017/05/12 13:31:33 Done.
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,
pkotwicz 2017/05/12 07:20:26 The following scenario is buggy: 1) User is using
pkotwicz 2017/05/12 15:37:42 I think that this bug affects https://www.twitter.
mastiz 2017/05/15 14:07:00 Added UnknownManifestWithoutIconsAndRegularIconInH
pkotwicz 2017/05/16 06:36:35 Yes I would prefer that. That works well with my p
mastiz 2017/05/16 11:05:22 I have some trouble following this discussion, mig
mastiz 2017/05/16 17:53:45 As discussed offline: the original bug is not an a
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 const std::vector<FaviconURL>& candidates) {
388 // Mark manifest download as finished.
389 manifest_download_request_.Cancel();
390
391 if (!candidates.empty()) {
392 OnGotFinalIconURLCandidates(candidates);
393 return;
394 }
395
396 // If either the downloading of the manifest failed, OR the manifest contains
397 // no icons, proceed with the list of icons listed in the HTML.
398 DVLOG(1) << "Could not fetch Manifest icons from " << *manifest_url_
399 << ", falling back to inlined ones, which are "
400 << non_manifest_original_candidates_.size();
401
402 manifest_url_.reset();
403 OnGotFinalIconURLCandidates(non_manifest_original_candidates_);
404 }
405
406 void FaviconHandler::OnGotFinalIconURLCandidates(
407 const std::vector<FaviconURL>& candidates) {
323 std::vector<FaviconCandidate> sorted_candidates; 408 std::vector<FaviconCandidate> sorted_candidates;
324 const std::vector<int> desired_pixel_sizes = 409 const std::vector<int> desired_pixel_sizes =
325 GetDesiredPixelSizes(handler_type_); 410 GetDesiredPixelSizes(handler_type_);
326 for (const FaviconURL& candidate : candidates) { 411 for (const FaviconURL& candidate : candidates) {
327 if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) { 412 if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) {
328 sorted_candidates.push_back( 413 sorted_candidates.push_back(
329 FaviconCandidate::FromFaviconURL(candidate, desired_pixel_sizes)); 414 FaviconCandidate::FromFaviconURL(candidate, desired_pixel_sizes));
330 } 415 }
331 } 416 }
332 417
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
431 // Process the next candidate. 516 // Process the next candidate.
432 ++current_candidate_index_; 517 ++current_candidate_index_;
433 DownloadCurrentCandidateOrAskFaviconService(); 518 DownloadCurrentCandidateOrAskFaviconService();
434 } else { 519 } else {
435 // OnDidDownloadFavicon() can only be called after requesting a download, so 520 // OnDidDownloadFavicon() can only be called after requesting a download, so
436 // |num_image_download_requests_| can never be 0. 521 // |num_image_download_requests_| can never be 0.
437 RecordDownloadAttemptsForHandlerType(handler_type_, 522 RecordDownloadAttemptsForHandlerType(handler_type_,
438 num_image_download_requests_); 523 num_image_download_requests_);
439 // We have either found the ideal candidate or run out of candidates. 524 // We have either found the ideal candidate or run out of candidates.
440 if (best_favicon_.candidate.icon_type != favicon_base::INVALID_ICON) { 525 if (best_favicon_.candidate.icon_type != favicon_base::INVALID_ICON) {
441 // No more icons to request, set the favicon from the candidate. 526 // No more icons to request, set the favicon from the candidate. Note that
442 SetFavicon(best_favicon_.candidate.icon_url, best_favicon_.image, 527 // manifest URLs override icon URLs if available.
pkotwicz 2017/05/12 06:13:29 Nit: "Note that manifest URLs override icon URLs i
mastiz 2017/05/12 13:31:33 Done.
443 best_favicon_.candidate.icon_type); 528 SetFavicon(manifest_url_.value_or(best_favicon_.candidate.icon_url),
529 best_favicon_.image, best_favicon_.candidate.icon_type);
444 } 530 }
445 // Clear download related state. 531 // Clear download related state.
446 current_candidate_index_ = candidates_.size(); 532 current_candidate_index_ = candidates_.size();
447 num_image_download_requests_ = 0; 533 num_image_download_requests_ = 0;
448 best_favicon_ = DownloadedFavicon(); 534 best_favicon_ = DownloadedFavicon();
449 } 535 }
450 } 536 }
451 537
452 const std::vector<GURL> FaviconHandler::GetIconURLs() const { 538 const std::vector<GURL> FaviconHandler::GetIconURLs() const {
453 std::vector<GURL> icon_urls; 539 std::vector<GURL> icon_urls;
454 for (const FaviconCandidate& candidate : candidates_) 540 for (const FaviconCandidate& candidate : candidates_)
455 icon_urls.push_back(candidate.icon_url); 541 icon_urls.push_back(candidate.icon_url);
456 return icon_urls; 542 return icon_urls;
457 } 543 }
458 544
459 bool FaviconHandler::HasPendingTasksForTest() { 545 bool FaviconHandler::HasPendingTasksForTest() {
460 return !image_download_request_.IsCancelled() || 546 return !image_download_request_.IsCancelled() ||
547 !manifest_download_request_.IsCancelled() ||
461 cancelable_task_tracker_for_page_url_.HasTrackedTasks() || 548 cancelable_task_tracker_for_page_url_.HasTrackedTasks() ||
462 cancelable_task_tracker_for_candidates_.HasTrackedTasks(); 549 cancelable_task_tracker_for_candidates_.HasTrackedTasks();
463 } 550 }
464 551
465 bool FaviconHandler::ShouldSaveFavicon() { 552 bool FaviconHandler::ShouldSaveFavicon() {
466 if (!delegate_->IsOffTheRecord()) 553 if (!delegate_->IsOffTheRecord())
467 return true; 554 return true;
468 555
469 // Always save favicon if the page is bookmarked. 556 // Always save favicon if the page is bookmarked.
470 return delegate_->IsBookmarked(url_); 557 return delegate_->IsBookmarked(url_);
471 } 558 }
472 559
473 void FaviconHandler::OnFaviconDataForInitialURLFromFaviconService( 560 void FaviconHandler::OnFaviconDataForInitialURLFromFaviconService(
474 const std::vector<favicon_base::FaviconRawBitmapResult>& 561 const std::vector<favicon_base::FaviconRawBitmapResult>&
475 favicon_bitmap_results) { 562 favicon_bitmap_results) {
476 got_favicon_from_history_ = true; 563 got_favicon_from_history_ = true;
477 bool has_valid_result = HasValidResult(favicon_bitmap_results); 564 bool has_valid_result = HasValidResult(favicon_bitmap_results);
478 initial_history_result_expired_or_incomplete_ = 565 initial_history_result_expired_or_incomplete_ =
479 !has_valid_result || 566 !has_valid_result ||
480 HasExpiredOrIncompleteResult(preferred_icon_size(), 567 HasExpiredOrIncompleteResult(preferred_icon_size(),
481 favicon_bitmap_results); 568 favicon_bitmap_results);
482 redownload_icons_ = initial_history_result_expired_or_incomplete_ && 569 redownload_icons_ = initial_history_result_expired_or_incomplete_ &&
483 !favicon_bitmap_results.empty(); 570 !favicon_bitmap_results.empty();
484 571
485 if (has_valid_result && (!current_candidate() || 572 if (has_valid_result) {
pkotwicz 2017/05/12 06:13:29 Optional: In a separate CL you can remove the if()
486 DoUrlsAndIconsMatch(current_candidate()->icon_url, 573 if (!current_candidate() ||
487 current_candidate()->icon_type, 574 DoUrlsAndIconsMatch(current_candidate()->icon_url,
488 favicon_bitmap_results))) { 575 current_candidate()->icon_type,
489 // The db knows the favicon (although it may be out of date) and the entry 576 favicon_bitmap_results) ||
490 // doesn't have an icon. Set the favicon now, and if the favicon turns out 577 (manifest_url_ &&
491 // to be expired (or the wrong url) we'll fetch later on. This way the 578 DoUrlsAndIconsMatch(*manifest_url_, favicon_base::FAVICON,
492 // user doesn't see a flash of the default favicon. 579 favicon_bitmap_results))) {
493 NotifyFaviconUpdated(favicon_bitmap_results); 580 // The db knows the favicon (although it may be out of date) and the entry
581 // doesn't have an icon. Set the favicon now, and if the favicon turns out
582 // to be expired (or the wrong url) we'll fetch later on. This way the
583 // user doesn't see a flash of the default favicon.
584 NotifyFaviconUpdated(favicon_bitmap_results);
585 }
494 } 586 }
495 587
496 if (current_candidate()) 588 if (current_candidate())
497 OnGotInitialHistoryDataAndIconURLCandidates(); 589 OnGotInitialHistoryDataAndIconURLCandidates();
498 } 590 }
499 591
500 void FaviconHandler::DownloadCurrentCandidateOrAskFaviconService() { 592 void FaviconHandler::DownloadCurrentCandidateOrAskFaviconService() {
501 GURL icon_url = current_candidate()->icon_url; 593 const GURL icon_url = current_candidate()->icon_url;
502 favicon_base::IconType icon_type = current_candidate()->icon_type; 594 const favicon_base::IconType icon_type = current_candidate()->icon_type;
503 595 // If the icons listed in a manifest are being processed, skip the cache
504 if (redownload_icons_) { 596 // lookup for |icon_url| since the manifest's URL is used for caching anyway,
597 // and this lookup has happened earlier.
pkotwicz 2017/05/12 06:13:29 How about: "since the manifest's URL is using for
mastiz 2017/05/12 13:31:33 Done.
598 if (redownload_icons_ || manifest_url_) {
505 // We have the mapping, but the favicon is out of date. Download it now. 599 // We have the mapping, but the favicon is out of date. Download it now.
506 ScheduleImageDownload(icon_url, icon_type); 600 ScheduleImageDownload(icon_url, icon_type);
507 } else { 601 } else {
508 // We don't know the favicon, but we may have previously downloaded the 602 GetFaviconAndUpdateMappingsUnlessIncognito(
509 // favicon for another page that shares the same favicon. Ask for the 603 icon_url, icon_type,
510 // favicon given the favicon URL. 604 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 } 605 }
528 } 606 }
529 607
608 void FaviconHandler::GetFaviconAndUpdateMappingsUnlessIncognito(
609 const GURL& icon_url,
610 favicon_base::IconType icon_type,
611 const favicon_base::FaviconResultsCallback& callback) {
612 // We don't know the favicon, but we may have previously downloaded the
613 // favicon for another page that shares the same favicon. Ask for the
614 // favicon given the favicon URL.
615 if (delegate_->IsOffTheRecord()) {
616 service_->GetFavicon(icon_url, icon_type, preferred_icon_size(), callback,
617 &cancelable_task_tracker_for_candidates_);
618 } else {
619 // Ask the history service for the icon. This does two things:
620 // 1. Attempts to fetch the favicon data from the database.
621 // 2. If the favicon exists in the database, this updates the database to
622 // include the mapping between the page url and the favicon url.
623 // This is asynchronous. The history service will call back when done.
624 service_->UpdateFaviconMappingsAndFetch(
625 url_, icon_url, icon_type, preferred_icon_size(), callback,
626 &cancelable_task_tracker_for_candidates_);
627 }
628 }
629
530 void FaviconHandler::OnFaviconData(const std::vector< 630 void FaviconHandler::OnFaviconData(const std::vector<
531 favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) { 631 favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) {
532 bool has_valid_result = HasValidResult(favicon_bitmap_results); 632 bool has_valid_result = HasValidResult(favicon_bitmap_results);
533 bool has_expired_or_incomplete_result = 633 bool has_expired_or_incomplete_result =
534 !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(), 634 !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(),
535 favicon_bitmap_results); 635 favicon_bitmap_results);
536 636
537 if (has_valid_result) { 637 if (has_valid_result) {
538 // There is a valid favicon. Notify any observers. It is useful to notify 638 // 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 639 // 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 671 // A max bitmap size is specified to avoid receiving huge bitmaps in
572 // OnDidDownloadFavicon(). See FaviconDriver::StartDownload() 672 // OnDidDownloadFavicon(). See FaviconDriver::StartDownload()
573 // for more details about the max bitmap size. 673 // for more details about the max bitmap size.
574 const int download_id = 674 const int download_id =
575 delegate_->DownloadImage(image_url, GetMaximalIconSize(handler_type_), 675 delegate_->DownloadImage(image_url, GetMaximalIconSize(handler_type_),
576 image_download_request_.callback()); 676 image_download_request_.callback());
577 DCHECK_NE(download_id, 0); 677 DCHECK_NE(download_id, 0);
578 } 678 }
579 679
580 } // namespace favicon 680 } // namespace favicon
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698