| 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 <algorithm> |
| 7 #include <memory> | 8 #include <memory> |
| 8 | 9 |
| 9 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/feature_list.h" |
| 10 #include "base/location.h" | 12 #include "base/location.h" |
| 11 #include "base/logging.h" | 13 #include "base/logging.h" |
| 12 #include "base/macros.h" | 14 #include "base/macros.h" |
| 15 #include "base/memory/ptr_util.h" |
| 13 #include "base/memory/ref_counted.h" | 16 #include "base/memory/ref_counted.h" |
| 17 #include "base/strings/stringprintf.h" |
| 14 #include "base/task_runner.h" | 18 #include "base/task_runner.h" |
| 19 #include "base/task_runner_util.h" |
| 15 #include "base/threading/sequenced_worker_pool.h" | 20 #include "base/threading/sequenced_worker_pool.h" |
| 21 #include "base/threading/thread_task_runner_handle.h" |
| 22 #include "components/data_use_measurement/core/data_use_user_data.h" |
| 16 #include "components/favicon/core/favicon_service.h" | 23 #include "components/favicon/core/favicon_service.h" |
| 24 #include "components/favicon/core/features.h" |
| 17 #include "components/favicon_base/fallback_icon_style.h" | 25 #include "components/favicon_base/fallback_icon_style.h" |
| 18 #include "components/favicon_base/favicon_types.h" | 26 #include "components/favicon_base/favicon_types.h" |
| 27 #include "components/favicon_base/favicon_util.h" |
| 28 #include "components/image_fetcher/image_fetcher.h" |
| 19 #include "skia/ext/image_operations.h" | 29 #include "skia/ext/image_operations.h" |
| 20 #include "ui/gfx/codec/png_codec.h" | 30 #include "ui/gfx/codec/png_codec.h" |
| 21 #include "ui/gfx/geometry/size.h" | 31 #include "ui/gfx/geometry/size.h" |
| 22 | 32 |
| 33 using image_fetcher::ImageFetcher; |
| 34 |
| 35 namespace favicon { |
| 36 |
| 23 namespace { | 37 namespace { |
| 24 | 38 |
| 25 // Processes the bitmap data returned from the FaviconService as part of a | 39 const char kGoogleServer1RequestFormat[] = |
| 40 "https://s2.googleusercontent.com/s2/" |
| 41 "favicons?domain=%s&src=chrome_large_icons&sz=%d&alt=404"; |
| 42 const int kGoogleServer1DesiredSizeInPixel = 32; |
| 43 |
| 44 // Type APPLE_TOUCH includes TOUCH_ICONs as well as FAVICONs as fallback. |
| 45 const char kGoogleServer2RequestFormat[] = |
| 46 "https://t0.gstatic.com/" |
| 47 "faviconV2?url=%s&type=APPLE_TOUCH&size=%d&min_size=%d&max_size=%d"; |
| 48 const int kGoogleServer2MinSizeInPixel = 48; |
| 49 const int kGoogleServer2MaxSizeInPixel = 256; |
| 50 const int kGoogleServer2DesiredSizeInPixel = 192; |
| 51 |
| 52 struct LargeIconRequest { |
| 53 const GURL& page_url; |
| 54 int min_source_size_in_pixel; |
| 55 int desired_size_in_pixel; |
| 56 const favicon_base::LargeIconCallback& callback; |
| 57 }; |
| 58 |
| 59 std::unique_ptr<favicon_base::LargeIconResult> CreateLargeIconResult( |
| 60 const favicon_base::FaviconRawBitmapResult* bitmap_result, |
| 61 bool create_bitmap) { |
| 62 if (create_bitmap && bitmap_result && bitmap_result->is_valid()) { |
| 63 return base::MakeUnique<favicon_base::LargeIconResult>(*bitmap_result); |
| 64 } else { |
| 65 // Compute fallback icon style. |
| 66 std::unique_ptr<favicon_base::FallbackIconStyle> fallback_icon_style = |
| 67 base::MakeUnique<favicon_base::FallbackIconStyle>(); |
| 68 if (bitmap_result && bitmap_result->is_valid()) { |
| 69 favicon_base::SetDominantColorAsBackground(bitmap_result->bitmap_data, |
| 70 fallback_icon_style.get()); |
| 71 } |
| 72 return base::MakeUnique<favicon_base::LargeIconResult>( |
| 73 std::move(fallback_icon_style)); |
| 74 } |
| 75 } |
| 76 |
| 77 // Must run on the owner (UI) thread in production. |
| 78 void RunCallback(const favicon_base::LargeIconCallback& callback, |
| 79 std::unique_ptr<favicon_base::LargeIconResult> result) { |
| 80 callback.Run(*result); |
| 81 } |
| 82 |
| 83 // Must run on a background thread in production. |
| 84 // If |bitmap_result| is square and large enough (>= |min_source_in_pixel|), |
| 85 // resizes it to |desired_size_in_pixel_| (but if |desired_size_in_pixel| is |
| 86 // 0 then don't resize). If successful, stores the resulting bitmap data |
| 87 // into |resized_bitmap_result| and returns true. |
| 88 bool ResizeRawBitmapOnBackgroundThreadIfValid( |
| 89 LargeIconRequest* request, |
| 90 const favicon_base::FaviconRawBitmapResult& bitmap_result, |
| 91 favicon_base::FaviconRawBitmapResult* resized_bitmap_result) { |
| 92 // Require bitmap to be valid and square. |
| 93 if (!bitmap_result.is_valid() || |
| 94 bitmap_result.pixel_size.width() != bitmap_result.pixel_size.height()) |
| 95 return false; |
| 96 |
| 97 // Require bitmap to be large enough. It's square, so just check width. |
| 98 if (bitmap_result.pixel_size.width() < request->min_source_size_in_pixel) |
| 99 return false; |
| 100 |
| 101 *resized_bitmap_result = bitmap_result; |
| 102 |
| 103 // Special case: Can use |bitmap_result| as is. |
| 104 if (request->desired_size_in_pixel_ == 0 || |
| 105 bitmap_result.pixel_size.width() == request->desired_size_in_pixel) |
| 106 return true; |
| 107 |
| 108 // Resize bitmap: decode PNG, resize, and re-encode PNG. |
| 109 SkBitmap decoded_bitmap; |
| 110 if (!gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(), |
| 111 bitmap_result.bitmap_data->size(), |
| 112 &decoded_bitmap)) |
| 113 return false; |
| 114 |
| 115 SkBitmap bitmap = skia::ImageOperations::Resize( |
| 116 decoded_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, |
| 117 request->desired_size_in_pixel, request->desired_size_in_pixel); |
| 118 |
| 119 std::vector<unsigned char> bitmap_data; |
| 120 if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data)) |
| 121 return false; |
| 122 |
| 123 resized_bitmap_result->pixel_size = |
| 124 gfx::Size(request->desired_size_in_pixel, request->desired_size_in_pixel); |
| 125 resized_bitmap_result->bitmap_data = |
| 126 base::RefCountedBytes::TakeVector(&bitmap_data); |
| 127 return true; |
| 128 } |
| 129 |
| 130 // Must run on a background thread in production. |
| 131 // If |image| is square and large enough (>= |min_source_in_pixel|), resizes it |
| 132 // to |desired_size_in_pixel_| (but if |desired_size_in_pixel| is 0 then don't |
| 133 // resize). If successful, stores the resulting bitmap data into |
| 134 // |resized_bitmap_result| and returns true. |
| 135 bool ResizeImageOnBackgroundThreadIfValid( |
| 136 LargeIconRequest* request, |
| 137 const GURL& icon_url, |
| 138 const gfx::Image image, |
| 139 favicon_base::FaviconRawBitmapResult* resized_bitmap_result) { |
| 140 // Require bitmap to be valid and square. |
| 141 if (!image.IsEmpty() || image.Width() != image.Height()) |
| 142 return false; |
| 143 |
| 144 // Require bitmap to be large enough. It's square, so just check width. |
| 145 if (image.Width() < request->min_source_size_in_pixel) |
| 146 return false; |
| 147 |
| 148 SkBitmap bitmap = image.AsBitmap(); |
| 149 |
| 150 // Resize if necessary. |
| 151 if (request->desired_size_in_pixel != 0 && |
| 152 image.Width() != request->desired_size_in_pixel) { |
| 153 bitmap = skia::ImageOperations::Resize( |
| 154 bitmap, skia::ImageOperations::RESIZE_LANCZOS3, |
| 155 request->desired_size_in_pixel, request->desired_size_in_pixel); |
| 156 } |
| 157 |
| 158 std::vector<unsigned char> bitmap_data; |
| 159 if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data)) |
| 160 return false; |
| 161 |
| 162 resized_bitmap_result->pixel_size = |
| 163 gfx::Size(request->desired_size_in_pixel, request->desired_size_in_pixel); |
| 164 resized_bitmap_result->bitmap_data = |
| 165 base::RefCountedBytes::TakeVector(&bitmap_data); |
| 166 resized_bitmap_result->expired = true; |
| 167 resized_bitmap_result->icon_url = icon_url; |
| 168 resized_bitmap_result->icon_type = favicon_base::IconType::TOUCH_ICON; |
| 169 |
| 170 return true; |
| 171 } |
| 172 |
| 173 // Processes the bitmap data returned from a Google favicon server as part of a |
| 26 // LargeIconService request. | 174 // LargeIconService request. |
| 27 class LargeIconWorker : public base::RefCountedThreadSafe<LargeIconWorker> { | 175 class LargeIconCacheWorker |
| 176 : public base::RefCountedThreadSafe<LargeIconCacheWorker> { |
| 28 public: | 177 public: |
| 29 LargeIconWorker(int min_source_size_in_pixel, | 178 LargeIconCacheWorker(scoped_refptr<base::TaskRunner> background_task_runner); |
| 30 int desired_size_in_pixel, | 179 |
| 31 favicon_base::LargeIconCallback callback, | 180 base::CancelableTaskTracker::TaskId Start( |
| 32 scoped_refptr<base::TaskRunner> background_task_runner, | 181 base::CancelableTaskTracker* tracker, |
| 33 base::CancelableTaskTracker* tracker); | 182 FaviconService* favicon_service, |
| 183 LargeIconRequest* request); |
| 184 |
| 185 private: |
| 186 friend class base::RefCountedThreadSafe<LargeIconCacheWorker>; |
| 187 ~LargeIconCacheWorker() = default; |
| 34 | 188 |
| 35 // Must run on the owner (UI) thread in production. | 189 // Must run on the owner (UI) thread in production. |
| 36 // Intermediate callback for GetLargeIconOrFallbackStyle(). Invokes | 190 // Intermediate callback for GetLargeIconOrFallbackStyle(). Invokes |
| 37 // ProcessIconOnBackgroundThread() so we do not perform complex image | 191 // ProcessIconOnBackgroundThread() so we do not perform complex image |
| 38 // operations on the UI thread. | 192 // operations on the UI thread. |
| 39 void OnIconLookupComplete( | 193 void OnCachedIconLookupComplete( |
| 194 LargeIconRequest* request, |
| 40 const favicon_base::FaviconRawBitmapResult& bitmap_result); | 195 const favicon_base::FaviconRawBitmapResult& bitmap_result); |
| 41 | 196 |
| 42 private: | |
| 43 friend class base::RefCountedThreadSafe<LargeIconWorker>; | |
| 44 | |
| 45 ~LargeIconWorker(); | |
| 46 | |
| 47 // Must run on a background thread in production. | 197 // Must run on a background thread in production. |
| 48 // Tries to resize |bitmap_result_| and pass the output to |callback_|. If | 198 // Tries to resize |bitmap_result| and pass the output to |callback_|. If |
| 49 // that does not work, computes the icon fallback style and uses it to | 199 // that does not work, computes the icon fallback style and uses it to |
| 50 // invoke |callback_|. This must be run on a background thread because image | 200 // invoke |callback_|. This must be run on a background thread because image |
| 51 // resizing and dominant color extraction can be expensive. | 201 // resizing and dominant color extraction can be expensive. |
| 52 void ProcessIconOnBackgroundThread(); | 202 std::unique_ptr<favicon_base::LargeIconResult> |
| 53 | 203 ProcessCachedIconOnBackgroundThread( |
| 54 // Must run on a background thread in production. | 204 LargeIconRequest* request, |
| 55 // If |bitmap_result_| is square and large enough (>= |min_source_in_pixel_|), | 205 const favicon_base::FaviconRawBitmapResult& bitmap_result); |
| 56 // resizes it to |desired_size_in_pixel_| (but if |desired_size_in_pixel_| is | 206 |
| 57 // 0 then don't resize). If successful, stores the resulting bitmap data | |
| 58 // into |resized_bitmap_result| and returns true. | |
| 59 bool ResizeLargeIconOnBackgroundThreadIfValid( | |
| 60 favicon_base::FaviconRawBitmapResult* resized_bitmap_result); | |
| 61 | |
| 62 // Must run on the owner (UI) thread in production. | |
| 63 // Invoked when ProcessIconOnBackgroundThread() is done. | |
| 64 void OnIconProcessingComplete(); | |
| 65 | |
| 66 int min_source_size_in_pixel_; | |
| 67 int desired_size_in_pixel_; | |
| 68 favicon_base::LargeIconCallback callback_; | |
| 69 scoped_refptr<base::TaskRunner> background_task_runner_; | 207 scoped_refptr<base::TaskRunner> background_task_runner_; |
| 70 base::CancelableTaskTracker* tracker_; | 208 FaviconService* favicon_service_; |
| 71 favicon_base::FaviconRawBitmapResult bitmap_result_; | 209 |
| 72 std::unique_ptr<favicon_base::LargeIconResult> result_; | 210 DISALLOW_COPY_AND_ASSIGN(LargeIconCacheWorker); |
| 73 | |
| 74 DISALLOW_COPY_AND_ASSIGN(LargeIconWorker); | |
| 75 }; | 211 }; |
| 76 | 212 |
| 77 LargeIconWorker::LargeIconWorker( | 213 LargeIconCacheWorker::LargeIconCacheWorker( |
| 214 scoped_refptr<base::TaskRunner> background_task_runner, |
| 215 FaviconService* favicon_service) |
| 216 : background_task_runner_(background_task_runner), |
| 217 favicon_service_(favicon_service) {} |
| 218 |
| 219 base::CancelableTaskTracker::TaskId LargeIconCacheWorker::Start( |
| 220 base::CancelableTaskTracker* tracker, |
| 221 LargeIconRequest* request) { |
| 222 return favicon_service->GetRawFaviconForPageURL( |
| 223 request->page_url, |
| 224 favicon_base::IconType::FAVICON | favicon_base::IconType::TOUCH_ICON | |
| 225 favicon_base::IconType::TOUCH_PRECOMPOSED_ICON, |
| 226 request->desired_size_in_pixel, |
| 227 base::Bind(&LargeIconCacheWorker::OnCachedIconLookupComplete, this, |
| 228 request), |
| 229 tracker); |
| 230 } |
| 231 |
| 232 void LargeIconCacheWorker::OnCachedIconLookupComplete( |
| 233 LargeIconRequest* request, |
| 234 const favicon_base::FaviconRawBitmapResult& bitmap_result) { |
| 235 // Prepare the data for the callback and run it. |
| 236 base::PostTaskAndReplyWithResult( |
| 237 background_task_runner_.get(), FROM_HERE, |
| 238 base::Bind(&LargeIconCacheWorker::ProcessCachedIconOnBackgroundThread, |
| 239 this, request, bitmap_result), |
| 240 base::Bind(&RunCallback, request->callback)); |
| 241 } |
| 242 |
| 243 std::unique_ptr<favicon_base::LargeIconResult> |
| 244 LargeIconCacheWorker::ProcessCachedIconOnBackgroundThread( |
| 245 LargeIconRequest* request, |
| 246 const favicon_base::FaviconRawBitmapResult& bitmap_result) { |
| 247 favicon_base::FaviconRawBitmapResult resized_bitmap_result; |
| 248 bool create_bitmap = ResizeRawBitmapOnBackgroundThreadIfValid( |
| 249 request, bitmap_result, &resized_bitmap_result); |
| 250 return CreateLargeIconResult(&resized_bitmap_result, create_bitmap); |
| 251 } |
| 252 |
| 253 // Processes the bitmap data returned from the FaviconService as part of a |
| 254 // LargeIconService request. |
| 255 class LargeIconGoogleServerWorker |
| 256 : public base::RefCountedThreadSafe<LargeIconGoogleServerWorker> { |
| 257 public: |
| 258 LargeIconGoogleServerWorker( |
| 259 scoped_refptr<base::TaskRunner> background_task_runner, |
| 260 base::CancelableTaskTracker* tracker, |
| 261 FaviconService* favicon_service, |
| 262 ImageFetcher* image_fetcher); |
| 263 |
| 264 base::CancelableTaskTracker::TaskId Start(LargeIconRequest* request); |
| 265 |
| 266 private: |
| 267 friend class base::RefCountedThreadSafe<LargeIconGoogleServerWorker>; |
| 268 ~LargeIconGoogleServerWorker() = default; |
| 269 |
| 270 // Create the GET URL that requests the desired favicon from a Google favicon |
| 271 // server (version 1 / version 2). |
| 272 GURL GetIconUrlForGoogleServer1(LargeIconRequest* request) const; |
| 273 GURL GetIconUrlForGoogleServer2(LargeIconRequest* request) const; |
| 274 |
| 275 // Fetches an icon for the given |icon_url| and stores it in the favicon DB. |
| 276 void FetchIconFromGoogleServer(LargeIconRequest* request, |
| 277 const GURL& icon_url); |
| 278 void OnFetchIconFromGoogleServerComplete(LargeIconRequest* request, |
| 279 const std::string& icon_url, |
| 280 const gfx::Image& image); |
| 281 |
| 282 void ProcessIconFromGoogleServerOnBackgroundThread(LargeIconRequest* request, |
| 283 const GURL& icon_url, |
| 284 const gfx::Image& image); |
| 285 |
| 286 scoped_refptr<base::TaskRunner> background_task_runner_; |
| 287 FaviconService* favicon_service_; |
| 288 ImageFetcher* image_fetcher_; |
| 289 |
| 290 DISALLOW_COPY_AND_ASSIGN(LargeIconGoogleServerWorker); |
| 291 }; |
| 292 |
| 293 LargeIconGoogleServerWorker::LargeIconGoogleServerWorker( |
| 294 scoped_refptr<base::TaskRunner> background_task_runner, |
| 295 FaviconService* favicon_service, |
| 296 ImageFetcher* image_fetcher) |
| 297 : background_task_runner_(background_task_runner), |
| 298 favicon_service_(favicon_service), |
| 299 image_fetcher_(image_fetcher) {} |
| 300 |
| 301 base::CancelableTaskTracker::TaskId LargeIconGoogleServerWorker::Start( |
| 302 base::CancelableTaskTracker* tracker, |
| 303 LargeIconRequest* request) { |
| 304 GURL server_url; |
| 305 if (base::FeatureList::IsEnabled(kFaviconFetchLargeIconFromGoogleServer2)) { |
| 306 server_url = GetIconUrlForGoogleServer2(request); |
| 307 } else if (request->min_source_size_in_pixel <= 16 && |
| 308 base::FeatureList::IsEnabled( |
| 309 kFaviconFetchLargeIconFromGoogleServer1)) { |
| 310 // Server 1 cannot guarantee that the served icon has original size >=16px |
| 311 // as it upscales images to fill in empty spots for missing sizes (32px, |
| 312 // 64px) when indexing. |
| 313 server_url = GetIconUrlForGoogleServer1(request); |
| 314 } |
| 315 |
| 316 DLOG(WARNING) << "large min " << min_source_size_in_pixel_ << " url " |
| 317 << page_url_.spec(); |
| 318 DLOG(WARNING) << "large server " << server_url.spec(); |
| 319 |
| 320 // Run on the current thread as this is needed for posting the I/O task. |
| 321 return tracker->PostTask( |
| 322 base::ThreadTaskRunnerHandle::Get().get(), FROM_HERE, |
| 323 base::Bind(&LargeIconGoogleServerWorker::FetchIconFromGoogleServer, this, |
| 324 request, server_url)); |
| 325 } |
| 326 |
| 327 GURL LargeIconGoogleServerWorker::GetIconUrlForGoogleServer1( |
| 328 LargeIconRequest* request) const { |
| 329 return GURL(base::StringPrintf(kGoogleServer1RequestFormat, |
| 330 request->page_url.spec().c_str(), |
| 331 kGoogleServer1DesiredSizeInPixel)); |
| 332 } |
| 333 |
| 334 GURL LargeIconGoogleServerWorker::GetIconUrlForGoogleServer2( |
| 335 LargeIconRequest* request) const { |
| 336 // TODO(jkrcal): make the desired/min/max sizes depend on the device (its |
| 337 // scale factor, etc). |
| 338 return GURL(base::StringPrintf( |
| 339 kGoogleServer2RequestFormat, request->page_url.spec().c_str(), |
| 340 kGoogleServer2DesiredSizeInPixel, kGoogleServer2MinSizeInPixel, |
| 341 kGoogleServer2MaxSizeInPixel)); |
| 342 } |
| 343 |
| 344 void LargeIconGoogleServerWorker::FetchIconFromGoogleServer( |
| 345 LargeIconRequest* request, |
| 346 const GURL& icon_url) { |
| 347 if (icon_url.is_empty() // Do not fetch if fetching is disabled. |
| 348 || favicon_service_->WasUnableToDownloadFavicon(icon_url) |
| 349 // Do not retry if there is a previous cache miss recorded for |icon_url|. |
| 350 ) { |
| 351 DLOG(WARNING) << "large server fetch cannot proceed " << icon_url; |
| 352 CreateLargeIconResult(nullptr, /*create_bitmap=*/false); |
| 353 RunCallback(); |
| 354 return; |
| 355 } |
| 356 |
| 357 image_fetcher_->SetDataUseServiceName( |
| 358 data_use_measurement::DataUseUserData::LARGE_ICON_SERVICE); |
| 359 image_fetcher_->StartOrQueueNetworkRequest( |
| 360 icon_url.spec(), icon_url, |
| 361 base::Bind( |
| 362 &LargeIconGoogleServerWorker::OnFetchIconFromGoogleServerComplete, |
| 363 this, request)); |
| 364 } |
| 365 |
| 366 void LargeIconGoogleServerWorker::OnFetchIconFromGoogleServerComplete( |
| 367 LargeIconRequest* request, |
| 368 const std::string& icon_url, |
| 369 const gfx::Image& image) { |
| 370 if (image.IsEmpty()) { |
| 371 DLOG(WARNING) << "large server fetch empty " << icon_url; |
| 372 favicon_service_->UnableToDownloadFavicon(GURL(icon_url)); |
| 373 CreateLargeIconResult(nullptr, /*create_bitmap=*/false); |
| 374 RunCallback(); |
| 375 return; |
| 376 } |
| 377 DLOG(WARNING) << "large server fetch successful " << icon_url << " " |
| 378 << image.Width(); |
| 379 |
| 380 // TODO(jkrcal): Extract the original icon url from the response headers if |
| 381 // available and use it instead of |icon_url|. |
| 382 |
| 383 // TODO(jkrcal): never overwrite favicons. Fix in this CL! |
| 384 favicon_service_->SetFavicons(request->page_url, GURL(icon_url), |
| 385 favicon_base::IconType::TOUCH_ICON, image); |
| 386 // Mark the icons as out-of-date so that they are refetched when we visit the |
| 387 // original page any time in the future. |
| 388 favicon_service_->SetFaviconOutOfDateForPage(request->page_url); |
| 389 |
| 390 base::PostTaskAndReplyWithResult( |
| 391 background_task_runner_.get(), FROM_HERE, |
| 392 base::Bind(&LargeIconGoogleServerWorker:: |
| 393 ProcessIconFromGoogleServerOnBackgroundThread, |
| 394 this, request, GURL(icon_url), image), |
| 395 base::Bind(&RunCallback, this, request->callback)); |
| 396 } |
| 397 |
| 398 void LargeIconGoogleServerWorker::ProcessIconFromGoogleServerOnBackgroundThread( |
| 399 LargeIconRequest* request, |
| 400 const GURL& icon_url, |
| 401 const gfx::Image& image) { |
| 402 favicon_base::FaviconRawBitmapResult resized_bitmap_result; |
| 403 bool create_bitmap = ResizeImageOnBackgroundThreadIfValid( |
| 404 request, icon_url, image, &resized_bitmap_result); |
| 405 return CreateLargeIconResult(&resized_bitmap_result, create_bitmap); |
| 406 } |
| 407 |
| 408 } // namespace |
| 409 |
| 410 LargeIconService::LargeIconService( |
| 411 FaviconService* favicon_service, |
| 412 const scoped_refptr<base::TaskRunner>& background_task_runner, |
| 413 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher) |
| 414 : favicon_service_(favicon_service), |
| 415 background_task_runner_(background_task_runner), |
| 416 image_fetcher_(std::move(image_fetcher)) {} |
| 417 |
| 418 LargeIconService::~LargeIconService() { |
| 419 } |
| 420 |
| 421 base::CancelableTaskTracker::TaskId |
| 422 LargeIconService::GetLargeIconOrFallbackStyle( |
| 423 const GURL& page_url, |
| 78 int min_source_size_in_pixel, | 424 int min_source_size_in_pixel, |
| 79 int desired_size_in_pixel, | 425 int desired_size_in_pixel, |
| 80 favicon_base::LargeIconCallback callback, | 426 const favicon_base::LargeIconCallback& callback, |
| 81 scoped_refptr<base::TaskRunner> background_task_runner, | 427 base::CancelableTaskTracker* tracker) { |
| 82 base::CancelableTaskTracker* tracker) | 428 DCHECK_LE(0, min_source_size_in_pixel); |
| 83 : min_source_size_in_pixel_(min_source_size_in_pixel), | 429 DCHECK_LE(0, desired_size_in_pixel); |
| 84 desired_size_in_pixel_(desired_size_in_pixel), | 430 |
| 85 callback_(callback), | 431 std::unique_ptr<LargeIconRequest> request = |
| 86 background_task_runner_(background_task_runner), | 432 base::MakeUnique<favicon_base::FallbackIconStyle>( |
| 87 tracker_(tracker) { | 433 page_url, min_source_size_in_pixel, desired_size_in_pixel, callback); |
| 88 } | 434 |
| 89 | 435 scoped_refptr<LargeIconCacheWorker> worker = |
| 90 LargeIconWorker::~LargeIconWorker() { | 436 new LargeIconCacheWorker(background_task_runner_); |
| 91 } | 437 return worker->Start(tracker, favicon_service_, request.get()); |
| 92 | |
| 93 void LargeIconWorker::OnIconLookupComplete( | |
| 94 const favicon_base::FaviconRawBitmapResult& bitmap_result) { | |
| 95 bitmap_result_ = bitmap_result; | |
| 96 tracker_->PostTaskAndReply( | |
| 97 background_task_runner_.get(), FROM_HERE, | |
| 98 base::Bind(&LargeIconWorker::ProcessIconOnBackgroundThread, this), | |
| 99 base::Bind(&LargeIconWorker::OnIconProcessingComplete, this)); | |
| 100 } | |
| 101 | |
| 102 void LargeIconWorker::ProcessIconOnBackgroundThread() { | |
| 103 favicon_base::FaviconRawBitmapResult resized_bitmap_result; | |
| 104 if (ResizeLargeIconOnBackgroundThreadIfValid(&resized_bitmap_result)) { | |
| 105 result_.reset( | |
| 106 new favicon_base::LargeIconResult(resized_bitmap_result)); | |
| 107 } else { | |
| 108 // Failed to resize |bitmap_result_|, so compute fallback icon style. | |
| 109 std::unique_ptr<favicon_base::FallbackIconStyle> fallback_icon_style( | |
| 110 new favicon_base::FallbackIconStyle()); | |
| 111 if (bitmap_result_.is_valid()) { | |
| 112 favicon_base::SetDominantColorAsBackground( | |
| 113 bitmap_result_.bitmap_data, fallback_icon_style.get()); | |
| 114 } | |
| 115 result_.reset( | |
| 116 new favicon_base::LargeIconResult(fallback_icon_style.release())); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 bool LargeIconWorker::ResizeLargeIconOnBackgroundThreadIfValid( | |
| 121 favicon_base::FaviconRawBitmapResult* resized_bitmap_result) { | |
| 122 // Require bitmap to be valid and square. | |
| 123 if (!bitmap_result_.is_valid() || | |
| 124 bitmap_result_.pixel_size.width() != bitmap_result_.pixel_size.height()) | |
| 125 return false; | |
| 126 | |
| 127 // Require bitmap to be large enough. It's square, so just check width. | |
| 128 if (bitmap_result_.pixel_size.width() < min_source_size_in_pixel_) | |
| 129 return false; | |
| 130 | |
| 131 *resized_bitmap_result = bitmap_result_; | |
| 132 | |
| 133 // Special case: Can use |bitmap_result_| as is. | |
| 134 if (desired_size_in_pixel_ == 0 || | |
| 135 bitmap_result_.pixel_size.width() == desired_size_in_pixel_) | |
| 136 return true; | |
| 137 | |
| 138 // Resize bitmap: decode PNG, resize, and re-encode PNG. | |
| 139 SkBitmap decoded_bitmap; | |
| 140 if (!gfx::PNGCodec::Decode(bitmap_result_.bitmap_data->front(), | |
| 141 bitmap_result_.bitmap_data->size(), &decoded_bitmap)) | |
| 142 return false; | |
| 143 | |
| 144 SkBitmap resized_bitmap = skia::ImageOperations::Resize( | |
| 145 decoded_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, | |
| 146 desired_size_in_pixel_, desired_size_in_pixel_); | |
| 147 | |
| 148 std::vector<unsigned char> bitmap_data; | |
| 149 if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_bitmap, false, &bitmap_data)) | |
| 150 return false; | |
| 151 | |
| 152 resized_bitmap_result->pixel_size = | |
| 153 gfx::Size(desired_size_in_pixel_, desired_size_in_pixel_); | |
| 154 resized_bitmap_result->bitmap_data = | |
| 155 base::RefCountedBytes::TakeVector(&bitmap_data); | |
| 156 return true; | |
| 157 } | |
| 158 | |
| 159 void LargeIconWorker::OnIconProcessingComplete() { | |
| 160 callback_.Run(*result_); | |
| 161 } | |
| 162 | |
| 163 } // namespace | |
| 164 | |
| 165 namespace favicon { | |
| 166 | |
| 167 LargeIconService::LargeIconService( | |
| 168 FaviconService* favicon_service, | |
| 169 const scoped_refptr<base::TaskRunner>& background_task_runner) | |
| 170 : favicon_service_(favicon_service), | |
| 171 background_task_runner_(background_task_runner) { | |
| 172 large_icon_types_.push_back(favicon_base::IconType::FAVICON); | |
| 173 large_icon_types_.push_back(favicon_base::IconType::TOUCH_ICON); | |
| 174 large_icon_types_.push_back(favicon_base::IconType::TOUCH_PRECOMPOSED_ICON); | |
| 175 } | |
| 176 | |
| 177 LargeIconService::~LargeIconService() { | |
| 178 } | 438 } |
| 179 | 439 |
| 180 base::CancelableTaskTracker::TaskId | 440 base::CancelableTaskTracker::TaskId |
| 181 LargeIconService::GetLargeIconOrFallbackStyle( | 441 LargeIconService::GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| 182 const GURL& page_url, | 442 const GURL& page_url, |
| 183 int min_source_size_in_pixel, | 443 int min_source_size_in_pixel, |
| 184 int desired_size_in_pixel, | 444 int desired_size_in_pixel, |
| 185 const favicon_base::LargeIconCallback& callback, | 445 const favicon_base::LargeIconCallback& callback, |
| 186 base::CancelableTaskTracker* tracker) { | 446 base::CancelableTaskTracker* tracker) { |
| 187 DCHECK_LE(1, min_source_size_in_pixel); | 447 DCHECK_LE(0, min_source_size_in_pixel); |
| 188 DCHECK_LE(0, desired_size_in_pixel); | 448 DCHECK_LE(0, desired_size_in_pixel); |
| 189 | 449 |
| 190 scoped_refptr<LargeIconWorker> worker = | 450 std::unique_ptr<LargeIconRequest> request = |
| 191 new LargeIconWorker(min_source_size_in_pixel, desired_size_in_pixel, | 451 base::MakeUnique<favicon_base::FallbackIconStyle>( |
| 192 callback, background_task_runner_, tracker); | 452 page_url, min_source_size_in_pixel, desired_size_in_pixel, callback); |
| 193 | 453 |
| 194 // TODO(beaudoin): For now this is just a wrapper around | 454 scoped_refptr<LargeIconGoogleServerWorker> worker = |
| 195 // GetLargestRawFaviconForPageURL. Add the logic required to select the best | 455 new LargeIconGoogleServerWorker(background_task_runner_, tracker, |
| 196 // possible large icon. Also add logic to fetch-on-demand when the URL of | 456 favicon_service_, image_fetcher_.get()); |
| 197 // a large icon is known but its bitmap is not available. | 457 return worker->Start(request.get()); |
| 198 return favicon_service_->GetLargestRawFaviconForPageURL( | |
| 199 page_url, large_icon_types_, min_source_size_in_pixel, | |
| 200 base::Bind(&LargeIconWorker::OnIconLookupComplete, worker), | |
| 201 tracker); | |
| 202 } | 458 } |
| 203 | 459 |
| 204 } // namespace favicon | 460 } // namespace favicon |
| OLD | NEW |