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

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

Issue 2799273002: Add support to process favicons from Web Manifests (Closed)
Patch Set: Browsertest comments addressed. 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 // Return true if |bitmap_result| is expired. 39 // Return true if |bitmap_result| is expired.
35 bool IsExpired(const favicon_base::FaviconRawBitmapResult& bitmap_result) { 40 bool IsExpired(const favicon_base::FaviconRawBitmapResult& bitmap_result) {
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 } 139 }
135 case FaviconDriverObserver::NON_TOUCH_LARGEST: 140 case FaviconDriverObserver::NON_TOUCH_LARGEST:
136 return std::vector<int>(1U, kNonTouchLargestIconSize); 141 return std::vector<int>(1U, kNonTouchLargestIconSize);
137 case FaviconDriverObserver::TOUCH_LARGEST: 142 case FaviconDriverObserver::TOUCH_LARGEST:
138 return std::vector<int>(1U, kTouchIconSize); 143 return std::vector<int>(1U, kTouchIconSize);
139 } 144 }
140 NOTREACHED(); 145 NOTREACHED();
141 return std::vector<int>(); 146 return std::vector<int>();
142 } 147 }
143 148
149 bool FaviconURLEquals(const FaviconURL& lhs, const FaviconURL& rhs) {
150 return lhs.icon_url == rhs.icon_url && lhs.icon_type == rhs.icon_type &&
151 lhs.icon_sizes == rhs.icon_sizes;
152 }
153
144 } // namespace 154 } // namespace
145 155
146 //////////////////////////////////////////////////////////////////////////////// 156 ////////////////////////////////////////////////////////////////////////////////
147 157
148 // static 158 // static
149 FaviconHandler::FaviconCandidate 159 FaviconHandler::FaviconCandidate
150 FaviconHandler::FaviconCandidate::FromFaviconURL( 160 FaviconHandler::FaviconCandidate::FromFaviconURL(
151 const favicon::FaviconURL& favicon_url, 161 const favicon::FaviconURL& favicon_url,
152 const std::vector<int>& desired_pixel_sizes) { 162 const std::vector<int>& desired_pixel_sizes) {
153 FaviconCandidate candidate; 163 FaviconCandidate candidate;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
202 212
203 void FaviconHandler::FetchFavicon(const GURL& url) { 213 void FaviconHandler::FetchFavicon(const GURL& url) {
204 cancelable_task_tracker_for_page_url_.TryCancelAll(); 214 cancelable_task_tracker_for_page_url_.TryCancelAll();
205 cancelable_task_tracker_for_candidates_.TryCancelAll(); 215 cancelable_task_tracker_for_candidates_.TryCancelAll();
206 216
207 url_ = url; 217 url_ = url;
208 218
209 initial_history_result_expired_or_incomplete_ = false; 219 initial_history_result_expired_or_incomplete_ = false;
210 redownload_icons_ = false; 220 redownload_icons_ = false;
211 got_favicon_from_history_ = false; 221 got_favicon_from_history_ = false;
222 manifest_download_request_.Cancel();
212 image_download_request_.Cancel(); 223 image_download_request_.Cancel();
224 manifest_url_ = GURL();
225 non_manifest_original_candidates_.clear();
213 candidates_.clear(); 226 candidates_.clear();
214 notification_icon_url_ = GURL(); 227 notification_icon_url_ = GURL();
215 notification_icon_type_ = favicon_base::INVALID_ICON; 228 notification_icon_type_ = favicon_base::INVALID_ICON;
216 num_image_download_requests_ = 0; 229 num_image_download_requests_ = 0;
217 current_candidate_index_ = 0u; 230 current_candidate_index_ = 0u;
218 best_favicon_ = DownloadedFavicon(); 231 best_favicon_ = DownloadedFavicon();
219 232
220 // Request the favicon from the history service. In parallel to this the 233 // Request the favicon from the history service. In parallel to this the
221 // renderer is going to notify us (well WebContents) when the favicon url is 234 // renderer is going to notify us (well WebContents) when the favicon url is
222 // available. 235 // available.
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
290 delegate_->OnFaviconUpdated(url_, handler_type_, icon_url, 303 delegate_->OnFaviconUpdated(url_, handler_type_, icon_url,
291 icon_url != notification_icon_url_, 304 icon_url != notification_icon_url_,
292 image_with_adjusted_colorspace); 305 image_with_adjusted_colorspace);
293 306
294 notification_icon_url_ = icon_url; 307 notification_icon_url_ = icon_url;
295 notification_icon_type_ = icon_type; 308 notification_icon_type_ = icon_type;
296 } 309 }
297 310
298 void FaviconHandler::OnUpdateCandidates( 311 void FaviconHandler::OnUpdateCandidates(
299 const GURL& page_url, 312 const GURL& page_url,
300 const std::vector<FaviconURL>& candidates) { 313 const std::vector<FaviconURL>& candidates,
314 const GURL& manifest_url) {
301 if (page_url != url_) 315 if (page_url != url_)
302 return; 316 return;
303 317
318 bool manifests_feature_enabled =
319 base::FeatureList::IsEnabled(kFaviconsFromWebManifest);
320
321 // |candidates| or |manifest_url| could have been modified via Javascript. If
322 // neither changed, ignore the call.
323 if ((!manifests_feature_enabled || manifest_url_ == manifest_url) &&
324 non_manifest_original_candidates_.size() == candidates.size() &&
325 std::equal(candidates.begin(), candidates.end(),
326 non_manifest_original_candidates_.begin(),
327 &FaviconURLEquals)) {
328 return;
329 }
330
331 non_manifest_original_candidates_ = candidates;
332 cancelable_task_tracker_for_candidates_.TryCancelAll();
333 manifest_download_request_.Cancel();
334 image_download_request_.Cancel();
335 num_image_download_requests_ = 0;
336 current_candidate_index_ = 0u;
337 best_favicon_ = DownloadedFavicon();
338
339 if (manifests_feature_enabled)
340 manifest_url_ = manifest_url;
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_.is_empty() &&
345 service_->WasUnableToDownloadFavicon(manifest_url_)) {
346 DVLOG(1) << "Skip failed Manifest: " << manifest_url;
347 manifest_url_ = GURL();
348 }
349
350 // If no manifest available, proceed with the regular candidates only.
351 if (manifest_url_.is_empty()) {
352 OnGotFinalIconURLCandidates(candidates);
353 return;
354 }
355
356 // See if there is a cached favicon for the manifest. This will update the DB
357 // mappings only if the manifest URL is cached.
358 GetFaviconAndUpdateMappingsUnlessIncognito(
359 /*icon_url=*/manifest_url_, favicon_base::FAVICON,
360 base::Bind(&FaviconHandler::OnFaviconDataForManifestFromFaviconService,
361 base::Unretained(this)));
362 }
363
364 void FaviconHandler::OnFaviconDataForManifestFromFaviconService(
365 const std::vector<favicon_base::FaviconRawBitmapResult>&
366 favicon_bitmap_results) {
367 // The database lookup for the page URL is guaranteed to be completed because
368 // the HistoryBackend uses a SequencedTaskRunner, and we also know that
369 // FetchFavicon() was called before OnUpdateCandidates().
370 DCHECK(got_favicon_from_history_);
371
372 bool has_valid_result = HasValidResult(favicon_bitmap_results);
373 bool has_expired_or_incomplete_result =
374 !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(),
375 favicon_bitmap_results);
376
377 if (has_valid_result && (notification_icon_url_ != manifest_url_ ||
378 notification_icon_type_ != favicon_base::FAVICON)) {
379 // There is a valid favicon. Notify any observers. It is useful to notify
380 // the observers even if the favicon is expired or incomplete (incorrect
381 // size) because temporarily showing the user an expired favicon or
382 // streched favicon is preferable to showing the user the default favicon.
383 NotifyFaviconUpdated(favicon_bitmap_results);
384 }
385
386 if (has_expired_or_incomplete_result) {
387 manifest_download_request_.Reset(base::Bind(
388 &FaviconHandler::OnDidDownloadManifest, base::Unretained(this)));
389 delegate_->DownloadManifest(manifest_url_,
390 manifest_download_request_.callback());
391 }
392 }
393
394 void FaviconHandler::OnDidDownloadManifest(
395 const std::vector<FaviconURL>& candidates) {
396 // Mark manifest download as finished.
397 manifest_download_request_.Cancel();
398
399 if (!candidates.empty()) {
400 OnGotFinalIconURLCandidates(candidates);
401 return;
402 }
403
404 // If either the downloading of the manifest failed, OR the manifest contains
405 // no icons, proceed with the list of icons listed in the HTML.
406 DVLOG(1) << "Could not fetch Manifest icons from " << manifest_url_
407 << ", falling back to inlined ones, which are "
408 << non_manifest_original_candidates_.size();
409
410 service_->UnableToDownloadFavicon(manifest_url_);
411 manifest_url_ = GURL();
412
413 OnGotFinalIconURLCandidates(non_manifest_original_candidates_);
414 }
415
416 void FaviconHandler::OnGotFinalIconURLCandidates(
417 const std::vector<FaviconURL>& candidates) {
304 std::vector<FaviconCandidate> sorted_candidates; 418 std::vector<FaviconCandidate> sorted_candidates;
305 const std::vector<int> desired_pixel_sizes = 419 const std::vector<int> desired_pixel_sizes =
306 GetDesiredPixelSizes(handler_type_); 420 GetDesiredPixelSizes(handler_type_);
307 for (const FaviconURL& candidate : candidates) { 421 for (const FaviconURL& candidate : candidates) {
308 if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) { 422 if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) {
309 sorted_candidates.push_back( 423 sorted_candidates.push_back(
310 FaviconCandidate::FromFaviconURL(candidate, desired_pixel_sizes)); 424 FaviconCandidate::FromFaviconURL(candidate, desired_pixel_sizes));
311 } 425 }
312 } 426 }
313 427
314 std::stable_sort(sorted_candidates.begin(), sorted_candidates.end(), 428 std::stable_sort(sorted_candidates.begin(), sorted_candidates.end(),
315 &FaviconCandidate::CompareScore); 429 &FaviconCandidate::CompareScore);
316 430
317 if (candidates_.size() == sorted_candidates.size() &&
318 std::equal(sorted_candidates.begin(), sorted_candidates.end(),
319 candidates_.begin())) {
320 return;
321 }
322
323 cancelable_task_tracker_for_candidates_.TryCancelAll();
324 image_download_request_.Cancel();
325 candidates_ = std::move(sorted_candidates); 431 candidates_ = std::move(sorted_candidates);
326 num_image_download_requests_ = 0;
327 current_candidate_index_ = 0u;
328 best_favicon_ = DownloadedFavicon();
329 432
330 // TODO(davemoore) Should clear on empty url. Currently we ignore it. 433 // TODO(davemoore) Should clear on empty url. Currently we ignore it.
331 // This appears to be what FF does as well. 434 // This appears to be what FF does as well.
332 if (current_candidate() && got_favicon_from_history_) 435 if (current_candidate() && got_favicon_from_history_)
333 OnGotInitialHistoryDataAndIconURLCandidates(); 436 OnGotInitialHistoryDataAndIconURLCandidates();
334 } 437 }
335 438
336 // static 439 // static
337 int FaviconHandler::GetMaximalIconSize( 440 int FaviconHandler::GetMaximalIconSize(
338 FaviconDriverObserver::NotificationIconType handler_type) { 441 FaviconDriverObserver::NotificationIconType handler_type) {
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
412 // Process the next candidate. 515 // Process the next candidate.
413 ++current_candidate_index_; 516 ++current_candidate_index_;
414 DownloadCurrentCandidateOrAskFaviconService(); 517 DownloadCurrentCandidateOrAskFaviconService();
415 } else { 518 } else {
416 // OnDidDownloadFavicon() can only be called after requesting a download, so 519 // OnDidDownloadFavicon() can only be called after requesting a download, so
417 // |num_image_download_requests_| can never be 0. 520 // |num_image_download_requests_| can never be 0.
418 RecordDownloadAttemptsForHandlerType(handler_type_, 521 RecordDownloadAttemptsForHandlerType(handler_type_,
419 num_image_download_requests_); 522 num_image_download_requests_);
420 // We have either found the ideal candidate or run out of candidates. 523 // We have either found the ideal candidate or run out of candidates.
421 if (best_favicon_.candidate.icon_type != favicon_base::INVALID_ICON) { 524 if (best_favicon_.candidate.icon_type != favicon_base::INVALID_ICON) {
422 // No more icons to request, set the favicon from the candidate. 525 // No more icons to request, set the favicon from the candidate. The
423 SetFavicon(best_favicon_.candidate.icon_url, best_favicon_.image, 526 // manifest URL, if available, is used instead of the icon URL.
424 best_favicon_.candidate.icon_type); 527 SetFavicon(manifest_url_.is_empty() ? best_favicon_.candidate.icon_url
528 : manifest_url_,
529 best_favicon_.image, best_favicon_.candidate.icon_type);
425 } 530 }
426 // Clear download related state. 531 // Clear download related state.
427 current_candidate_index_ = candidates_.size(); 532 current_candidate_index_ = candidates_.size();
428 num_image_download_requests_ = 0; 533 num_image_download_requests_ = 0;
429 best_favicon_ = DownloadedFavicon(); 534 best_favicon_ = DownloadedFavicon();
430 } 535 }
431 } 536 }
432 537
433 const std::vector<GURL> FaviconHandler::GetIconURLs() const { 538 const std::vector<GURL> FaviconHandler::GetIconURLs() const {
434 std::vector<GURL> icon_urls; 539 std::vector<GURL> icon_urls;
435 for (const FaviconCandidate& candidate : candidates_) 540 for (const FaviconCandidate& candidate : candidates_)
436 icon_urls.push_back(candidate.icon_url); 541 icon_urls.push_back(candidate.icon_url);
437 return icon_urls; 542 return icon_urls;
438 } 543 }
439 544
440 bool FaviconHandler::HasPendingTasksForTest() { 545 bool FaviconHandler::HasPendingTasksForTest() {
441 return !image_download_request_.IsCancelled() || 546 return !image_download_request_.IsCancelled() ||
547 !manifest_download_request_.IsCancelled() ||
442 cancelable_task_tracker_for_page_url_.HasTrackedTasks() || 548 cancelable_task_tracker_for_page_url_.HasTrackedTasks() ||
443 cancelable_task_tracker_for_candidates_.HasTrackedTasks(); 549 cancelable_task_tracker_for_candidates_.HasTrackedTasks();
444 } 550 }
445 551
446 bool FaviconHandler::ShouldSaveFavicon() { 552 bool FaviconHandler::ShouldSaveFavicon() {
447 if (!delegate_->IsOffTheRecord()) 553 if (!delegate_->IsOffTheRecord())
448 return true; 554 return true;
449 555
450 // Always save favicon if the page is bookmarked. 556 // Always save favicon if the page is bookmarked.
451 return delegate_->IsBookmarked(url_); 557 return delegate_->IsBookmarked(url_);
(...skipping 17 matching lines...) Expand all
469 // url) we'll fetch later on. This way the user doesn't see a flash of the 575 // url) we'll fetch later on. This way the user doesn't see a flash of the
470 // default favicon. 576 // default favicon.
471 NotifyFaviconUpdated(favicon_bitmap_results); 577 NotifyFaviconUpdated(favicon_bitmap_results);
472 } 578 }
473 579
474 if (current_candidate()) 580 if (current_candidate())
475 OnGotInitialHistoryDataAndIconURLCandidates(); 581 OnGotInitialHistoryDataAndIconURLCandidates();
476 } 582 }
477 583
478 void FaviconHandler::DownloadCurrentCandidateOrAskFaviconService() { 584 void FaviconHandler::DownloadCurrentCandidateOrAskFaviconService() {
479 GURL icon_url = current_candidate()->icon_url; 585 const GURL icon_url = current_candidate()->icon_url;
480 favicon_base::IconType icon_type = current_candidate()->icon_type; 586 const favicon_base::IconType icon_type = current_candidate()->icon_type;
481 587 // If the icons listed in a manifest are being processed, skip the cache
482 if (redownload_icons_) { 588 // lookup for |icon_url| since the manifest's URL is used for caching, not the
589 // icon URL, and this lookup has happened earlier.
590 if (redownload_icons_ || !manifest_url_.is_empty()) {
483 // We have the mapping, but the favicon is out of date. Download it now. 591 // We have the mapping, but the favicon is out of date. Download it now.
484 ScheduleImageDownload(icon_url, icon_type); 592 ScheduleImageDownload(icon_url, icon_type);
485 } else { 593 } else {
486 // We don't know the favicon, but we may have previously downloaded the 594 GetFaviconAndUpdateMappingsUnlessIncognito(
487 // favicon for another page that shares the same favicon. Ask for the 595 icon_url, icon_type,
488 // favicon given the favicon URL. 596 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)));
489 if (delegate_->IsOffTheRecord()) {
490 service_->GetFavicon(
491 icon_url, icon_type, preferred_icon_size(),
492 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)),
493 &cancelable_task_tracker_for_candidates_);
494 } else {
495 // Ask the history service for the icon. This does two things:
496 // 1. Attempts to fetch the favicon data from the database.
497 // 2. If the favicon exists in the database, this updates the database to
498 // include the mapping between the page url and the favicon url.
499 // This is asynchronous. The history service will call back when done.
500 service_->UpdateFaviconMappingsAndFetch(
501 url_, icon_url, icon_type, preferred_icon_size(),
502 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)),
503 &cancelable_task_tracker_for_candidates_);
504 }
505 } 597 }
506 } 598 }
507 599
600 void FaviconHandler::GetFaviconAndUpdateMappingsUnlessIncognito(
601 const GURL& icon_url,
602 favicon_base::IconType icon_type,
603 const favicon_base::FaviconResultsCallback& callback) {
604 // We don't know the favicon, but we may have previously downloaded the
605 // favicon for another page that shares the same favicon. Ask for the
606 // favicon given the favicon URL.
607 if (delegate_->IsOffTheRecord()) {
608 service_->GetFavicon(icon_url, icon_type, preferred_icon_size(), callback,
609 &cancelable_task_tracker_for_candidates_);
610 } else {
611 // Ask the history service for the icon. This does two things:
612 // 1. Attempts to fetch the favicon data from the database.
613 // 2. If the favicon exists in the database, this updates the database to
614 // include the mapping between the page url and the favicon url.
615 // This is asynchronous. The history service will call back when done.
616 service_->UpdateFaviconMappingsAndFetch(
617 url_, icon_url, icon_type, preferred_icon_size(), callback,
618 &cancelable_task_tracker_for_candidates_);
619 }
620 }
621
508 void FaviconHandler::OnFaviconData(const std::vector< 622 void FaviconHandler::OnFaviconData(const std::vector<
509 favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) { 623 favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) {
510 bool has_valid_result = HasValidResult(favicon_bitmap_results); 624 bool has_valid_result = HasValidResult(favicon_bitmap_results);
511 bool has_expired_or_incomplete_result = 625 bool has_expired_or_incomplete_result =
512 !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(), 626 !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(),
513 favicon_bitmap_results); 627 favicon_bitmap_results);
514 628
515 if (has_valid_result) { 629 if (has_valid_result) {
516 // There is a valid favicon. Notify any observers. It is useful to notify 630 // There is a valid favicon. Notify any observers. It is useful to notify
517 // the observers even if the favicon is expired or incomplete (incorrect 631 // the observers even if the favicon is expired or incomplete (incorrect
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 // A max bitmap size is specified to avoid receiving huge bitmaps in 663 // A max bitmap size is specified to avoid receiving huge bitmaps in
550 // OnDidDownloadFavicon(). See FaviconDriver::StartDownload() 664 // OnDidDownloadFavicon(). See FaviconDriver::StartDownload()
551 // for more details about the max bitmap size. 665 // for more details about the max bitmap size.
552 const int download_id = 666 const int download_id =
553 delegate_->DownloadImage(image_url, GetMaximalIconSize(handler_type_), 667 delegate_->DownloadImage(image_url, GetMaximalIconSize(handler_type_),
554 image_download_request_.callback()); 668 image_download_request_.callback());
555 DCHECK_NE(download_id, 0); 669 DCHECK_NE(download_id, 0);
556 } 670 }
557 671
558 } // namespace favicon 672 } // namespace favicon
OLDNEW
« no previous file with comments | « components/favicon/core/favicon_handler.h ('k') | components/favicon/core/favicon_handler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698