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.
5 #include "chrome/browser/favicon/favicon_service.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"
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())
36 callback
.Run(results
);
39 // Helper to run callback with empty results if we cannot get the history
41 base::CancelableTaskTracker::TaskId
RunWithEmptyResultAsync(
42 const favicon_base::FaviconResultsCallback
& callback
,
43 base::CancelableTaskTracker
* tracker
) {
44 return tracker
->PostTask(
45 base::MessageLoopProxy::current().get(),
47 Bind(callback
, std::vector
<favicon_base::FaviconRawBitmapResult
>()));
50 // Return the TaskId to retreive the favicon from chrome specific URL.
51 base::CancelableTaskTracker::TaskId
GetFaviconForChromeURL(
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
);
67 // Returns a vector of pixel edge sizes from |size_in_dip| and
68 // favicon_base::GetFaviconScales().
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
]));
75 return sizes_in_pixel
;
80 FaviconService::FaviconService(Profile
* profile
, FaviconClient
* favicon_client
)
82 HistoryServiceFactory::GetForProfile(profile
,
83 Profile::EXPLICIT_ACCESS
)),
85 favicon_client_(favicon_client
) {
89 void FaviconService::FaviconResultsCallbackRunner(
90 const favicon_base::FaviconResultsCallback
& callback
,
91 const std::vector
<favicon_base::FaviconRawBitmapResult
>* results
) {
92 callback
.Run(*results
);
95 base::CancelableTaskTracker::TaskId
FaviconService::GetFaviconImage(
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(
107 favicon_base::FAVICON
,
108 GetPixelSizesForFaviconScales(gfx::kFaviconSize
),
112 return RunWithEmptyResultAsync(callback_runner
, 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),
125 desired_size_in_pixel
);
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
);
133 return history_service_
->GetFavicons(
134 icon_urls
, icon_type
, desired_sizes_in_pixel
, callback_runner
, tracker
);
136 return RunWithEmptyResultAsync(callback_runner
, 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(
151 GetPixelSizesForFaviconScales(desired_size_in_dip
),
155 return RunWithEmptyResultAsync(callback
, 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(
164 favicon_base::FAVICON
,
165 GetPixelSizesForFaviconScales(gfx::kFaviconSize
),
166 Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults
,
167 base::Unretained(this),
173 base::CancelableTaskTracker::TaskId
FaviconService::GetRawFaviconForPageURL(
174 const GURL
& page_url
,
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(
184 desired_sizes_in_pixel
,
185 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults
,
186 base::Unretained(this),
188 desired_size_in_pixel
),
192 base::CancelableTaskTracker::TaskId
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),
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_
,
210 desired_sizes_in_pixel
,
211 favicon_results_callback
,
214 if (history_service_
) {
215 return history_service_
->GetLargestFaviconForURL(page_url
, icon_types
,
216 minimum_size_in_pixels
, callback
, tracker
);
218 return RunWithEmptyResultAsync(favicon_results_callback
, tracker
);
221 base::CancelableTaskTracker::TaskId
FaviconService::GetFaviconForPageURL(
222 const GURL
& page_url
,
224 int desired_size_in_dip
,
225 const favicon_base::FaviconResultsCallback
& callback
,
226 base::CancelableTaskTracker
* tracker
) {
227 return GetFaviconForPageURLImpl(
230 GetPixelSizesForFaviconScales(desired_size_in_dip
),
235 base::CancelableTaskTracker::TaskId
236 FaviconService::UpdateFaviconMappingsAndFetch(
237 const GURL
& page_url
,
238 const std::vector
<GURL
>& icon_urls
,
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(
248 GetPixelSizesForFaviconScales(desired_size_in_dip
),
252 return RunWithEmptyResultAsync(callback
, 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
261 int desired_size
= 0;
262 favicon_base::FaviconResultsCallback callback_runner
=
263 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults
,
264 base::Unretained(this),
268 if (history_service_
) {
269 return history_service_
->GetFaviconForID(
270 favicon_id
, desired_size
, callback_runner
, tracker
);
272 return RunWithEmptyResultAsync(callback_runner
, tracker
);
275 void FaviconService::SetFaviconOutOfDateForPage(const GURL
& page_url
) {
276 if (history_service_
)
277 history_service_
->SetFaviconsOutOfDateForPage(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
);
286 void FaviconService::SetImportedFavicons(
287 const std::vector
<ImportedFaviconUsage
>& favicon_usage
) {
288 if (history_service_
)
289 history_service_
->SetImportedFavicons(favicon_usage
);
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
,
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_
)
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()) {
323 bitmaps
.push_back(image_reps
[i
].sk_bitmap());
325 history_service_
->SetFavicons(page_url
, icon_type
, icon_url
, bitmaps
);
328 void FaviconService::UnableToDownloadFavicon(const GURL
& icon_url
) {
329 MissingFaviconURLHash url_hash
= base::Hash(icon_url
.spec());
330 missing_favicon_urls_
.insert(url_hash
);
333 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();
338 void FaviconService::ClearUnableToDownloadFavicons() {
339 missing_favicon_urls_
.clear();
342 FaviconService::~FaviconService() {}
344 base::CancelableTaskTracker::TaskId
FaviconService::GetFaviconForPageURLImpl(
345 const GURL
& page_url
,
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
);
355 if (history_service_
) {
356 return history_service_
->GetFaviconsForURL(page_url
,
358 desired_sizes_in_pixel
,
362 return RunWithEmptyResultAsync(callback
, tracker
);
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
);
377 image_result
.icon_url
= image_result
.image
.IsEmpty() ?
378 GURL() : favicon_bitmap_results
[0].icon_url
;
379 callback
.Run(image_result
);
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());
392 favicon_base::FaviconRawBitmapResult bitmap_result
=
393 favicon_bitmap_results
[0];
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
);
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
);
410 // Convert raw bytes to SkBitmap, resize via SelectFaviconFrames(), then
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
);
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());
424 bitmap_result
.bitmap_data
= base::RefCountedBytes::TakeVector(
425 &resized_bitmap_data
);
426 callback
.Run(bitmap_result
);