| Index: components/favicon/core/favicon_handler.cc
|
| diff --git a/components/favicon/core/favicon_handler.cc b/components/favicon/core/favicon_handler.cc
|
| index f811c350376f517a6888ab7222b2e98e9929e5cf..8f8622d7e79db019d6987d195f6867b0e84605b8 100644
|
| --- a/components/favicon/core/favicon_handler.cc
|
| +++ b/components/favicon/core/favicon_handler.cc
|
| @@ -11,6 +11,7 @@
|
|
|
| #include "base/bind.h"
|
| #include "base/bind_helpers.h"
|
| +#include "base/feature_list.h"
|
| #include "base/memory/ref_counted_memory.h"
|
| #include "base/metrics/histogram_macros.h"
|
| #include "build/build_config.h"
|
| @@ -23,6 +24,10 @@
|
| #include "ui/gfx/image/image_util.h"
|
|
|
| namespace favicon {
|
| +
|
| +const base::Feature kFaviconsFromWebManifest{"FaviconsFromWebManifest",
|
| + base::FEATURE_DISABLED_BY_DEFAULT};
|
| +
|
| namespace {
|
|
|
| const int kNonTouchLargestIconSize = 192;
|
| @@ -141,6 +146,11 @@ std::vector<int> GetDesiredPixelSizes(
|
| return std::vector<int>();
|
| }
|
|
|
| +bool FaviconURLEquals(const FaviconURL& lhs, const FaviconURL& rhs) {
|
| + return lhs.icon_url == rhs.icon_url && lhs.icon_type == rhs.icon_type &&
|
| + lhs.icon_sizes == rhs.icon_sizes;
|
| +}
|
| +
|
| } // namespace
|
|
|
| ////////////////////////////////////////////////////////////////////////////////
|
| @@ -209,7 +219,10 @@ void FaviconHandler::FetchFavicon(const GURL& url) {
|
| initial_history_result_expired_or_incomplete_ = false;
|
| redownload_icons_ = false;
|
| got_favicon_from_history_ = false;
|
| + manifest_download_request_.Cancel();
|
| image_download_request_.Cancel();
|
| + manifest_url_ = GURL();
|
| + non_manifest_original_candidates_.clear();
|
| candidates_.clear();
|
| notification_icon_url_ = GURL();
|
| notification_icon_type_ = favicon_base::INVALID_ICON;
|
| @@ -297,10 +310,111 @@ void FaviconHandler::NotifyFaviconUpdated(const GURL& icon_url,
|
|
|
| void FaviconHandler::OnUpdateCandidates(
|
| const GURL& page_url,
|
| - const std::vector<FaviconURL>& candidates) {
|
| + const std::vector<FaviconURL>& candidates,
|
| + const GURL& manifest_url) {
|
| if (page_url != url_)
|
| return;
|
|
|
| + bool manifests_feature_enabled =
|
| + base::FeatureList::IsEnabled(kFaviconsFromWebManifest);
|
| +
|
| + // |candidates| or |manifest_url| could have been modified via Javascript. If
|
| + // neither changed, ignore the call.
|
| + if ((!manifests_feature_enabled || manifest_url_ == manifest_url) &&
|
| + non_manifest_original_candidates_.size() == candidates.size() &&
|
| + std::equal(candidates.begin(), candidates.end(),
|
| + non_manifest_original_candidates_.begin(),
|
| + &FaviconURLEquals)) {
|
| + return;
|
| + }
|
| +
|
| + non_manifest_original_candidates_ = candidates;
|
| + cancelable_task_tracker_for_candidates_.TryCancelAll();
|
| + manifest_download_request_.Cancel();
|
| + image_download_request_.Cancel();
|
| + num_image_download_requests_ = 0;
|
| + current_candidate_index_ = 0u;
|
| + best_favicon_ = DownloadedFavicon();
|
| +
|
| + if (manifests_feature_enabled)
|
| + manifest_url_ = manifest_url;
|
| +
|
| + // Check if the manifest was previously blacklisted (e.g. returned a 404) and
|
| + // ignore the manifest URL if that's the case.
|
| + if (!manifest_url_.is_empty() &&
|
| + service_->WasUnableToDownloadFavicon(manifest_url_)) {
|
| + DVLOG(1) << "Skip failed Manifest: " << manifest_url;
|
| + manifest_url_ = GURL();
|
| + }
|
| +
|
| + // If no manifest available, proceed with the regular candidates only.
|
| + if (manifest_url_.is_empty()) {
|
| + OnGotFinalIconURLCandidates(candidates);
|
| + return;
|
| + }
|
| +
|
| + // See if there is a cached favicon for the manifest. This will update the DB
|
| + // mappings only if the manifest URL is cached.
|
| + GetFaviconAndUpdateMappingsUnlessIncognito(
|
| + /*icon_url=*/manifest_url_, favicon_base::FAVICON,
|
| + base::Bind(&FaviconHandler::OnFaviconDataForManifestFromFaviconService,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +void FaviconHandler::OnFaviconDataForManifestFromFaviconService(
|
| + const std::vector<favicon_base::FaviconRawBitmapResult>&
|
| + favicon_bitmap_results) {
|
| + // The database lookup for the page URL is guaranteed to be completed because
|
| + // the HistoryBackend uses a SequencedTaskRunner, and we also know that
|
| + // FetchFavicon() was called before OnUpdateCandidates().
|
| + DCHECK(got_favicon_from_history_);
|
| +
|
| + bool has_valid_result = HasValidResult(favicon_bitmap_results);
|
| + bool has_expired_or_incomplete_result =
|
| + !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(),
|
| + favicon_bitmap_results);
|
| +
|
| + if (has_valid_result && (notification_icon_url_ != manifest_url_ ||
|
| + notification_icon_type_ != favicon_base::FAVICON)) {
|
| + // There is a valid favicon. Notify any observers. It is useful to notify
|
| + // the observers even if the favicon is expired or incomplete (incorrect
|
| + // size) because temporarily showing the user an expired favicon or
|
| + // streched favicon is preferable to showing the user the default favicon.
|
| + NotifyFaviconUpdated(favicon_bitmap_results);
|
| + }
|
| +
|
| + if (has_expired_or_incomplete_result) {
|
| + manifest_download_request_.Reset(base::Bind(
|
| + &FaviconHandler::OnDidDownloadManifest, base::Unretained(this)));
|
| + delegate_->DownloadManifest(manifest_url_,
|
| + manifest_download_request_.callback());
|
| + }
|
| +}
|
| +
|
| +void FaviconHandler::OnDidDownloadManifest(
|
| + const std::vector<FaviconURL>& candidates) {
|
| + // Mark manifest download as finished.
|
| + manifest_download_request_.Cancel();
|
| +
|
| + if (!candidates.empty()) {
|
| + OnGotFinalIconURLCandidates(candidates);
|
| + return;
|
| + }
|
| +
|
| + // If either the downloading of the manifest failed, OR the manifest contains
|
| + // no icons, proceed with the list of icons listed in the HTML.
|
| + DVLOG(1) << "Could not fetch Manifest icons from " << manifest_url_
|
| + << ", falling back to inlined ones, which are "
|
| + << non_manifest_original_candidates_.size();
|
| +
|
| + service_->UnableToDownloadFavicon(manifest_url_);
|
| + manifest_url_ = GURL();
|
| +
|
| + OnGotFinalIconURLCandidates(non_manifest_original_candidates_);
|
| +}
|
| +
|
| +void FaviconHandler::OnGotFinalIconURLCandidates(
|
| + const std::vector<FaviconURL>& candidates) {
|
| std::vector<FaviconCandidate> sorted_candidates;
|
| const std::vector<int> desired_pixel_sizes =
|
| GetDesiredPixelSizes(handler_type_);
|
| @@ -314,18 +428,7 @@ void FaviconHandler::OnUpdateCandidates(
|
| std::stable_sort(sorted_candidates.begin(), sorted_candidates.end(),
|
| &FaviconCandidate::CompareScore);
|
|
|
| - if (candidates_.size() == sorted_candidates.size() &&
|
| - std::equal(sorted_candidates.begin(), sorted_candidates.end(),
|
| - candidates_.begin())) {
|
| - return;
|
| - }
|
| -
|
| - cancelable_task_tracker_for_candidates_.TryCancelAll();
|
| - image_download_request_.Cancel();
|
| candidates_ = std::move(sorted_candidates);
|
| - num_image_download_requests_ = 0;
|
| - current_candidate_index_ = 0u;
|
| - best_favicon_ = DownloadedFavicon();
|
|
|
| // TODO(davemoore) Should clear on empty url. Currently we ignore it.
|
| // This appears to be what FF does as well.
|
| @@ -419,9 +522,11 @@ void FaviconHandler::OnDidDownloadFavicon(
|
| num_image_download_requests_);
|
| // We have either found the ideal candidate or run out of candidates.
|
| if (best_favicon_.candidate.icon_type != favicon_base::INVALID_ICON) {
|
| - // No more icons to request, set the favicon from the candidate.
|
| - SetFavicon(best_favicon_.candidate.icon_url, best_favicon_.image,
|
| - best_favicon_.candidate.icon_type);
|
| + // No more icons to request, set the favicon from the candidate. The
|
| + // manifest URL, if available, is used instead of the icon URL.
|
| + SetFavicon(manifest_url_.is_empty() ? best_favicon_.candidate.icon_url
|
| + : manifest_url_,
|
| + best_favicon_.image, best_favicon_.candidate.icon_type);
|
| }
|
| // Clear download related state.
|
| current_candidate_index_ = candidates_.size();
|
| @@ -439,6 +544,7 @@ const std::vector<GURL> FaviconHandler::GetIconURLs() const {
|
|
|
| bool FaviconHandler::HasPendingTasksForTest() {
|
| return !image_download_request_.IsCancelled() ||
|
| + !manifest_download_request_.IsCancelled() ||
|
| cancelable_task_tracker_for_page_url_.HasTrackedTasks() ||
|
| cancelable_task_tracker_for_candidates_.HasTrackedTasks();
|
| }
|
| @@ -476,32 +582,40 @@ void FaviconHandler::OnFaviconDataForInitialURLFromFaviconService(
|
| }
|
|
|
| void FaviconHandler::DownloadCurrentCandidateOrAskFaviconService() {
|
| - GURL icon_url = current_candidate()->icon_url;
|
| - favicon_base::IconType icon_type = current_candidate()->icon_type;
|
| -
|
| - if (redownload_icons_) {
|
| + const GURL icon_url = current_candidate()->icon_url;
|
| + const favicon_base::IconType icon_type = current_candidate()->icon_type;
|
| + // If the icons listed in a manifest are being processed, skip the cache
|
| + // lookup for |icon_url| since the manifest's URL is used for caching, not the
|
| + // icon URL, and this lookup has happened earlier.
|
| + if (redownload_icons_ || !manifest_url_.is_empty()) {
|
| // We have the mapping, but the favicon is out of date. Download it now.
|
| ScheduleImageDownload(icon_url, icon_type);
|
| } else {
|
| - // We don't know the favicon, but we may have previously downloaded the
|
| - // favicon for another page that shares the same favicon. Ask for the
|
| - // favicon given the favicon URL.
|
| - if (delegate_->IsOffTheRecord()) {
|
| - service_->GetFavicon(
|
| - icon_url, icon_type, preferred_icon_size(),
|
| - base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)),
|
| - &cancelable_task_tracker_for_candidates_);
|
| - } else {
|
| - // Ask the history service for the icon. This does two things:
|
| - // 1. Attempts to fetch the favicon data from the database.
|
| - // 2. If the favicon exists in the database, this updates the database to
|
| - // include the mapping between the page url and the favicon url.
|
| - // This is asynchronous. The history service will call back when done.
|
| - service_->UpdateFaviconMappingsAndFetch(
|
| - url_, icon_url, icon_type, preferred_icon_size(),
|
| - base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)),
|
| - &cancelable_task_tracker_for_candidates_);
|
| - }
|
| + GetFaviconAndUpdateMappingsUnlessIncognito(
|
| + icon_url, icon_type,
|
| + base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)));
|
| + }
|
| +}
|
| +
|
| +void FaviconHandler::GetFaviconAndUpdateMappingsUnlessIncognito(
|
| + const GURL& icon_url,
|
| + favicon_base::IconType icon_type,
|
| + const favicon_base::FaviconResultsCallback& callback) {
|
| + // We don't know the favicon, but we may have previously downloaded the
|
| + // favicon for another page that shares the same favicon. Ask for the
|
| + // favicon given the favicon URL.
|
| + if (delegate_->IsOffTheRecord()) {
|
| + service_->GetFavicon(icon_url, icon_type, preferred_icon_size(), callback,
|
| + &cancelable_task_tracker_for_candidates_);
|
| + } else {
|
| + // Ask the history service for the icon. This does two things:
|
| + // 1. Attempts to fetch the favicon data from the database.
|
| + // 2. If the favicon exists in the database, this updates the database to
|
| + // include the mapping between the page url and the favicon url.
|
| + // This is asynchronous. The history service will call back when done.
|
| + service_->UpdateFaviconMappingsAndFetch(
|
| + url_, icon_url, icon_type, preferred_icon_size(), callback,
|
| + &cancelable_task_tracker_for_candidates_);
|
| }
|
| }
|
|
|
|
|