| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/large_icon_service.h" | 5 #include "components/favicon/core/large_icon_service.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/location.h" | 10 #include "base/location.h" |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 } | 49 } |
| 50 | 50 |
| 51 GURL GetIconUrlForGoogleServerV2(const GURL& page_url, | 51 GURL GetIconUrlForGoogleServerV2(const GURL& page_url, |
| 52 int min_source_size_in_pixel) { | 52 int min_source_size_in_pixel) { |
| 53 return GURL(base::StringPrintf( | 53 return GURL(base::StringPrintf( |
| 54 kGoogleServerV2RequestFormat, kGoogleServerV2DesiredSizeInPixel, | 54 kGoogleServerV2RequestFormat, kGoogleServerV2DesiredSizeInPixel, |
| 55 min_source_size_in_pixel, kGoogleServerV2MaxSizeInPixel, | 55 min_source_size_in_pixel, kGoogleServerV2MaxSizeInPixel, |
| 56 page_url.spec().c_str())); | 56 page_url.spec().c_str())); |
| 57 } | 57 } |
| 58 | 58 |
| 59 bool IsDbResultAdequate(const favicon_base::FaviconRawBitmapResult& db_result, |
| 60 int min_source_size) { |
| 61 return db_result.is_valid() && |
| 62 db_result.pixel_size.width() == db_result.pixel_size.height() && |
| 63 db_result.pixel_size.width() >= min_source_size; |
| 64 } |
| 65 |
| 66 // Wraps the PNG data in |db_result| in a gfx::Image. If |desired_size| is not |
| 67 // 0, the image gets decoded and resized to |desired_size| (in px). Must run on |
| 68 // a background thread in production. |
| 69 gfx::Image ResizeLargeIconOnBackgroundThread( |
| 70 const favicon_base::FaviconRawBitmapResult& db_result, |
| 71 int desired_size) { |
| 72 gfx::Image image = gfx::Image::CreateFrom1xPNGBytes( |
| 73 db_result.bitmap_data->front(), db_result.bitmap_data->size()); |
| 74 |
| 75 if (desired_size == 0 || db_result.pixel_size.width() == desired_size) { |
| 76 return image; |
| 77 } |
| 78 |
| 79 SkBitmap resized = skia::ImageOperations::Resize( |
| 80 image.AsBitmap(), skia::ImageOperations::RESIZE_LANCZOS3, desired_size, |
| 81 desired_size); |
| 82 return gfx::Image::CreateFrom1xBitmap(resized); |
| 83 } |
| 84 |
| 85 // Processes the |db_result| and writes the result into |raw_result| if |
| 86 // |raw_result| is not nullptr or to |bitmap|, otherwise. If |db_result| is not |
| 87 // valid or is smaller than |min_source_size|, the resulting fallback style is |
| 88 // written into |fallback_icon_style|. |
| 89 void ProcessIconOnBackgroundThread( |
| 90 const favicon_base::FaviconRawBitmapResult& db_result, |
| 91 int min_source_size, |
| 92 int desired_size, |
| 93 favicon_base::FaviconRawBitmapResult* raw_result, |
| 94 SkBitmap* bitmap, |
| 95 favicon_base::FallbackIconStyle* fallback_icon_style) { |
| 96 if (IsDbResultAdequate(db_result, min_source_size)) { |
| 97 gfx::Image image; |
| 98 image = ResizeLargeIconOnBackgroundThread(db_result, desired_size); |
| 99 |
| 100 if (!image.IsEmpty()) { |
| 101 if (raw_result) { |
| 102 *raw_result = db_result; |
| 103 if (desired_size != 0) |
| 104 raw_result->pixel_size = gfx::Size(desired_size, desired_size); |
| 105 raw_result->bitmap_data = image.As1xPNGBytes(); |
| 106 } |
| 107 if (bitmap) { |
| 108 *bitmap = image.AsBitmap(); |
| 109 } |
| 110 return; |
| 111 } |
| 112 } |
| 113 |
| 114 if (!fallback_icon_style) |
| 115 return; |
| 116 |
| 117 *fallback_icon_style = favicon_base::FallbackIconStyle(); |
| 118 if (db_result.is_valid()) { |
| 119 favicon_base::SetDominantColorAsBackground(db_result.bitmap_data, |
| 120 fallback_icon_style); |
| 121 } |
| 122 } |
| 123 |
| 59 // Processes the bitmap data returned from the FaviconService as part of a | 124 // Processes the bitmap data returned from the FaviconService as part of a |
| 60 // LargeIconService request. | 125 // LargeIconService request. |
| 61 class LargeIconWorker : public base::RefCountedThreadSafe<LargeIconWorker> { | 126 class LargeIconWorker : public base::RefCountedThreadSafe<LargeIconWorker> { |
| 62 public: | 127 public: |
| 128 // Exactly one of the callbacks is expected to be non-null. |
| 63 LargeIconWorker(int min_source_size_in_pixel, | 129 LargeIconWorker(int min_source_size_in_pixel, |
| 64 int desired_size_in_pixel, | 130 int desired_size_in_pixel, |
| 65 favicon_base::LargeIconCallback callback, | 131 favicon_base::LargeIconCallback raw_bitmap_callback, |
| 132 favicon_base::LargeIconImageCallback image_callback, |
| 66 scoped_refptr<base::TaskRunner> background_task_runner, | 133 scoped_refptr<base::TaskRunner> background_task_runner, |
| 67 base::CancelableTaskTracker* tracker); | 134 base::CancelableTaskTracker* tracker); |
| 68 | 135 |
| 69 // Must run on the owner (UI) thread in production. | 136 // Must run on the owner (UI) thread in production. |
| 70 // Intermediate callback for GetLargeIconOrFallbackStyle(). Invokes | 137 // Intermediate callback for GetLargeIconOrFallbackStyle(). Invokes |
| 71 // ProcessIconOnBackgroundThread() so we do not perform complex image | 138 // ProcessIconOnBackgroundThread() so we do not perform complex image |
| 72 // operations on the UI thread. | 139 // operations on the UI thread. |
| 73 void OnIconLookupComplete( | 140 void OnIconLookupComplete( |
| 74 const favicon_base::FaviconRawBitmapResult& bitmap_result); | 141 const favicon_base::FaviconRawBitmapResult& db_result); |
| 75 | 142 |
| 76 private: | 143 private: |
| 77 friend class base::RefCountedThreadSafe<LargeIconWorker>; | 144 friend class base::RefCountedThreadSafe<LargeIconWorker>; |
| 78 | 145 |
| 79 ~LargeIconWorker(); | 146 ~LargeIconWorker(); |
| 80 | 147 |
| 81 // Must run on a background thread in production. | |
| 82 // Tries to resize |bitmap_result_| and pass the output to |callback_|. If | |
| 83 // that does not work, computes the icon fallback style and uses it to | |
| 84 // invoke |callback_|. This must be run on a background thread because image | |
| 85 // resizing and dominant color extraction can be expensive. | |
| 86 void ProcessIconOnBackgroundThread(); | |
| 87 | |
| 88 // Must run on a background thread in production. | |
| 89 // If |bitmap_result_| is square and large enough (>= |min_source_in_pixel_|), | |
| 90 // resizes it to |desired_size_in_pixel_| (but if |desired_size_in_pixel_| is | |
| 91 // 0 then don't resize). If successful, stores the resulting bitmap data | |
| 92 // into |resized_bitmap_result| and returns true. | |
| 93 bool ResizeLargeIconOnBackgroundThreadIfValid( | |
| 94 favicon_base::FaviconRawBitmapResult* resized_bitmap_result); | |
| 95 | |
| 96 // Must run on the owner (UI) thread in production. | 148 // Must run on the owner (UI) thread in production. |
| 97 // Invoked when ProcessIconOnBackgroundThread() is done. | 149 // Invoked when ProcessIconOnBackgroundThread() is done. |
| 98 void OnIconProcessingComplete(); | 150 void OnIconProcessingComplete(); |
| 99 | 151 |
| 100 int min_source_size_in_pixel_; | 152 int min_source_size_in_pixel_; |
| 101 int desired_size_in_pixel_; | 153 int desired_size_in_pixel_; |
| 102 favicon_base::LargeIconCallback callback_; | 154 favicon_base::LargeIconCallback raw_bitmap_callback_; |
| 155 favicon_base::LargeIconImageCallback image_callback_; |
| 103 scoped_refptr<base::TaskRunner> background_task_runner_; | 156 scoped_refptr<base::TaskRunner> background_task_runner_; |
| 104 base::CancelableTaskTracker* tracker_; | 157 base::CancelableTaskTracker* tracker_; |
| 105 favicon_base::FaviconRawBitmapResult bitmap_result_; | 158 |
| 106 std::unique_ptr<favicon_base::LargeIconResult> result_; | 159 favicon_base::FaviconRawBitmapResult raw_bitmap_result_; |
| 160 SkBitmap bitmap_result_; |
| 161 std::unique_ptr<favicon_base::FallbackIconStyle> fallback_icon_style_; |
| 107 | 162 |
| 108 DISALLOW_COPY_AND_ASSIGN(LargeIconWorker); | 163 DISALLOW_COPY_AND_ASSIGN(LargeIconWorker); |
| 109 }; | 164 }; |
| 110 | 165 |
| 111 LargeIconWorker::LargeIconWorker( | 166 LargeIconWorker::LargeIconWorker( |
| 112 int min_source_size_in_pixel, | 167 int min_source_size_in_pixel, |
| 113 int desired_size_in_pixel, | 168 int desired_size_in_pixel, |
| 114 favicon_base::LargeIconCallback callback, | 169 favicon_base::LargeIconCallback raw_bitmap_callback, |
| 170 favicon_base::LargeIconImageCallback image_callback, |
| 115 scoped_refptr<base::TaskRunner> background_task_runner, | 171 scoped_refptr<base::TaskRunner> background_task_runner, |
| 116 base::CancelableTaskTracker* tracker) | 172 base::CancelableTaskTracker* tracker) |
| 117 : min_source_size_in_pixel_(min_source_size_in_pixel), | 173 : min_source_size_in_pixel_(min_source_size_in_pixel), |
| 118 desired_size_in_pixel_(desired_size_in_pixel), | 174 desired_size_in_pixel_(desired_size_in_pixel), |
| 119 callback_(callback), | 175 raw_bitmap_callback_(raw_bitmap_callback), |
| 176 image_callback_(image_callback), |
| 120 background_task_runner_(background_task_runner), | 177 background_task_runner_(background_task_runner), |
| 121 tracker_(tracker) { | 178 tracker_(tracker), |
| 122 } | 179 fallback_icon_style_( |
| 180 base::MakeUnique<favicon_base::FallbackIconStyle>()) {} |
| 123 | 181 |
| 124 LargeIconWorker::~LargeIconWorker() { | 182 LargeIconWorker::~LargeIconWorker() { |
| 125 } | 183 } |
| 126 | 184 |
| 127 void LargeIconWorker::OnIconLookupComplete( | 185 void LargeIconWorker::OnIconLookupComplete( |
| 128 const favicon_base::FaviconRawBitmapResult& bitmap_result) { | 186 const favicon_base::FaviconRawBitmapResult& db_result) { |
| 129 bitmap_result_ = bitmap_result; | |
| 130 tracker_->PostTaskAndReply( | 187 tracker_->PostTaskAndReply( |
| 131 background_task_runner_.get(), FROM_HERE, | 188 background_task_runner_.get(), FROM_HERE, |
| 132 base::Bind(&LargeIconWorker::ProcessIconOnBackgroundThread, this), | 189 base::Bind(&ProcessIconOnBackgroundThread, db_result, |
| 190 min_source_size_in_pixel_, desired_size_in_pixel_, |
| 191 raw_bitmap_callback_ ? &raw_bitmap_result_ : nullptr, |
| 192 image_callback_ ? &bitmap_result_ : nullptr, |
| 193 fallback_icon_style_.get()), |
| 133 base::Bind(&LargeIconWorker::OnIconProcessingComplete, this)); | 194 base::Bind(&LargeIconWorker::OnIconProcessingComplete, this)); |
| 134 } | 195 } |
| 135 | 196 |
| 136 void LargeIconWorker::ProcessIconOnBackgroundThread() { | 197 void LargeIconWorker::OnIconProcessingComplete() { |
| 137 favicon_base::FaviconRawBitmapResult resized_bitmap_result; | 198 // If |raw_bitmap_callback_| is provided, return the raw result. |
| 138 if (ResizeLargeIconOnBackgroundThreadIfValid(&resized_bitmap_result)) { | 199 if (raw_bitmap_callback_) { |
| 139 result_.reset( | 200 if (raw_bitmap_result_.is_valid()) { |
| 140 new favicon_base::LargeIconResult(resized_bitmap_result)); | 201 raw_bitmap_callback_.Run( |
| 141 } else { | 202 favicon_base::LargeIconResult(raw_bitmap_result_)); |
| 142 // Failed to resize |bitmap_result_|, so compute fallback icon style. | 203 return; |
| 143 std::unique_ptr<favicon_base::FallbackIconStyle> fallback_icon_style( | |
| 144 new favicon_base::FallbackIconStyle()); | |
| 145 if (bitmap_result_.is_valid()) { | |
| 146 favicon_base::SetDominantColorAsBackground( | |
| 147 bitmap_result_.bitmap_data, fallback_icon_style.get()); | |
| 148 } | 204 } |
| 149 result_.reset( | 205 raw_bitmap_callback_.Run( |
| 150 new favicon_base::LargeIconResult(fallback_icon_style.release())); | 206 favicon_base::LargeIconResult(fallback_icon_style_.release())); |
| 207 return; |
| 151 } | 208 } |
| 152 } | |
| 153 | 209 |
| 154 bool LargeIconWorker::ResizeLargeIconOnBackgroundThreadIfValid( | 210 if (!bitmap_result_.isNull()) { |
| 155 favicon_base::FaviconRawBitmapResult* resized_bitmap_result) { | 211 image_callback_.Run(favicon_base::LargeIconImageResult( |
| 156 // Require bitmap to be valid and square. | 212 gfx::Image::CreateFrom1xBitmap(bitmap_result_))); |
| 157 if (!bitmap_result_.is_valid() || | 213 return; |
| 158 bitmap_result_.pixel_size.width() != bitmap_result_.pixel_size.height()) | 214 } |
| 159 return false; | 215 image_callback_.Run( |
| 160 | 216 favicon_base::LargeIconImageResult(fallback_icon_style_.release())); |
| 161 // Require bitmap to be large enough. It's square, so just check width. | |
| 162 if (bitmap_result_.pixel_size.width() < min_source_size_in_pixel_) | |
| 163 return false; | |
| 164 | |
| 165 *resized_bitmap_result = bitmap_result_; | |
| 166 | |
| 167 // Special case: Can use |bitmap_result_| as is. | |
| 168 if (desired_size_in_pixel_ == 0 || | |
| 169 bitmap_result_.pixel_size.width() == desired_size_in_pixel_) | |
| 170 return true; | |
| 171 | |
| 172 // Resize bitmap: decode PNG, resize, and re-encode PNG. | |
| 173 SkBitmap decoded_bitmap; | |
| 174 if (!gfx::PNGCodec::Decode(bitmap_result_.bitmap_data->front(), | |
| 175 bitmap_result_.bitmap_data->size(), &decoded_bitmap)) | |
| 176 return false; | |
| 177 | |
| 178 SkBitmap resized_bitmap = skia::ImageOperations::Resize( | |
| 179 decoded_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, | |
| 180 desired_size_in_pixel_, desired_size_in_pixel_); | |
| 181 | |
| 182 std::vector<unsigned char> bitmap_data; | |
| 183 if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_bitmap, false, &bitmap_data)) | |
| 184 return false; | |
| 185 | |
| 186 resized_bitmap_result->pixel_size = | |
| 187 gfx::Size(desired_size_in_pixel_, desired_size_in_pixel_); | |
| 188 resized_bitmap_result->bitmap_data = | |
| 189 base::RefCountedBytes::TakeVector(&bitmap_data); | |
| 190 return true; | |
| 191 } | |
| 192 | |
| 193 void LargeIconWorker::OnIconProcessingComplete() { | |
| 194 callback_.Run(*result_); | |
| 195 } | 217 } |
| 196 | 218 |
| 197 void OnFetchIconFromGoogleServerComplete( | 219 void OnFetchIconFromGoogleServerComplete( |
| 198 FaviconService* favicon_service, | 220 FaviconService* favicon_service, |
| 199 const GURL& page_url, | 221 const GURL& page_url, |
| 200 const base::Callback<void(bool success)>& callback, | 222 const base::Callback<void(bool success)>& callback, |
| 201 const std::string& icon_url, | 223 const std::string& icon_url, |
| 202 const gfx::Image& image, | 224 const gfx::Image& image, |
| 203 const image_fetcher::RequestMetadata& metadata) { | 225 const image_fetcher::RequestMetadata& metadata) { |
| 204 if (image.IsEmpty()) { | 226 if (image.IsEmpty()) { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 235 image_fetcher_(std::move(image_fetcher)) { | 257 image_fetcher_(std::move(image_fetcher)) { |
| 236 large_icon_types_.push_back(favicon_base::IconType::FAVICON); | 258 large_icon_types_.push_back(favicon_base::IconType::FAVICON); |
| 237 large_icon_types_.push_back(favicon_base::IconType::TOUCH_ICON); | 259 large_icon_types_.push_back(favicon_base::IconType::TOUCH_ICON); |
| 238 large_icon_types_.push_back(favicon_base::IconType::TOUCH_PRECOMPOSED_ICON); | 260 large_icon_types_.push_back(favicon_base::IconType::TOUCH_PRECOMPOSED_ICON); |
| 239 } | 261 } |
| 240 | 262 |
| 241 LargeIconService::~LargeIconService() { | 263 LargeIconService::~LargeIconService() { |
| 242 } | 264 } |
| 243 | 265 |
| 244 base::CancelableTaskTracker::TaskId | 266 base::CancelableTaskTracker::TaskId |
| 245 LargeIconService::GetLargeIconOrFallbackStyle( | 267 LargeIconService::GetLargeIconOrFallbackStyle( |
| 246 const GURL& page_url, | 268 const GURL& page_url, |
| 247 int min_source_size_in_pixel, | 269 int min_source_size_in_pixel, |
| 248 int desired_size_in_pixel, | 270 int desired_size_in_pixel, |
| 249 const favicon_base::LargeIconCallback& callback, | 271 const favicon_base::LargeIconCallback& raw_bitmap_callback, |
| 250 base::CancelableTaskTracker* tracker) { | 272 base::CancelableTaskTracker* tracker) { |
| 251 DCHECK_LE(1, min_source_size_in_pixel); | 273 return GetLargeIconOrFallbackStyleImpl( |
| 252 DCHECK_LE(0, desired_size_in_pixel); | 274 page_url, min_source_size_in_pixel, desired_size_in_pixel, |
| 275 raw_bitmap_callback, favicon_base::LargeIconImageCallback(), tracker); |
| 276 } |
| 253 | 277 |
| 254 scoped_refptr<LargeIconWorker> worker = | 278 base::CancelableTaskTracker::TaskId |
| 255 new LargeIconWorker(min_source_size_in_pixel, desired_size_in_pixel, | 279 LargeIconService::GetLargeIconImageOrFallbackStyle( |
| 256 callback, background_task_runner_, tracker); | 280 const GURL& page_url, |
| 257 | 281 int min_source_size_in_pixel, |
| 258 // TODO(beaudoin): For now this is just a wrapper around | 282 int desired_size_in_pixel, |
| 259 // GetLargestRawFaviconForPageURL. Add the logic required to select the best | 283 const favicon_base::LargeIconImageCallback& image_callback, |
| 260 // possible large icon. Also add logic to fetch-on-demand when the URL of | 284 base::CancelableTaskTracker* tracker) { |
| 261 // a large icon is known but its bitmap is not available. | 285 return GetLargeIconOrFallbackStyleImpl( |
| 262 return favicon_service_->GetLargestRawFaviconForPageURL( | 286 page_url, min_source_size_in_pixel, desired_size_in_pixel, |
| 263 page_url, large_icon_types_, min_source_size_in_pixel, | 287 favicon_base::LargeIconCallback(), image_callback, tracker); |
| 264 base::Bind(&LargeIconWorker::OnIconLookupComplete, worker), | |
| 265 tracker); | |
| 266 } | 288 } |
| 267 | 289 |
| 268 void LargeIconService:: | 290 void LargeIconService:: |
| 269 GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( | 291 GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| 270 const GURL& page_url, | 292 const GURL& page_url, |
| 271 int min_source_size_in_pixel, | 293 int min_source_size_in_pixel, |
| 272 const base::Callback<void(bool success)>& callback) { | 294 const base::Callback<void(bool success)>& callback) { |
| 273 DCHECK_LE(0, min_source_size_in_pixel); | 295 DCHECK_LE(0, min_source_size_in_pixel); |
| 274 | 296 |
| 275 const GURL trimmed_page_url = TrimPageUrlForGoogleServer(page_url); | 297 const GURL trimmed_page_url = TrimPageUrlForGoogleServer(page_url); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 286 } | 308 } |
| 287 | 309 |
| 288 image_fetcher_->SetDataUseServiceName( | 310 image_fetcher_->SetDataUseServiceName( |
| 289 data_use_measurement::DataUseUserData::LARGE_ICON_SERVICE); | 311 data_use_measurement::DataUseUserData::LARGE_ICON_SERVICE); |
| 290 image_fetcher_->StartOrQueueNetworkRequest( | 312 image_fetcher_->StartOrQueueNetworkRequest( |
| 291 icon_url.spec(), icon_url, | 313 icon_url.spec(), icon_url, |
| 292 base::Bind(&OnFetchIconFromGoogleServerComplete, favicon_service_, | 314 base::Bind(&OnFetchIconFromGoogleServerComplete, favicon_service_, |
| 293 page_url, callback)); | 315 page_url, callback)); |
| 294 } | 316 } |
| 295 | 317 |
| 318 base::CancelableTaskTracker::TaskId |
| 319 LargeIconService::GetLargeIconOrFallbackStyleImpl( |
| 320 const GURL& page_url, |
| 321 int min_source_size_in_pixel, |
| 322 int desired_size_in_pixel, |
| 323 const favicon_base::LargeIconCallback& raw_bitmap_callback, |
| 324 const favicon_base::LargeIconImageCallback& image_callback, |
| 325 base::CancelableTaskTracker* tracker) { |
| 326 DCHECK_LE(1, min_source_size_in_pixel); |
| 327 DCHECK_LE(0, desired_size_in_pixel); |
| 328 |
| 329 scoped_refptr<LargeIconWorker> worker = new LargeIconWorker( |
| 330 min_source_size_in_pixel, desired_size_in_pixel, raw_bitmap_callback, |
| 331 image_callback, background_task_runner_, tracker); |
| 332 |
| 333 // TODO(beaudoin): For now this is just a wrapper around |
| 334 // GetLargestRawFaviconForPageURL. Add the logic required to select the best |
| 335 // possible large icon. Also add logic to fetch-on-demand when the URL of |
| 336 // a large icon is known but its bitmap is not available. |
| 337 return favicon_service_->GetLargestRawFaviconForPageURL( |
| 338 page_url, large_icon_types_, min_source_size_in_pixel, |
| 339 base::Bind(&LargeIconWorker::OnIconLookupComplete, worker), tracker); |
| 340 } |
| 341 |
| 296 } // namespace favicon | 342 } // namespace favicon |
| OLD | NEW |