Roll src/third_party/WebKit 9f7fb92:f103b33 (svn 202621:202622)
[chromium-blink-merge.git] / components / favicon / core / large_icon_service.cc
blob57953b271d9fe6d9cba00fb2555efc346abf9a59
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
3 // found in the LICENSE file.
5 #include "components/favicon/core/large_icon_service.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/task_runner.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "components/favicon/core/favicon_service.h"
15 #include "components/favicon_base/fallback_icon_style.h"
16 #include "components/favicon_base/favicon_types.h"
17 #include "skia/ext/image_operations.h"
18 #include "ui/gfx/codec/png_codec.h"
19 #include "ui/gfx/geometry/size.h"
21 namespace {
23 // Processes the bitmap data returned from the FaviconService as part of a
24 // LargeIconService request.
25 class LargeIconWorker : public base::RefCountedThreadSafe<LargeIconWorker> {
26 public:
27 LargeIconWorker(int min_source_size_in_pixel,
28 int desired_size_in_pixel,
29 favicon_base::LargeIconCallback callback,
30 scoped_refptr<base::TaskRunner> background_task_runner,
31 base::CancelableTaskTracker* tracker);
33 // Must run on the owner (UI) thread in production.
34 // Intermediate callback for GetLargeIconOrFallbackStyle(). Invokes
35 // ProcessIconOnBackgroundThread() so we do not perform complex image
36 // operations on the UI thread.
37 void OnIconLookupComplete(
38 const favicon_base::FaviconRawBitmapResult& bitmap_result);
40 private:
41 friend class base::RefCountedThreadSafe<LargeIconWorker>;
43 ~LargeIconWorker();
45 // Must run on a background thread in production.
46 // Tries to resize |bitmap_result_| and pass the output to |callback_|. If
47 // that does not work, computes the icon fallback style and uses it to
48 // invoke |callback_|. This must be run on a background thread because image
49 // resizing and dominant color extraction can be expensive.
50 void ProcessIconOnBackgroundThread();
52 // Must run on a background thread in production.
53 // If |bitmap_result_| is square and large enough (>= |min_source_in_pixel_|),
54 // resizes it to |desired_size_in_pixel_| (but if |desired_size_in_pixel_| is
55 // 0 then don't resize). If successful, stores the resulting bitmap data
56 // into |resized_bitmap_result| and returns true.
57 bool ResizeLargeIconOnBackgroundThreadIfValid(
58 favicon_base::FaviconRawBitmapResult* resized_bitmap_result);
60 // Must run on the owner (UI) thread in production.
61 // Invoked when ProcessIconOnBackgroundThread() is done.
62 void OnIconProcessingComplete();
64 int min_source_size_in_pixel_;
65 int desired_size_in_pixel_;
66 favicon_base::LargeIconCallback callback_;
67 scoped_refptr<base::TaskRunner> background_task_runner_;
68 base::CancelableTaskTracker* tracker_;
69 favicon_base::FaviconRawBitmapResult bitmap_result_;
70 scoped_ptr<favicon_base::LargeIconResult> result_;
72 DISALLOW_COPY_AND_ASSIGN(LargeIconWorker);
75 LargeIconWorker::LargeIconWorker(
76 int min_source_size_in_pixel,
77 int desired_size_in_pixel,
78 favicon_base::LargeIconCallback callback,
79 scoped_refptr<base::TaskRunner> background_task_runner,
80 base::CancelableTaskTracker* tracker)
81 : min_source_size_in_pixel_(min_source_size_in_pixel),
82 desired_size_in_pixel_(desired_size_in_pixel),
83 callback_(callback),
84 background_task_runner_(background_task_runner),
85 tracker_(tracker) {
88 LargeIconWorker::~LargeIconWorker() {
91 void LargeIconWorker::OnIconLookupComplete(
92 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
93 bitmap_result_ = bitmap_result;
94 tracker_->PostTaskAndReply(
95 background_task_runner_.get(), FROM_HERE,
96 base::Bind(&LargeIconWorker::ProcessIconOnBackgroundThread, this),
97 base::Bind(&LargeIconWorker::OnIconProcessingComplete, this));
100 void LargeIconWorker::ProcessIconOnBackgroundThread() {
101 favicon_base::FaviconRawBitmapResult resized_bitmap_result;
102 if (ResizeLargeIconOnBackgroundThreadIfValid(&resized_bitmap_result)) {
103 result_.reset(
104 new favicon_base::LargeIconResult(resized_bitmap_result));
105 } else {
106 // Failed to resize |bitmap_result_|, so compute fallback icon style.
107 scoped_ptr<favicon_base::FallbackIconStyle> fallback_icon_style(
108 new favicon_base::FallbackIconStyle());
109 if (bitmap_result_.is_valid()) {
110 favicon_base::SetDominantColorAsBackground(
111 bitmap_result_.bitmap_data, fallback_icon_style.get());
113 result_.reset(
114 new favicon_base::LargeIconResult(fallback_icon_style.release()));
118 bool LargeIconWorker::ResizeLargeIconOnBackgroundThreadIfValid(
119 favicon_base::FaviconRawBitmapResult* resized_bitmap_result) {
120 // Require bitmap to be valid and square.
121 if (!bitmap_result_.is_valid() ||
122 bitmap_result_.pixel_size.width() != bitmap_result_.pixel_size.height())
123 return false;
125 // Require bitmap to be large enough. It's square, so just check width.
126 if (bitmap_result_.pixel_size.width() < min_source_size_in_pixel_)
127 return false;
129 *resized_bitmap_result = bitmap_result_;
131 // Special case: Can use |bitmap_result_| as is.
132 if (desired_size_in_pixel_ == 0 ||
133 bitmap_result_.pixel_size.width() == desired_size_in_pixel_)
134 return true;
136 // Resize bitmap: decode PNG, resize, and re-encode PNG.
137 SkBitmap decoded_bitmap;
138 if (!gfx::PNGCodec::Decode(bitmap_result_.bitmap_data->front(),
139 bitmap_result_.bitmap_data->size(), &decoded_bitmap))
140 return false;
142 SkBitmap resized_bitmap = skia::ImageOperations::Resize(
143 decoded_bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
144 desired_size_in_pixel_, desired_size_in_pixel_);
146 std::vector<unsigned char> bitmap_data;
147 if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_bitmap, false, &bitmap_data))
148 return false;
150 resized_bitmap_result->pixel_size =
151 gfx::Size(desired_size_in_pixel_, desired_size_in_pixel_);
152 resized_bitmap_result->bitmap_data =
153 base::RefCountedBytes::TakeVector(&bitmap_data);
154 return true;
157 void LargeIconWorker::OnIconProcessingComplete() {
158 callback_.Run(*result_);
161 } // namespace
163 namespace favicon {
165 LargeIconService::LargeIconService(
166 FaviconService* favicon_service,
167 const scoped_refptr<base::TaskRunner>& background_task_runner)
168 : favicon_service_(favicon_service),
169 background_task_runner_(background_task_runner) {
170 large_icon_types_.push_back(favicon_base::IconType::FAVICON);
171 large_icon_types_.push_back(favicon_base::IconType::TOUCH_ICON);
172 large_icon_types_.push_back(favicon_base::IconType::TOUCH_PRECOMPOSED_ICON);
175 LargeIconService::~LargeIconService() {
178 base::CancelableTaskTracker::TaskId
179 LargeIconService::GetLargeIconOrFallbackStyle(
180 const GURL& page_url,
181 int min_source_size_in_pixel,
182 int desired_size_in_pixel,
183 const favicon_base::LargeIconCallback& callback,
184 base::CancelableTaskTracker* tracker) {
185 DCHECK_LE(1, min_source_size_in_pixel);
186 DCHECK_LE(0, desired_size_in_pixel);
188 scoped_refptr<LargeIconWorker> worker =
189 new LargeIconWorker(min_source_size_in_pixel, desired_size_in_pixel,
190 callback, background_task_runner_, tracker);
192 // TODO(beaudoin): For now this is just a wrapper around
193 // GetLargestRawFaviconForPageURL. Add the logic required to select the best
194 // possible large icon. Also add logic to fetch-on-demand when the URL of
195 // a large icon is known but its bitmap is not available.
196 return favicon_service_->GetLargestRawFaviconForPageURL(
197 page_url, large_icon_types_, min_source_size_in_pixel,
198 base::Bind(&LargeIconWorker::OnIconLookupComplete, worker),
199 tracker);
202 } // namespace favicon