1 // Copyright (c) 2012 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.
4
5 #include "chrome/browser/favicon/favicon_service.h"
6
7 #include <cmath>
8
9 #include "base/hash.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "chrome/browser/history/history_backend.h"
12 #include "chrome/browser/history/history_service.h"
13 #include "chrome/browser/history/history_service_factory.h"
14 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
15 #include "chrome/common/importer/imported_favicon_usage.h"
16 #include "chrome/common/url_constants.h"
17 #include "components/favicon_base/favicon_types.h"
18 #include "components/favicon_base/favicon_util.h"
19 #include "components/favicon_base/select_favicon_frames.h"
20 #include "extensions/common/constants.h"
21 #include "third_party/skia/include/core/SkBitmap.h"
22 #include "ui/gfx/codec/png_codec.h"
23 #include "ui/gfx/favicon_size.h"
24 #include "ui/gfx/image/image_skia.h"
25
26 using base::Bind;
27
28 namespace {
29
CancelOrRunFaviconResultsCallback(const base::CancelableTaskTracker::IsCanceledCallback & is_canceled,const favicon_base::FaviconResultsCallback & callback,const std::vector<favicon_base::FaviconRawBitmapResult> & results)30 void CancelOrRunFaviconResultsCallback(
31 const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
32 const favicon_base::FaviconResultsCallback& callback,
33 const std::vector<favicon_base::FaviconRawBitmapResult>& results) {
34 if (is_canceled.Run())
35 return;
36 callback.Run(results);
37 }
38
39 // Helper to run callback with empty results if we cannot get the history
40 // service.
RunWithEmptyResultAsync(const favicon_base::FaviconResultsCallback & callback,base::CancelableTaskTracker * tracker)41 base::CancelableTaskTracker::TaskId RunWithEmptyResultAsync(
42 const favicon_base::FaviconResultsCallback& callback,
43 base::CancelableTaskTracker* tracker) {
44 return tracker->PostTask(
45 base::MessageLoopProxy::current().get(),
46 FROM_HERE,
47 Bind(callback, std::vector<favicon_base::FaviconRawBitmapResult>()));
48 }
49
50 // Return the TaskId to retreive the favicon from chrome specific URL.
GetFaviconForChromeURL(Profile * profile,const GURL & page_url,const std::vector<int> & desired_sizes_in_pixel,const favicon_base::FaviconResultsCallback & callback,base::CancelableTaskTracker * tracker)51 base::CancelableTaskTracker::TaskId GetFaviconForChromeURL(
52 Profile* profile,
53 const GURL& page_url,
54 const std::vector<int>& desired_sizes_in_pixel,
55 const favicon_base::FaviconResultsCallback& callback,
56 base::CancelableTaskTracker* tracker) {
57 base::CancelableTaskTracker::IsCanceledCallback is_canceled_cb;
58 base::CancelableTaskTracker::TaskId id =
59 tracker->NewTrackedTaskId(&is_canceled_cb);
60 favicon_base::FaviconResultsCallback cancelable_cb =
61 Bind(&CancelOrRunFaviconResultsCallback, is_canceled_cb, callback);
62 ChromeWebUIControllerFactory::GetInstance()->GetFaviconForURL(
63 profile, page_url, desired_sizes_in_pixel, cancelable_cb);
64 return id;
65 }
66
67 // Returns a vector of pixel edge sizes from |size_in_dip| and
68 // favicon_base::GetFaviconScales().
GetPixelSizesForFaviconScales(int size_in_dip)69 std::vector<int> GetPixelSizesForFaviconScales(int size_in_dip) {
70 std::vector<float> scales = favicon_base::GetFaviconScales();
71 std::vector<int> sizes_in_pixel;
72 for (size_t i = 0; i < scales.size(); ++i) {
73 sizes_in_pixel.push_back(std::ceil(size_in_dip * scales[i]));
74 }
75 return sizes_in_pixel;
76 }
77
78 } // namespace
79
FaviconService(Profile * profile,FaviconClient * favicon_client)80 FaviconService::FaviconService(Profile* profile, FaviconClient* favicon_client)
81 : history_service_(
82 HistoryServiceFactory::GetForProfile(profile,
83 Profile::EXPLICIT_ACCESS)),
84 profile_(profile),
85 favicon_client_(favicon_client) {
86 }
87
88 // static
FaviconResultsCallbackRunner(const favicon_base::FaviconResultsCallback & callback,const std::vector<favicon_base::FaviconRawBitmapResult> * results)89 void FaviconService::FaviconResultsCallbackRunner(
90 const favicon_base::FaviconResultsCallback& callback,
91 const std::vector<favicon_base::FaviconRawBitmapResult>* results) {
92 callback.Run(*results);
93 }
94
GetFaviconImage(const GURL & icon_url,const favicon_base::FaviconImageCallback & callback,base::CancelableTaskTracker * tracker)95 base::CancelableTaskTracker::TaskId FaviconService::GetFaviconImage(
96 const GURL& icon_url,
97 const favicon_base::FaviconImageCallback& callback,
98 base::CancelableTaskTracker* tracker) {
99 favicon_base::FaviconResultsCallback callback_runner =
100 Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults,
101 base::Unretained(this), callback, gfx::kFaviconSize);
102 if (history_service_) {
103 std::vector<GURL> icon_urls;
104 icon_urls.push_back(icon_url);
105 return history_service_->GetFavicons(
106 icon_urls,
107 favicon_base::FAVICON,
108 GetPixelSizesForFaviconScales(gfx::kFaviconSize),
109 callback_runner,
110 tracker);
111 }
112 return RunWithEmptyResultAsync(callback_runner, tracker);
113 }
114
GetRawFavicon(const GURL & icon_url,favicon_base::IconType icon_type,int desired_size_in_pixel,const favicon_base::FaviconRawBitmapCallback & callback,base::CancelableTaskTracker * tracker)115 base::CancelableTaskTracker::TaskId FaviconService::GetRawFavicon(
116 const GURL& icon_url,
117 favicon_base::IconType icon_type,
118 int desired_size_in_pixel,
119 const favicon_base::FaviconRawBitmapCallback& callback,
120 base::CancelableTaskTracker* tracker) {
121 favicon_base::FaviconResultsCallback callback_runner =
122 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
123 base::Unretained(this),
124 callback,
125 desired_size_in_pixel);
126
127 if (history_service_) {
128 std::vector<GURL> icon_urls;
129 icon_urls.push_back(icon_url);
130 std::vector<int> desired_sizes_in_pixel;
131 desired_sizes_in_pixel.push_back(desired_size_in_pixel);
132
133 return history_service_->GetFavicons(
134 icon_urls, icon_type, desired_sizes_in_pixel, callback_runner, tracker);
135 }
136 return RunWithEmptyResultAsync(callback_runner, tracker);
137 }
138
GetFavicon(const GURL & icon_url,favicon_base::IconType icon_type,int desired_size_in_dip,const favicon_base::FaviconResultsCallback & callback,base::CancelableTaskTracker * tracker)139 base::CancelableTaskTracker::TaskId FaviconService::GetFavicon(
140 const GURL& icon_url,
141 favicon_base::IconType icon_type,
142 int desired_size_in_dip,
143 const favicon_base::FaviconResultsCallback& callback,
144 base::CancelableTaskTracker* tracker) {
145 if (history_service_) {
146 std::vector<GURL> icon_urls;
147 icon_urls.push_back(icon_url);
148 return history_service_->GetFavicons(
149 icon_urls,
150 icon_type,
151 GetPixelSizesForFaviconScales(desired_size_in_dip),
152 callback,
153 tracker);
154 }
155 return RunWithEmptyResultAsync(callback, tracker);
156 }
157
GetFaviconImageForPageURL(const GURL & page_url,const favicon_base::FaviconImageCallback & callback,base::CancelableTaskTracker * tracker)158 base::CancelableTaskTracker::TaskId FaviconService::GetFaviconImageForPageURL(
159 const GURL& page_url,
160 const favicon_base::FaviconImageCallback& callback,
161 base::CancelableTaskTracker* tracker) {
162 return GetFaviconForPageURLImpl(
163 page_url,
164 favicon_base::FAVICON,
165 GetPixelSizesForFaviconScales(gfx::kFaviconSize),
166 Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults,
167 base::Unretained(this),
168 callback,
169 gfx::kFaviconSize),
170 tracker);
171 }
172
GetRawFaviconForPageURL(const GURL & page_url,int icon_types,int desired_size_in_pixel,const favicon_base::FaviconRawBitmapCallback & callback,base::CancelableTaskTracker * tracker)173 base::CancelableTaskTracker::TaskId FaviconService::GetRawFaviconForPageURL(
174 const GURL& page_url,
175 int icon_types,
176 int desired_size_in_pixel,
177 const favicon_base::FaviconRawBitmapCallback& callback,
178 base::CancelableTaskTracker* tracker) {
179 std::vector<int> desired_sizes_in_pixel;
180 desired_sizes_in_pixel.push_back(desired_size_in_pixel);
181 return GetFaviconForPageURLImpl(
182 page_url,
183 icon_types,
184 desired_sizes_in_pixel,
185 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
186 base::Unretained(this),
187 callback,
188 desired_size_in_pixel),
189 tracker);
190 }
191
192 base::CancelableTaskTracker::TaskId
GetLargestRawFaviconForPageURL(const GURL & page_url,const std::vector<int> & icon_types,int minimum_size_in_pixels,const favicon_base::FaviconRawBitmapCallback & callback,base::CancelableTaskTracker * tracker)193 FaviconService::GetLargestRawFaviconForPageURL(
194 const GURL& page_url,
195 const std::vector<int>& icon_types,
196 int minimum_size_in_pixels,
197 const favicon_base::FaviconRawBitmapCallback& callback,
198 base::CancelableTaskTracker* tracker) {
199 favicon_base::FaviconResultsCallback favicon_results_callback =
200 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
201 base::Unretained(this),
202 callback,
203 0);
204 if (page_url.SchemeIs(content::kChromeUIScheme) ||
205 page_url.SchemeIs(extensions::kExtensionScheme)) {
206 std::vector<int> desired_sizes_in_pixel;
207 desired_sizes_in_pixel.push_back(0);
208 return GetFaviconForChromeURL(profile_,
209 page_url,
210 desired_sizes_in_pixel,
211 favicon_results_callback,
212 tracker);
213 }
214 if (history_service_) {
215 return history_service_->GetLargestFaviconForURL(page_url, icon_types,
216 minimum_size_in_pixels, callback, tracker);
217 }
218 return RunWithEmptyResultAsync(favicon_results_callback, tracker);
219 }
220
GetFaviconForPageURL(const GURL & page_url,int icon_types,int desired_size_in_dip,const favicon_base::FaviconResultsCallback & callback,base::CancelableTaskTracker * tracker)221 base::CancelableTaskTracker::TaskId FaviconService::GetFaviconForPageURL(
222 const GURL& page_url,
223 int icon_types,
224 int desired_size_in_dip,
225 const favicon_base::FaviconResultsCallback& callback,
226 base::CancelableTaskTracker* tracker) {
227 return GetFaviconForPageURLImpl(
228 page_url,
229 icon_types,
230 GetPixelSizesForFaviconScales(desired_size_in_dip),
231 callback,
232 tracker);
233 }
234
235 base::CancelableTaskTracker::TaskId
UpdateFaviconMappingsAndFetch(const GURL & page_url,const std::vector<GURL> & icon_urls,int icon_types,int desired_size_in_dip,const favicon_base::FaviconResultsCallback & callback,base::CancelableTaskTracker * tracker)236 FaviconService::UpdateFaviconMappingsAndFetch(
237 const GURL& page_url,
238 const std::vector<GURL>& icon_urls,
239 int icon_types,
240 int desired_size_in_dip,
241 const favicon_base::FaviconResultsCallback& callback,
242 base::CancelableTaskTracker* tracker) {
243 if (history_service_) {
244 return history_service_->UpdateFaviconMappingsAndFetch(
245 page_url,
246 icon_urls,
247 icon_types,
248 GetPixelSizesForFaviconScales(desired_size_in_dip),
249 callback,
250 tracker);
251 }
252 return RunWithEmptyResultAsync(callback, tracker);
253 }
254
GetLargestRawFaviconForID(favicon_base::FaviconID favicon_id,const favicon_base::FaviconRawBitmapCallback & callback,base::CancelableTaskTracker * tracker)255 base::CancelableTaskTracker::TaskId FaviconService::GetLargestRawFaviconForID(
256 favicon_base::FaviconID favicon_id,
257 const favicon_base::FaviconRawBitmapCallback& callback,
258 base::CancelableTaskTracker* tracker) {
259 // Use 0 as |desired_size| to get the largest bitmap for |favicon_id| without
260 // any resizing.
261 int desired_size = 0;
262 favicon_base::FaviconResultsCallback callback_runner =
263 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults,
264 base::Unretained(this),
265 callback,
266 desired_size);
267
268 if (history_service_) {
269 return history_service_->GetFaviconForID(
270 favicon_id, desired_size, callback_runner, tracker);
271 }
272 return RunWithEmptyResultAsync(callback_runner, tracker);
273 }
274
SetFaviconOutOfDateForPage(const GURL & page_url)275 void FaviconService::SetFaviconOutOfDateForPage(const GURL& page_url) {
276 if (history_service_)
277 history_service_->SetFaviconsOutOfDateForPage(page_url);
278 }
279
CloneFavicon(const GURL & old_page_url,const GURL & new_page_url)280 void FaviconService::CloneFavicon(const GURL& old_page_url,
281 const GURL& new_page_url) {
282 if (history_service_)
283 history_service_->CloneFavicons(old_page_url, new_page_url);
284 }
285
SetImportedFavicons(const std::vector<ImportedFaviconUsage> & favicon_usage)286 void FaviconService::SetImportedFavicons(
287 const std::vector<ImportedFaviconUsage>& favicon_usage) {
288 if (history_service_)
289 history_service_->SetImportedFavicons(favicon_usage);
290 }
291
MergeFavicon(const GURL & page_url,const GURL & icon_url,favicon_base::IconType icon_type,scoped_refptr<base::RefCountedMemory> bitmap_data,const gfx::Size & pixel_size)292 void FaviconService::MergeFavicon(
293 const GURL& page_url,
294 const GURL& icon_url,
295 favicon_base::IconType icon_type,
296 scoped_refptr<base::RefCountedMemory> bitmap_data,
297 const gfx::Size& pixel_size) {
298 if (history_service_) {
299 history_service_->MergeFavicon(page_url, icon_url, icon_type, bitmap_data,
300 pixel_size);
301 }
302 }
303
SetFavicons(const GURL & page_url,const GURL & icon_url,favicon_base::IconType icon_type,const gfx::Image & image)304 void FaviconService::SetFavicons(const GURL& page_url,
305 const GURL& icon_url,
306 favicon_base::IconType icon_type,
307 const gfx::Image& image) {
308 if (!history_service_)
309 return;
310
311 gfx::ImageSkia image_skia = image.AsImageSkia();
312 image_skia.EnsureRepsForSupportedScales();
313 const std::vector<gfx::ImageSkiaRep>& image_reps = image_skia.image_reps();
314 std::vector<SkBitmap> bitmaps;
315 const std::vector<float> favicon_scales = favicon_base::GetFaviconScales();
316 for (size_t i = 0; i < image_reps.size(); ++i) {
317 // Don't save if the scale isn't one of supported favicon scales.
318 if (std::find(favicon_scales.begin(),
319 favicon_scales.end(),
320 image_reps[i].scale()) == favicon_scales.end()) {
321 continue;
322 }
323 bitmaps.push_back(image_reps[i].sk_bitmap());
324 }
325 history_service_->SetFavicons(page_url, icon_type, icon_url, bitmaps);
326 }
327
UnableToDownloadFavicon(const GURL & icon_url)328 void FaviconService::UnableToDownloadFavicon(const GURL& icon_url) {
329 MissingFaviconURLHash url_hash = base::Hash(icon_url.spec());
330 missing_favicon_urls_.insert(url_hash);
331 }
332
WasUnableToDownloadFavicon(const GURL & icon_url) const333 bool FaviconService::WasUnableToDownloadFavicon(const GURL& icon_url) const {
334 MissingFaviconURLHash url_hash = base::Hash(icon_url.spec());
335 return missing_favicon_urls_.find(url_hash) != missing_favicon_urls_.end();
336 }
337
ClearUnableToDownloadFavicons()338 void FaviconService::ClearUnableToDownloadFavicons() {
339 missing_favicon_urls_.clear();
340 }
341
~FaviconService()342 FaviconService::~FaviconService() {}
343
GetFaviconForPageURLImpl(const GURL & page_url,int icon_types,const std::vector<int> & desired_sizes_in_pixel,const favicon_base::FaviconResultsCallback & callback,base::CancelableTaskTracker * tracker)344 base::CancelableTaskTracker::TaskId FaviconService::GetFaviconForPageURLImpl(
345 const GURL& page_url,
346 int icon_types,
347 const std::vector<int>& desired_sizes_in_pixel,
348 const favicon_base::FaviconResultsCallback& callback,
349 base::CancelableTaskTracker* tracker) {
350 if (page_url.SchemeIs(content::kChromeUIScheme) ||
351 page_url.SchemeIs(extensions::kExtensionScheme)) {
352 return GetFaviconForChromeURL(
353 profile_, page_url, desired_sizes_in_pixel, callback, tracker);
354 }
355 if (history_service_) {
356 return history_service_->GetFaviconsForURL(page_url,
357 icon_types,
358 desired_sizes_in_pixel,
359 callback,
360 tracker);
361 }
362 return RunWithEmptyResultAsync(callback, tracker);
363 }
364
RunFaviconImageCallbackWithBitmapResults(const favicon_base::FaviconImageCallback & callback,int desired_size_in_dip,const std::vector<favicon_base::FaviconRawBitmapResult> & favicon_bitmap_results)365 void FaviconService::RunFaviconImageCallbackWithBitmapResults(
366 const favicon_base::FaviconImageCallback& callback,
367 int desired_size_in_dip,
368 const std::vector<favicon_base::FaviconRawBitmapResult>&
369 favicon_bitmap_results) {
370 favicon_base::FaviconImageResult image_result;
371 image_result.image = favicon_base::SelectFaviconFramesFromPNGs(
372 favicon_bitmap_results,
373 favicon_base::GetFaviconScales(),
374 desired_size_in_dip);
375 favicon_base::SetFaviconColorSpace(&image_result.image);
376
377 image_result.icon_url = image_result.image.IsEmpty() ?
378 GURL() : favicon_bitmap_results[0].icon_url;
379 callback.Run(image_result);
380 }
381
RunFaviconRawBitmapCallbackWithBitmapResults(const favicon_base::FaviconRawBitmapCallback & callback,int desired_size_in_pixel,const std::vector<favicon_base::FaviconRawBitmapResult> & favicon_bitmap_results)382 void FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults(
383 const favicon_base::FaviconRawBitmapCallback& callback,
384 int desired_size_in_pixel,
385 const std::vector<favicon_base::FaviconRawBitmapResult>&
386 favicon_bitmap_results) {
387 if (favicon_bitmap_results.empty() || !favicon_bitmap_results[0].is_valid()) {
388 callback.Run(favicon_base::FaviconRawBitmapResult());
389 return;
390 }
391
392 favicon_base::FaviconRawBitmapResult bitmap_result =
393 favicon_bitmap_results[0];
394
395 // If the desired size is 0, SelectFaviconFrames() will return the largest
396 // bitmap without doing any resizing. As |favicon_bitmap_results| has bitmap
397 // data for a single bitmap, return it and avoid an unnecessary decode.
398 if (desired_size_in_pixel == 0) {
399 callback.Run(bitmap_result);
400 return;
401 }
402
403 // If history bitmap is already desired pixel size, return early.
404 if (bitmap_result.pixel_size.width() == desired_size_in_pixel &&
405 bitmap_result.pixel_size.height() == desired_size_in_pixel) {
406 callback.Run(bitmap_result);
407 return;
408 }
409
410 // Convert raw bytes to SkBitmap, resize via SelectFaviconFrames(), then
411 // convert back.
412 std::vector<float> desired_favicon_scales;
413 desired_favicon_scales.push_back(1.0f);
414 gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs(
415 favicon_bitmap_results, desired_favicon_scales, desired_size_in_pixel);
416
417 std::vector<unsigned char> resized_bitmap_data;
418 if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_image.AsBitmap(), false,
419 &resized_bitmap_data)) {
420 callback.Run(favicon_base::FaviconRawBitmapResult());
421 return;
422 }
423
424 bitmap_result.bitmap_data = base::RefCountedBytes::TakeVector(
425 &resized_bitmap_data);
426 callback.Run(bitmap_result);
427 }
428