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/url_constants.h"
16 #include "components/favicon_base/favicon_util.h"
17 #include "components/favicon_base/select_favicon_frames.h"
18 #include "extensions/common/constants.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
20 #include "ui/gfx/codec/png_codec.h"
21 #include "ui/gfx/favicon_size.h"
22 #include "ui/gfx/image/image_skia.h"
29 void CancelOrRunFaviconResultsCallback(
30 const base::CancelableTaskTracker::IsCanceledCallback
& is_canceled
,
31 const favicon_base::FaviconResultsCallback
& callback
,
32 const std::vector
<favicon_base::FaviconRawBitmapResult
>& results
) {
33 if (is_canceled
.Run())
35 callback
.Run(results
);
38 // Helper to run callback with empty results if we cannot get the history
40 base::CancelableTaskTracker::TaskId
RunWithEmptyResultAsync(
41 const favicon_base::FaviconResultsCallback
& callback
,
42 base::CancelableTaskTracker
* tracker
) {
43 return tracker
->PostTask(
44 base::MessageLoopProxy::current().get(),
46 Bind(callback
, std::vector
<favicon_base::FaviconRawBitmapResult
>()));
49 // Return the TaskId to retreive the favicon from chrome specific URL.
50 base::CancelableTaskTracker::TaskId
GetFaviconForChromeURL(
53 const std::vector
<int>& desired_sizes_in_pixel
,
54 const favicon_base::FaviconResultsCallback
& callback
,
55 base::CancelableTaskTracker
* tracker
) {
56 base::CancelableTaskTracker::IsCanceledCallback is_canceled_cb
;
57 base::CancelableTaskTracker::TaskId id
=
58 tracker
->NewTrackedTaskId(&is_canceled_cb
);
59 favicon_base::FaviconResultsCallback cancelable_cb
=
60 Bind(&CancelOrRunFaviconResultsCallback
, is_canceled_cb
, callback
);
61 ChromeWebUIControllerFactory::GetInstance()->GetFaviconForURL(
62 profile
, page_url
, desired_sizes_in_pixel
, cancelable_cb
);
66 // Returns a vector of pixel edge sizes from |size_in_dip| and
67 // favicon_base::GetFaviconScales().
68 std::vector
<int> GetPixelSizesForFaviconScales(int size_in_dip
) {
69 std::vector
<float> scales
= favicon_base::GetFaviconScales();
70 std::vector
<int> sizes_in_pixel
;
71 for (size_t i
= 0; i
< scales
.size(); ++i
) {
72 sizes_in_pixel
.push_back(std::ceil(size_in_dip
* scales
[i
]));
74 return sizes_in_pixel
;
79 FaviconService::FaviconService(Profile
* profile
, FaviconClient
* favicon_client
)
80 : history_service_(HistoryServiceFactory::GetForProfile(
82 ServiceAccessType::EXPLICIT_ACCESS
)),
84 favicon_client_(favicon_client
) {
88 void FaviconService::FaviconResultsCallbackRunner(
89 const favicon_base::FaviconResultsCallback
& callback
,
90 const std::vector
<favicon_base::FaviconRawBitmapResult
>* results
) {
91 callback
.Run(*results
);
94 base::CancelableTaskTracker::TaskId
FaviconService::GetFaviconImage(
96 const favicon_base::FaviconImageCallback
& callback
,
97 base::CancelableTaskTracker
* tracker
) {
98 favicon_base::FaviconResultsCallback callback_runner
=
99 Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults
,
100 base::Unretained(this), callback
, gfx::kFaviconSize
);
101 if (history_service_
) {
102 std::vector
<GURL
> icon_urls
;
103 icon_urls
.push_back(icon_url
);
104 return history_service_
->GetFavicons(
106 favicon_base::FAVICON
,
107 GetPixelSizesForFaviconScales(gfx::kFaviconSize
),
111 return RunWithEmptyResultAsync(callback_runner
, tracker
);
114 base::CancelableTaskTracker::TaskId
FaviconService::GetRawFavicon(
115 const GURL
& icon_url
,
116 favicon_base::IconType icon_type
,
117 int desired_size_in_pixel
,
118 const favicon_base::FaviconRawBitmapCallback
& callback
,
119 base::CancelableTaskTracker
* tracker
) {
120 favicon_base::FaviconResultsCallback callback_runner
=
121 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults
,
122 base::Unretained(this),
124 desired_size_in_pixel
);
126 if (history_service_
) {
127 std::vector
<GURL
> icon_urls
;
128 icon_urls
.push_back(icon_url
);
129 std::vector
<int> desired_sizes_in_pixel
;
130 desired_sizes_in_pixel
.push_back(desired_size_in_pixel
);
132 return history_service_
->GetFavicons(
133 icon_urls
, icon_type
, desired_sizes_in_pixel
, callback_runner
, tracker
);
135 return RunWithEmptyResultAsync(callback_runner
, tracker
);
138 base::CancelableTaskTracker::TaskId
FaviconService::GetFavicon(
139 const GURL
& icon_url
,
140 favicon_base::IconType icon_type
,
141 int desired_size_in_dip
,
142 const favicon_base::FaviconResultsCallback
& callback
,
143 base::CancelableTaskTracker
* tracker
) {
144 if (history_service_
) {
145 std::vector
<GURL
> icon_urls
;
146 icon_urls
.push_back(icon_url
);
147 return history_service_
->GetFavicons(
150 GetPixelSizesForFaviconScales(desired_size_in_dip
),
154 return RunWithEmptyResultAsync(callback
, tracker
);
157 base::CancelableTaskTracker::TaskId
FaviconService::GetFaviconImageForPageURL(
158 const GURL
& page_url
,
159 const favicon_base::FaviconImageCallback
& callback
,
160 base::CancelableTaskTracker
* tracker
) {
161 return GetFaviconForPageURLImpl(
163 favicon_base::FAVICON
,
164 GetPixelSizesForFaviconScales(gfx::kFaviconSize
),
165 Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults
,
166 base::Unretained(this),
172 base::CancelableTaskTracker::TaskId
FaviconService::GetRawFaviconForPageURL(
173 const GURL
& page_url
,
175 int desired_size_in_pixel
,
176 const favicon_base::FaviconRawBitmapCallback
& callback
,
177 base::CancelableTaskTracker
* tracker
) {
178 std::vector
<int> desired_sizes_in_pixel
;
179 desired_sizes_in_pixel
.push_back(desired_size_in_pixel
);
180 return GetFaviconForPageURLImpl(
183 desired_sizes_in_pixel
,
184 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults
,
185 base::Unretained(this),
187 desired_size_in_pixel
),
191 base::CancelableTaskTracker::TaskId
192 FaviconService::GetLargestRawFaviconForPageURL(
193 const GURL
& page_url
,
194 const std::vector
<int>& icon_types
,
195 int minimum_size_in_pixels
,
196 const favicon_base::FaviconRawBitmapCallback
& callback
,
197 base::CancelableTaskTracker
* tracker
) {
198 favicon_base::FaviconResultsCallback favicon_results_callback
=
199 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults
,
200 base::Unretained(this),
203 if (page_url
.SchemeIs(content::kChromeUIScheme
) ||
204 page_url
.SchemeIs(extensions::kExtensionScheme
)) {
205 std::vector
<int> desired_sizes_in_pixel
;
206 desired_sizes_in_pixel
.push_back(0);
207 return GetFaviconForChromeURL(profile_
,
209 desired_sizes_in_pixel
,
210 favicon_results_callback
,
213 if (history_service_
) {
214 return history_service_
->GetLargestFaviconForURL(page_url
, icon_types
,
215 minimum_size_in_pixels
, callback
, tracker
);
217 return RunWithEmptyResultAsync(favicon_results_callback
, tracker
);
220 base::CancelableTaskTracker::TaskId
FaviconService::GetFaviconForPageURL(
221 const GURL
& page_url
,
223 int desired_size_in_dip
,
224 const favicon_base::FaviconResultsCallback
& callback
,
225 base::CancelableTaskTracker
* tracker
) {
226 return GetFaviconForPageURLImpl(
229 GetPixelSizesForFaviconScales(desired_size_in_dip
),
234 base::CancelableTaskTracker::TaskId
235 FaviconService::UpdateFaviconMappingsAndFetch(
236 const GURL
& page_url
,
237 const std::vector
<GURL
>& icon_urls
,
239 int desired_size_in_dip
,
240 const favicon_base::FaviconResultsCallback
& callback
,
241 base::CancelableTaskTracker
* tracker
) {
242 if (history_service_
) {
243 return history_service_
->UpdateFaviconMappingsAndFetch(
247 GetPixelSizesForFaviconScales(desired_size_in_dip
),
251 return RunWithEmptyResultAsync(callback
, tracker
);
254 base::CancelableTaskTracker::TaskId
FaviconService::GetLargestRawFaviconForID(
255 favicon_base::FaviconID favicon_id
,
256 const favicon_base::FaviconRawBitmapCallback
& callback
,
257 base::CancelableTaskTracker
* tracker
) {
258 // Use 0 as |desired_size| to get the largest bitmap for |favicon_id| without
260 int desired_size
= 0;
261 favicon_base::FaviconResultsCallback callback_runner
=
262 Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults
,
263 base::Unretained(this),
267 if (history_service_
) {
268 return history_service_
->GetFaviconForID(
269 favicon_id
, desired_size
, callback_runner
, tracker
);
271 return RunWithEmptyResultAsync(callback_runner
, tracker
);
274 void FaviconService::SetFaviconOutOfDateForPage(const GURL
& page_url
) {
275 if (history_service_
)
276 history_service_
->SetFaviconsOutOfDateForPage(page_url
);
279 void FaviconService::CloneFavicon(const GURL
& old_page_url
,
280 const GURL
& new_page_url
) {
281 if (history_service_
)
282 history_service_
->CloneFavicons(old_page_url
, new_page_url
);
285 void FaviconService::SetImportedFavicons(
286 const favicon_base::FaviconUsageDataList
& favicon_usage
) {
287 if (history_service_
)
288 history_service_
->SetImportedFavicons(favicon_usage
);
291 void FaviconService::MergeFavicon(
292 const GURL
& page_url
,
293 const GURL
& icon_url
,
294 favicon_base::IconType icon_type
,
295 scoped_refptr
<base::RefCountedMemory
> bitmap_data
,
296 const gfx::Size
& pixel_size
) {
297 if (history_service_
) {
298 history_service_
->MergeFavicon(page_url
, icon_url
, icon_type
, bitmap_data
,
303 void FaviconService::SetFavicons(const GURL
& page_url
,
304 const GURL
& icon_url
,
305 favicon_base::IconType icon_type
,
306 const gfx::Image
& image
) {
307 if (!history_service_
)
310 gfx::ImageSkia image_skia
= image
.AsImageSkia();
311 image_skia
.EnsureRepsForSupportedScales();
312 const std::vector
<gfx::ImageSkiaRep
>& image_reps
= image_skia
.image_reps();
313 std::vector
<SkBitmap
> bitmaps
;
314 const std::vector
<float> favicon_scales
= favicon_base::GetFaviconScales();
315 for (size_t i
= 0; i
< image_reps
.size(); ++i
) {
316 // Don't save if the scale isn't one of supported favicon scales.
317 if (std::find(favicon_scales
.begin(),
318 favicon_scales
.end(),
319 image_reps
[i
].scale()) == favicon_scales
.end()) {
322 bitmaps
.push_back(image_reps
[i
].sk_bitmap());
324 history_service_
->SetFavicons(page_url
, icon_type
, icon_url
, bitmaps
);
327 void FaviconService::UnableToDownloadFavicon(const GURL
& icon_url
) {
328 MissingFaviconURLHash url_hash
= base::Hash(icon_url
.spec());
329 missing_favicon_urls_
.insert(url_hash
);
332 bool FaviconService::WasUnableToDownloadFavicon(const GURL
& icon_url
) const {
333 MissingFaviconURLHash url_hash
= base::Hash(icon_url
.spec());
334 return missing_favicon_urls_
.find(url_hash
) != missing_favicon_urls_
.end();
337 void FaviconService::ClearUnableToDownloadFavicons() {
338 missing_favicon_urls_
.clear();
341 FaviconService::~FaviconService() {}
343 base::CancelableTaskTracker::TaskId
FaviconService::GetFaviconForPageURLImpl(
344 const GURL
& page_url
,
346 const std::vector
<int>& desired_sizes_in_pixel
,
347 const favicon_base::FaviconResultsCallback
& callback
,
348 base::CancelableTaskTracker
* tracker
) {
349 if (page_url
.SchemeIs(content::kChromeUIScheme
) ||
350 page_url
.SchemeIs(extensions::kExtensionScheme
)) {
351 return GetFaviconForChromeURL(
352 profile_
, page_url
, desired_sizes_in_pixel
, callback
, tracker
);
354 if (history_service_
) {
355 return history_service_
->GetFaviconsForURL(page_url
,
357 desired_sizes_in_pixel
,
361 return RunWithEmptyResultAsync(callback
, tracker
);
364 void FaviconService::RunFaviconImageCallbackWithBitmapResults(
365 const favicon_base::FaviconImageCallback
& callback
,
366 int desired_size_in_dip
,
367 const std::vector
<favicon_base::FaviconRawBitmapResult
>&
368 favicon_bitmap_results
) {
369 favicon_base::FaviconImageResult image_result
;
370 image_result
.image
= favicon_base::SelectFaviconFramesFromPNGs(
371 favicon_bitmap_results
,
372 favicon_base::GetFaviconScales(),
373 desired_size_in_dip
);
374 favicon_base::SetFaviconColorSpace(&image_result
.image
);
376 image_result
.icon_url
= image_result
.image
.IsEmpty() ?
377 GURL() : favicon_bitmap_results
[0].icon_url
;
378 callback
.Run(image_result
);
381 void FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults(
382 const favicon_base::FaviconRawBitmapCallback
& callback
,
383 int desired_size_in_pixel
,
384 const std::vector
<favicon_base::FaviconRawBitmapResult
>&
385 favicon_bitmap_results
) {
386 if (favicon_bitmap_results
.empty() || !favicon_bitmap_results
[0].is_valid()) {
387 callback
.Run(favicon_base::FaviconRawBitmapResult());
391 favicon_base::FaviconRawBitmapResult bitmap_result
=
392 favicon_bitmap_results
[0];
394 // If the desired size is 0, SelectFaviconFrames() will return the largest
395 // bitmap without doing any resizing. As |favicon_bitmap_results| has bitmap
396 // data for a single bitmap, return it and avoid an unnecessary decode.
397 if (desired_size_in_pixel
== 0) {
398 callback
.Run(bitmap_result
);
402 // If history bitmap is already desired pixel size, return early.
403 if (bitmap_result
.pixel_size
.width() == desired_size_in_pixel
&&
404 bitmap_result
.pixel_size
.height() == desired_size_in_pixel
) {
405 callback
.Run(bitmap_result
);
409 // Convert raw bytes to SkBitmap, resize via SelectFaviconFrames(), then
411 std::vector
<float> desired_favicon_scales
;
412 desired_favicon_scales
.push_back(1.0f
);
413 gfx::Image resized_image
= favicon_base::SelectFaviconFramesFromPNGs(
414 favicon_bitmap_results
, desired_favicon_scales
, desired_size_in_pixel
);
416 std::vector
<unsigned char> resized_bitmap_data
;
417 if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_image
.AsBitmap(), false,
418 &resized_bitmap_data
)) {
419 callback
.Run(favicon_base::FaviconRawBitmapResult());
423 bitmap_result
.bitmap_data
= base::RefCountedBytes::TakeVector(
424 &resized_bitmap_data
);
425 callback
.Run(bitmap_result
);