Chromium Code Reviews| Index: components/favicon/core/large_icon_service.cc |
| diff --git a/components/favicon/core/large_icon_service.cc b/components/favicon/core/large_icon_service.cc |
| index fa42fe4d8f54f4189fc24d6595e9cd38b4369110..3a1e65b77939c9037647bf551c1de23addbd4bd2 100644 |
| --- a/components/favicon/core/large_icon_service.cc |
| +++ b/components/favicon/core/large_icon_service.cc |
| @@ -4,39 +4,78 @@ |
| #include "components/favicon/core/large_icon_service.h" |
| +#include <algorithm> |
| #include <memory> |
| #include "base/bind.h" |
| +#include "base/feature_list.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| +#include "base/strings/stringprintf.h" |
| #include "base/task_runner.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| +#include "components/data_use_measurement/core/data_use_user_data.h" |
| #include "components/favicon/core/favicon_service.h" |
| +#include "components/favicon/core/features.h" |
| #include "components/favicon_base/fallback_icon_style.h" |
| #include "components/favicon_base/favicon_types.h" |
| +#include "components/favicon_base/favicon_util.h" |
| +#include "components/image_fetcher/image_fetcher.h" |
| #include "skia/ext/image_operations.h" |
| #include "ui/gfx/codec/png_codec.h" |
| #include "ui/gfx/geometry/size.h" |
| +using image_fetcher::ImageFetcher; |
| + |
| +namespace favicon { |
| + |
| namespace { |
| +const double kGoogleServerDefaultDesiredSize = 128; |
| +const double kGoogleServerMinSizeToDesiredSizeFactor = 2.0; |
| +const double kGoogleServerDesiredSizeToMaxSizeFactor = 2.0; |
| + |
| +const char kGoogleServer1RequestFormat[] = |
| + "https://s2.googleusercontent.com/s2/" |
| + "favicons?domain=%s&src=chrome_large_icons&sz=%d&alt=404"; |
| +const int kGoogleServer1SupportedSizes[] = {16, 24, 32, 48, 64}; |
| + |
| +// Type APPLE_TOUCH includes TOUCH_ICONs as well as FAVICONs as fallback. |
| +const char kGoogleServer2RequestFormat[] = |
| + "https://t0.gstatic.com/" |
| + "faviconV2?url=%s&type=APPLE_TOUCH&size=%d&min_size=%d&max_size=%d"; |
|
pkotwicz
2017/02/10 20:44:34
Doesn't chrome want to prioritize FAVICONS over AP
jkrcal
2017/02/13 14:39:46
The code for FaviconService also prioritizes APPLE
|
| + |
| +int GetClosestSizeSupportedByGoogleServer1(int arbitrary_size) { |
| + // Take the smallest size larger than |arbitrary_size|. |
| + for (int size : kGoogleServer1SupportedSizes) { |
| + if (size >= arbitrary_size) |
| + return size; |
| + } |
| + // Or at least the largest available size. |
| + return kGoogleServer1SupportedSizes[arraysize(kGoogleServer1SupportedSizes) - |
| + 1]; |
| +} |
| + |
| // Processes the bitmap data returned from the FaviconService as part of a |
| // LargeIconService request. |
| class LargeIconWorker : public base::RefCountedThreadSafe<LargeIconWorker> { |
| public: |
| - LargeIconWorker(int min_source_size_in_pixel, |
| + LargeIconWorker(const GURL& page_url, |
| + int min_source_size_in_pixel, |
| int desired_size_in_pixel, |
| favicon_base::LargeIconCallback callback, |
| scoped_refptr<base::TaskRunner> background_task_runner, |
| - base::CancelableTaskTracker* tracker); |
| + base::CancelableTaskTracker* tracker, |
| + FaviconService* favicon_service, |
| + ImageFetcher* image_fetcher); |
| // Must run on the owner (UI) thread in production. |
| // Intermediate callback for GetLargeIconOrFallbackStyle(). Invokes |
| // ProcessIconOnBackgroundThread() so we do not perform complex image |
| // operations on the UI thread. |
| - void OnIconLookupComplete( |
| + void OnCachedIconLookupComplete( |
| const favicon_base::FaviconRawBitmapResult& bitmap_result); |
| private: |
| @@ -49,25 +88,46 @@ class LargeIconWorker : public base::RefCountedThreadSafe<LargeIconWorker> { |
| // that does not work, computes the icon fallback style and uses it to |
| // invoke |callback_|. This must be run on a background thread because image |
| // resizing and dominant color extraction can be expensive. |
| - void ProcessIconOnBackgroundThread(); |
| + void ProcessCachedIconOnBackgroundThread(); |
| // Must run on a background thread in production. |
| // If |bitmap_result_| is square and large enough (>= |min_source_in_pixel_|), |
| // resizes it to |desired_size_in_pixel_| (but if |desired_size_in_pixel_| is |
| // 0 then don't resize). If successful, stores the resulting bitmap data |
| // into |resized_bitmap_result| and returns true. |
| - bool ResizeLargeIconOnBackgroundThreadIfValid( |
| + bool ResizeCachedIconOnBackgroundThreadIfValid( |
| favicon_base::FaviconRawBitmapResult* resized_bitmap_result); |
| // Must run on the owner (UI) thread in production. |
| // Invoked when ProcessIconOnBackgroundThread() is done. |
| - void OnIconProcessingComplete(); |
| + void OnCachedIconProcessingComplete(); |
| + |
| + // Create the GET URL that requests the desired favicon from a Google favicon |
| + // server (version 1 / version 2). |
| + GURL GetIconUrlForGoogleServer1() const; |
| + GURL GetIconUrlForGoogleServer2() const; |
| + // Fetches an icon for the given |icon_url| and stores it in the favicon DB. |
| + void FetchIconFromGoogleServer(const GURL& icon_url); |
| + void OnFetchIconFromGoogleServerComplete(const std::string& icon_url, |
| + const gfx::Image& image); |
| + |
| + // Must run on a background thread in production. |
| + void ProcessAndStoreIconFromGoogleServerOnBackgroundThread( |
| + const GURL& icon_url, |
| + const gfx::Image& image); |
| + |
| + GURL page_url_; |
| int min_source_size_in_pixel_; |
| int desired_size_in_pixel_; |
| favicon_base::LargeIconCallback callback_; |
| scoped_refptr<base::TaskRunner> background_task_runner_; |
| base::CancelableTaskTracker* tracker_; |
| + FaviconService* favicon_service_; |
| + ImageFetcher* image_fetcher_; |
| + |
| + bool icon_found_; |
| + bool large_icon_found_; |
| favicon_base::FaviconRawBitmapResult bitmap_result_; |
| std::unique_ptr<favicon_base::LargeIconResult> result_; |
| @@ -75,35 +135,44 @@ class LargeIconWorker : public base::RefCountedThreadSafe<LargeIconWorker> { |
| }; |
| LargeIconWorker::LargeIconWorker( |
| + const GURL& page_url, |
| int min_source_size_in_pixel, |
| int desired_size_in_pixel, |
| favicon_base::LargeIconCallback callback, |
| scoped_refptr<base::TaskRunner> background_task_runner, |
| - base::CancelableTaskTracker* tracker) |
| - : min_source_size_in_pixel_(min_source_size_in_pixel), |
| + base::CancelableTaskTracker* tracker, |
| + FaviconService* favicon_service, |
| + ImageFetcher* image_fetcher) |
| + : page_url_(page_url), |
| + min_source_size_in_pixel_(min_source_size_in_pixel), |
| desired_size_in_pixel_(desired_size_in_pixel), |
| callback_(callback), |
| background_task_runner_(background_task_runner), |
| - tracker_(tracker) { |
| -} |
| + tracker_(tracker), |
| + favicon_service_(favicon_service), |
| + image_fetcher_(image_fetcher), |
| + icon_found_(false), |
| + large_icon_found_(false) {} |
| LargeIconWorker::~LargeIconWorker() { |
| } |
| -void LargeIconWorker::OnIconLookupComplete( |
| +void LargeIconWorker::OnCachedIconLookupComplete( |
| const favicon_base::FaviconRawBitmapResult& bitmap_result) { |
| bitmap_result_ = bitmap_result; |
| + icon_found_ = bitmap_result_.is_valid(); |
| tracker_->PostTaskAndReply( |
| background_task_runner_.get(), FROM_HERE, |
| - base::Bind(&LargeIconWorker::ProcessIconOnBackgroundThread, this), |
| - base::Bind(&LargeIconWorker::OnIconProcessingComplete, this)); |
| + base::Bind(&LargeIconWorker::ProcessCachedIconOnBackgroundThread, this), |
| + base::Bind(&LargeIconWorker::OnCachedIconProcessingComplete, this)); |
| } |
| -void LargeIconWorker::ProcessIconOnBackgroundThread() { |
| +void LargeIconWorker::ProcessCachedIconOnBackgroundThread() { |
| favicon_base::FaviconRawBitmapResult resized_bitmap_result; |
| - if (ResizeLargeIconOnBackgroundThreadIfValid(&resized_bitmap_result)) { |
| + if (ResizeCachedIconOnBackgroundThreadIfValid(&resized_bitmap_result)) { |
| result_.reset( |
| new favicon_base::LargeIconResult(resized_bitmap_result)); |
| + large_icon_found_ = true; |
| } else { |
| // Failed to resize |bitmap_result_|, so compute fallback icon style. |
| std::unique_ptr<favicon_base::FallbackIconStyle> fallback_icon_style( |
| @@ -113,11 +182,11 @@ void LargeIconWorker::ProcessIconOnBackgroundThread() { |
| bitmap_result_.bitmap_data, fallback_icon_style.get()); |
| } |
| result_.reset( |
| - new favicon_base::LargeIconResult(fallback_icon_style.release())); |
| + new favicon_base::LargeIconResult(std::move(fallback_icon_style))); |
| } |
| } |
| -bool LargeIconWorker::ResizeLargeIconOnBackgroundThreadIfValid( |
| +bool LargeIconWorker::ResizeCachedIconOnBackgroundThreadIfValid( |
| favicon_base::FaviconRawBitmapResult* resized_bitmap_result) { |
| // Require bitmap to be valid and square. |
| if (!bitmap_result_.is_valid() || |
| @@ -156,19 +225,113 @@ bool LargeIconWorker::ResizeLargeIconOnBackgroundThreadIfValid( |
| return true; |
| } |
| -void LargeIconWorker::OnIconProcessingComplete() { |
| +void LargeIconWorker::OnCachedIconProcessingComplete() { |
| callback_.Run(*result_); |
|
pkotwicz
2017/02/10 20:44:34
If we go and fetch results from the Google server
jkrcal
2017/02/13 14:39:46
This is conceptually a UX question to me.
The wa
pkotwicz
2017/02/13 21:33:31
Given how you are planning on using this, I'd rath
jkrcal
2017/02/27 17:31:19
Okay, the new functionality now does not get trigg
|
| + |
| + // If there is a large icon in the cache, we do not need to download anything. |
| + if (large_icon_found_) |
| + return; |
| + // No fetcher available (e.g. in unittests), we cannot download anything. |
| + if (!image_fetcher_) |
| + return; |
| + |
| + // No favicon in the cache. Download it from the server. |
| + |
| + if (desired_size_in_pixel_ == 0) { |
| + // We need to fix a concrete desired size if none is provided. |
| + if (min_source_size_in_pixel_ > 0) { |
| + desired_size_in_pixel_ = |
| + min_source_size_in_pixel_ * kGoogleServerMinSizeToDesiredSizeFactor; |
| + } else { |
| + desired_size_in_pixel_ = kGoogleServerDefaultDesiredSize; |
| + } |
| + } |
| + |
| + if (base::FeatureList::IsEnabled(kFaviconFetchLargeIconFromGoogleServer2)) { |
| + FetchIconFromGoogleServer(GetIconUrlForGoogleServer2()); |
| + } else if (min_source_size_in_pixel_ <= 16 && |
| + base::FeatureList::IsEnabled( |
| + kFaviconFetchLargeIconFromGoogleServer1)) { |
| + FetchIconFromGoogleServer(GetIconUrlForGoogleServer1()); |
| + } |
| } |
| -} // namespace |
| +GURL LargeIconWorker::GetIconUrlForGoogleServer1() const { |
| + int supported_size = |
| + GetClosestSizeSupportedByGoogleServer1(desired_size_in_pixel_); |
| + return GURL(base::StringPrintf(kGoogleServer1RequestFormat, |
| + page_url_.spec().c_str(), supported_size)); |
| +} |
| -namespace favicon { |
| +GURL LargeIconWorker::GetIconUrlForGoogleServer2() const { |
|
pkotwicz
2017/02/10 20:44:34
Different bits of UI want differently sized favico
jkrcal
2017/02/13 14:39:46
Good point! I've fixed min, max, and desired sizes
pkotwicz
2017/02/13 21:33:32
Thank you for pointing this out. I didn't see the
|
| + int max_source_size_in_pixel = |
| + desired_size_in_pixel_ * kGoogleServerDesiredSizeToMaxSizeFactor; |
| + return GURL( |
| + base::StringPrintf(kGoogleServer2RequestFormat, page_url_.spec().c_str(), |
| + desired_size_in_pixel_, min_source_size_in_pixel_, |
| + max_source_size_in_pixel)); |
| +} |
| + |
| +void LargeIconWorker::FetchIconFromGoogleServer(const GURL& icon_url) { |
| + // Do not retry if there is a previous cache miss recorded for |icon_url|. |
| + if (favicon_service_->WasUnableToDownloadFavicon(icon_url)) |
| + return; |
| + |
| + // TODO(jkrcal): Create a large icon service tag. |
| + image_fetcher_->SetDataUseServiceName( |
| + data_use_measurement::DataUseUserData::NOT_TAGGED); |
| + image_fetcher_->StartOrQueueNetworkRequest( |
| + icon_url.spec(), icon_url, |
| + base::Bind(&LargeIconWorker::OnFetchIconFromGoogleServerComplete, |
| + base::Unretained(this))); |
|
pkotwicz
2017/02/10 20:44:34
Don't use base::Unretained(). LargeIconWorker is r
jkrcal
2017/02/13 14:39:46
Done.
|
| +} |
| + |
| +void LargeIconWorker::OnFetchIconFromGoogleServerComplete( |
| + const std::string& icon_url, |
| + const gfx::Image& image) { |
| + if (image.IsEmpty()) { |
| + favicon_service_->UnableToDownloadFavicon(GURL(icon_url)); |
| + return; |
| + } |
| + |
| + tracker_->PostTask( |
| + background_task_runner_.get(), FROM_HERE, |
| + base::Bind(&LargeIconWorker:: |
| + ProcessAndStoreIconFromGoogleServerOnBackgroundThread, |
| + base::Unretained(this), GURL(icon_url), image)); |
|
pkotwicz
2017/02/10 20:44:34
Don't use base::Unretained(). LargeIconWorker is r
jkrcal
2017/02/13 14:39:46
Done.
|
| +} |
| + |
| +void LargeIconWorker::ProcessAndStoreIconFromGoogleServerOnBackgroundThread( |
| + const GURL& icon_url, |
| + const gfx::Image& image) { |
| + SkBitmap icon = image.AsBitmap(); |
|
pkotwicz
2017/02/10 20:44:34
I think that it would be better to store the non-r
jkrcal
2017/02/13 14:39:45
Done.
|
| + SkBitmap resized_icon = favicon_base::ResizeBitmapByDownsamplingIfPossible( |
| + {icon}, desired_size_in_pixel_); |
| + gfx::Image resized_image = gfx::Image::CreateFrom1xBitmap(resized_icon); |
| + favicon_base::SetFaviconColorSpace(&resized_image); |
| + |
| + // We must make sure we do not overwrite the FAVICON if there is any icon in |
| + // the cache (due to sync). |
| + favicon_base::IconType store_as_type = |
| + icon_found_ ? favicon_base::IconType::TOUCH_ICON |
| + : favicon_base::IconType::FAVICON; |
| + |
| + favicon_service_->SetFavicons(page_url_, icon_url, store_as_type, |
| + resized_image); |
| + // Mark the icons as out-of-date so that they are refetched when we visit the |
| + // original page any time in the future. |
| + favicon_service_->SetFaviconOutOfDateForPage(page_url_); |
| +} |
| + |
| +} // namespace |
| LargeIconService::LargeIconService( |
| FaviconService* favicon_service, |
| - const scoped_refptr<base::TaskRunner>& background_task_runner) |
| + const scoped_refptr<base::TaskRunner>& background_task_runner, |
| + std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher) |
| : favicon_service_(favicon_service), |
| - background_task_runner_(background_task_runner) { |
| + background_task_runner_(background_task_runner), |
| + image_fetcher_(std::move(image_fetcher)) { |
| large_icon_types_.push_back(favicon_base::IconType::FAVICON); |
| large_icon_types_.push_back(favicon_base::IconType::TOUCH_ICON); |
| large_icon_types_.push_back(favicon_base::IconType::TOUCH_PRECOMPOSED_ICON); |
| @@ -184,20 +347,21 @@ base::CancelableTaskTracker::TaskId |
| int desired_size_in_pixel, |
| const favicon_base::LargeIconCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| - DCHECK_LE(1, min_source_size_in_pixel); |
| + DCHECK_LE(0, min_source_size_in_pixel); |
| DCHECK_LE(0, desired_size_in_pixel); |
| - scoped_refptr<LargeIconWorker> worker = |
| - new LargeIconWorker(min_source_size_in_pixel, desired_size_in_pixel, |
| - callback, background_task_runner_, tracker); |
| + scoped_refptr<LargeIconWorker> worker = new LargeIconWorker( |
| + page_url, min_source_size_in_pixel, desired_size_in_pixel, callback, |
| + background_task_runner_, tracker, favicon_service_, image_fetcher_.get()); |
| - // TODO(beaudoin): For now this is just a wrapper around |
| - // GetLargestRawFaviconForPageURL. Add the logic required to select the best |
| - // possible large icon. Also add logic to fetch-on-demand when the URL of |
| - // a large icon is known but its bitmap is not available. |
| + // TODO(jkrcal): For now this is just a wrapper around |
| + // GetLargestRawFaviconForPageURL. Consider extending |
| + // GetRawFaviconForPageURL() by min_original_size_in_pixel and migrating |
| + // there. Otherwise, add the logic required to select the best possible large |
| + // icon. |
|
pkotwicz
2017/02/10 20:44:34
You should use CancelableTaskTracker::NewTrackedTa
jkrcal
2017/02/13 14:39:45
Well, I noticed this function would give me what I
pkotwicz
2017/02/13 21:33:31
I didn't see that comment. Looks like the author o
|
| return favicon_service_->GetLargestRawFaviconForPageURL( |
|
pkotwicz
2017/02/10 20:44:34
You can't use GetLargestRawFaviconForPageURL() her
jkrcal
2017/02/13 14:39:46
I am fine with using GetRawFaviconForPageURL() and
pkotwicz
2017/02/13 21:33:31
I have forgotten about the filtering in ThumbnailD
jkrcal
2017/02/27 17:31:19
Can we leave that for a further CL?
pkotwicz
2017/02/28 01:51:56
Doing this in a follow up CL is OK
|
| page_url, large_icon_types_, min_source_size_in_pixel, |
| - base::Bind(&LargeIconWorker::OnIconLookupComplete, worker), |
| + base::Bind(&LargeIconWorker::OnCachedIconLookupComplete, worker), |
| tracker); |
| } |