| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/favicon/favicon_service.h" |
| |
| #include <cmath> |
| |
| #include "base/hash.h" |
| #include "base/message_loop/message_loop_proxy.h" |
| #include "chrome/browser/history/history_backend.h" |
| #include "chrome/browser/history/history_service.h" |
| #include "chrome/browser/history/history_service_factory.h" |
| #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" |
| #include "chrome/common/importer/imported_favicon_usage.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/favicon_base/favicon_types.h" |
| #include "components/favicon_base/favicon_util.h" |
| #include "components/favicon_base/select_favicon_frames.h" |
| #include "extensions/common/constants.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/codec/png_codec.h" |
| #include "ui/gfx/favicon_size.h" |
| #include "ui/gfx/image/image_skia.h" |
| |
| using base::Bind; |
| |
| namespace { |
| |
| void CancelOrRunFaviconResultsCallback( |
| const base::CancelableTaskTracker::IsCanceledCallback& is_canceled, |
| const favicon_base::FaviconResultsCallback& callback, |
| const std::vector<favicon_base::FaviconRawBitmapResult>& results) { |
| if (is_canceled.Run()) |
| return; |
| callback.Run(results); |
| } |
| |
| // Helper to run callback with empty results if we cannot get the history |
| // service. |
| base::CancelableTaskTracker::TaskId RunWithEmptyResultAsync( |
| const favicon_base::FaviconResultsCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| return tracker->PostTask( |
| base::MessageLoopProxy::current().get(), |
| FROM_HERE, |
| Bind(callback, std::vector<favicon_base::FaviconRawBitmapResult>())); |
| } |
| |
| // Return the TaskId to retreive the favicon from chrome specific URL. |
| base::CancelableTaskTracker::TaskId GetFaviconForChromeURL( |
| Profile* profile, |
| const GURL& page_url, |
| const std::vector<int>& desired_sizes_in_pixel, |
| const favicon_base::FaviconResultsCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| base::CancelableTaskTracker::IsCanceledCallback is_canceled_cb; |
| base::CancelableTaskTracker::TaskId id = |
| tracker->NewTrackedTaskId(&is_canceled_cb); |
| favicon_base::FaviconResultsCallback cancelable_cb = |
| Bind(&CancelOrRunFaviconResultsCallback, is_canceled_cb, callback); |
| ChromeWebUIControllerFactory::GetInstance()->GetFaviconForURL( |
| profile, page_url, desired_sizes_in_pixel, cancelable_cb); |
| return id; |
| } |
| |
| // Returns a vector of pixel edge sizes from |size_in_dip| and |
| // favicon_base::GetFaviconScales(). |
| std::vector<int> GetPixelSizesForFaviconScales(int size_in_dip) { |
| std::vector<float> scales = favicon_base::GetFaviconScales(); |
| std::vector<int> sizes_in_pixel; |
| for (size_t i = 0; i < scales.size(); ++i) { |
| sizes_in_pixel.push_back(std::ceil(size_in_dip * scales[i])); |
| } |
| return sizes_in_pixel; |
| } |
| |
| } // namespace |
| |
| FaviconService::FaviconService(Profile* profile, FaviconClient* favicon_client) |
| : history_service_( |
| HistoryServiceFactory::GetForProfile(profile, |
| Profile::EXPLICIT_ACCESS)), |
| profile_(profile), |
| favicon_client_(favicon_client) { |
| } |
| |
| // static |
| void FaviconService::FaviconResultsCallbackRunner( |
| const favicon_base::FaviconResultsCallback& callback, |
| const std::vector<favicon_base::FaviconRawBitmapResult>* results) { |
| callback.Run(*results); |
| } |
| |
| base::CancelableTaskTracker::TaskId FaviconService::GetFaviconImage( |
| const GURL& icon_url, |
| const favicon_base::FaviconImageCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| favicon_base::FaviconResultsCallback callback_runner = |
| Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults, |
| base::Unretained(this), callback, gfx::kFaviconSize); |
| if (history_service_) { |
| std::vector<GURL> icon_urls; |
| icon_urls.push_back(icon_url); |
| return history_service_->GetFavicons( |
| icon_urls, |
| favicon_base::FAVICON, |
| GetPixelSizesForFaviconScales(gfx::kFaviconSize), |
| callback_runner, |
| tracker); |
| } |
| return RunWithEmptyResultAsync(callback_runner, tracker); |
| } |
| |
| base::CancelableTaskTracker::TaskId FaviconService::GetRawFavicon( |
| const GURL& icon_url, |
| favicon_base::IconType icon_type, |
| int desired_size_in_pixel, |
| const favicon_base::FaviconRawBitmapCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| favicon_base::FaviconResultsCallback callback_runner = |
| Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults, |
| base::Unretained(this), |
| callback, |
| desired_size_in_pixel); |
| |
| if (history_service_) { |
| std::vector<GURL> icon_urls; |
| icon_urls.push_back(icon_url); |
| std::vector<int> desired_sizes_in_pixel; |
| desired_sizes_in_pixel.push_back(desired_size_in_pixel); |
| |
| return history_service_->GetFavicons( |
| icon_urls, icon_type, desired_sizes_in_pixel, callback_runner, tracker); |
| } |
| return RunWithEmptyResultAsync(callback_runner, tracker); |
| } |
| |
| base::CancelableTaskTracker::TaskId FaviconService::GetFavicon( |
| const GURL& icon_url, |
| favicon_base::IconType icon_type, |
| int desired_size_in_dip, |
| const favicon_base::FaviconResultsCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| if (history_service_) { |
| std::vector<GURL> icon_urls; |
| icon_urls.push_back(icon_url); |
| return history_service_->GetFavicons( |
| icon_urls, |
| icon_type, |
| GetPixelSizesForFaviconScales(desired_size_in_dip), |
| callback, |
| tracker); |
| } |
| return RunWithEmptyResultAsync(callback, tracker); |
| } |
| |
| base::CancelableTaskTracker::TaskId FaviconService::GetFaviconImageForPageURL( |
| const GURL& page_url, |
| const favicon_base::FaviconImageCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| return GetFaviconForPageURLImpl( |
| page_url, |
| favicon_base::FAVICON, |
| GetPixelSizesForFaviconScales(gfx::kFaviconSize), |
| Bind(&FaviconService::RunFaviconImageCallbackWithBitmapResults, |
| base::Unretained(this), |
| callback, |
| gfx::kFaviconSize), |
| tracker); |
| } |
| |
| base::CancelableTaskTracker::TaskId FaviconService::GetRawFaviconForPageURL( |
| const GURL& page_url, |
| int icon_types, |
| int desired_size_in_pixel, |
| const favicon_base::FaviconRawBitmapCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| std::vector<int> desired_sizes_in_pixel; |
| desired_sizes_in_pixel.push_back(desired_size_in_pixel); |
| return GetFaviconForPageURLImpl( |
| page_url, |
| icon_types, |
| desired_sizes_in_pixel, |
| Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults, |
| base::Unretained(this), |
| callback, |
| desired_size_in_pixel), |
| tracker); |
| } |
| |
| base::CancelableTaskTracker::TaskId |
| FaviconService::GetLargestRawFaviconForPageURL( |
| const GURL& page_url, |
| const std::vector<int>& icon_types, |
| int minimum_size_in_pixels, |
| const favicon_base::FaviconRawBitmapCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| favicon_base::FaviconResultsCallback favicon_results_callback = |
| Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults, |
| base::Unretained(this), |
| callback, |
| 0); |
| if (page_url.SchemeIs(content::kChromeUIScheme) || |
| page_url.SchemeIs(extensions::kExtensionScheme)) { |
| std::vector<int> desired_sizes_in_pixel; |
| desired_sizes_in_pixel.push_back(0); |
| return GetFaviconForChromeURL(profile_, |
| page_url, |
| desired_sizes_in_pixel, |
| favicon_results_callback, |
| tracker); |
| } |
| if (history_service_) { |
| return history_service_->GetLargestFaviconForURL(page_url, icon_types, |
| minimum_size_in_pixels, callback, tracker); |
| } |
| return RunWithEmptyResultAsync(favicon_results_callback, tracker); |
| } |
| |
| base::CancelableTaskTracker::TaskId FaviconService::GetFaviconForPageURL( |
| const GURL& page_url, |
| int icon_types, |
| int desired_size_in_dip, |
| const favicon_base::FaviconResultsCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| return GetFaviconForPageURLImpl( |
| page_url, |
| icon_types, |
| GetPixelSizesForFaviconScales(desired_size_in_dip), |
| callback, |
| tracker); |
| } |
| |
| base::CancelableTaskTracker::TaskId |
| FaviconService::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) { |
| if (history_service_) { |
| return history_service_->UpdateFaviconMappingsAndFetch( |
| page_url, |
| icon_urls, |
| icon_types, |
| GetPixelSizesForFaviconScales(desired_size_in_dip), |
| callback, |
| tracker); |
| } |
| return RunWithEmptyResultAsync(callback, tracker); |
| } |
| |
| base::CancelableTaskTracker::TaskId FaviconService::GetLargestRawFaviconForID( |
| favicon_base::FaviconID favicon_id, |
| const favicon_base::FaviconRawBitmapCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| // Use 0 as |desired_size| to get the largest bitmap for |favicon_id| without |
| // any resizing. |
| int desired_size = 0; |
| favicon_base::FaviconResultsCallback callback_runner = |
| Bind(&FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults, |
| base::Unretained(this), |
| callback, |
| desired_size); |
| |
| if (history_service_) { |
| return history_service_->GetFaviconForID( |
| favicon_id, desired_size, callback_runner, tracker); |
| } |
| return RunWithEmptyResultAsync(callback_runner, tracker); |
| } |
| |
| void FaviconService::SetFaviconOutOfDateForPage(const GURL& page_url) { |
| if (history_service_) |
| history_service_->SetFaviconsOutOfDateForPage(page_url); |
| } |
| |
| void FaviconService::CloneFavicon(const GURL& old_page_url, |
| const GURL& new_page_url) { |
| if (history_service_) |
| history_service_->CloneFavicons(old_page_url, new_page_url); |
| } |
| |
| void FaviconService::SetImportedFavicons( |
| const std::vector<ImportedFaviconUsage>& favicon_usage) { |
| if (history_service_) |
| history_service_->SetImportedFavicons(favicon_usage); |
| } |
| |
| void FaviconService::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) { |
| if (history_service_) { |
| history_service_->MergeFavicon(page_url, icon_url, icon_type, bitmap_data, |
| pixel_size); |
| } |
| } |
| |
| void FaviconService::SetFavicons(const GURL& page_url, |
| const GURL& icon_url, |
| favicon_base::IconType icon_type, |
| const gfx::Image& image) { |
| if (!history_service_) |
| return; |
| |
| gfx::ImageSkia image_skia = image.AsImageSkia(); |
| image_skia.EnsureRepsForSupportedScales(); |
| const std::vector<gfx::ImageSkiaRep>& image_reps = image_skia.image_reps(); |
| std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data; |
| const std::vector<float> favicon_scales = favicon_base::GetFaviconScales(); |
| for (size_t i = 0; i < image_reps.size(); ++i) { |
| // Don't save if the scale isn't one of supported favicon scale. |
| if (std::find(favicon_scales.begin(), |
| favicon_scales.end(), |
| image_reps[i].scale()) == favicon_scales.end()) { |
| continue; |
| } |
| |
| scoped_refptr<base::RefCountedBytes> bitmap_data( |
| new base::RefCountedBytes()); |
| if (gfx::PNGCodec::EncodeBGRASkBitmap(image_reps[i].sk_bitmap(), |
| false, |
| &bitmap_data->data())) { |
| gfx::Size pixel_size(image_reps[i].pixel_width(), |
| image_reps[i].pixel_height()); |
| favicon_base::FaviconRawBitmapData bitmap_data_element; |
| bitmap_data_element.bitmap_data = bitmap_data; |
| bitmap_data_element.pixel_size = pixel_size; |
| bitmap_data_element.icon_url = icon_url; |
| |
| favicon_bitmap_data.push_back(bitmap_data_element); |
| } |
| } |
| history_service_->SetFavicons(page_url, icon_type, favicon_bitmap_data); |
| } |
| |
| void FaviconService::UnableToDownloadFavicon(const GURL& icon_url) { |
| MissingFaviconURLHash url_hash = base::Hash(icon_url.spec()); |
| missing_favicon_urls_.insert(url_hash); |
| } |
| |
| bool FaviconService::WasUnableToDownloadFavicon(const GURL& icon_url) const { |
| MissingFaviconURLHash url_hash = base::Hash(icon_url.spec()); |
| return missing_favicon_urls_.find(url_hash) != missing_favicon_urls_.end(); |
| } |
| |
| void FaviconService::ClearUnableToDownloadFavicons() { |
| missing_favicon_urls_.clear(); |
| } |
| |
| FaviconService::~FaviconService() {} |
| |
| base::CancelableTaskTracker::TaskId FaviconService::GetFaviconForPageURLImpl( |
| const GURL& page_url, |
| int icon_types, |
| const std::vector<int>& desired_sizes_in_pixel, |
| const favicon_base::FaviconResultsCallback& callback, |
| base::CancelableTaskTracker* tracker) { |
| if (page_url.SchemeIs(content::kChromeUIScheme) || |
| page_url.SchemeIs(extensions::kExtensionScheme)) { |
| return GetFaviconForChromeURL( |
| profile_, page_url, desired_sizes_in_pixel, callback, tracker); |
| } |
| if (history_service_) { |
| return history_service_->GetFaviconsForURL(page_url, |
| icon_types, |
| desired_sizes_in_pixel, |
| callback, |
| tracker); |
| } |
| return RunWithEmptyResultAsync(callback, tracker); |
| } |
| |
| void FaviconService::RunFaviconImageCallbackWithBitmapResults( |
| const favicon_base::FaviconImageCallback& callback, |
| int desired_size_in_dip, |
| const std::vector<favicon_base::FaviconRawBitmapResult>& |
| favicon_bitmap_results) { |
| favicon_base::FaviconImageResult image_result; |
| image_result.image = favicon_base::SelectFaviconFramesFromPNGs( |
| favicon_bitmap_results, |
| favicon_base::GetFaviconScales(), |
| desired_size_in_dip); |
| favicon_base::SetFaviconColorSpace(&image_result.image); |
| |
| image_result.icon_url = image_result.image.IsEmpty() ? |
| GURL() : favicon_bitmap_results[0].icon_url; |
| callback.Run(image_result); |
| } |
| |
| void FaviconService::RunFaviconRawBitmapCallbackWithBitmapResults( |
| const favicon_base::FaviconRawBitmapCallback& callback, |
| int desired_size_in_pixel, |
| const std::vector<favicon_base::FaviconRawBitmapResult>& |
| favicon_bitmap_results) { |
| if (favicon_bitmap_results.empty() || !favicon_bitmap_results[0].is_valid()) { |
| callback.Run(favicon_base::FaviconRawBitmapResult()); |
| return; |
| } |
| |
| favicon_base::FaviconRawBitmapResult bitmap_result = |
| favicon_bitmap_results[0]; |
| |
| // If the desired size is 0, SelectFaviconFrames() will return the largest |
| // bitmap without doing any resizing. As |favicon_bitmap_results| has bitmap |
| // data for a single bitmap, return it and avoid an unnecessary decode. |
| if (desired_size_in_pixel == 0) { |
| callback.Run(bitmap_result); |
| return; |
| } |
| |
| // If history bitmap is already desired pixel size, return early. |
| if (bitmap_result.pixel_size.width() == desired_size_in_pixel && |
| bitmap_result.pixel_size.height() == desired_size_in_pixel) { |
| callback.Run(bitmap_result); |
| return; |
| } |
| |
| // Convert raw bytes to SkBitmap, resize via SelectFaviconFrames(), then |
| // convert back. |
| std::vector<float> desired_favicon_scales; |
| desired_favicon_scales.push_back(1.0f); |
| gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs( |
| favicon_bitmap_results, desired_favicon_scales, desired_size_in_pixel); |
| |
| std::vector<unsigned char> resized_bitmap_data; |
| if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_image.AsBitmap(), false, |
| &resized_bitmap_data)) { |
| callback.Run(favicon_base::FaviconRawBitmapResult()); |
| return; |
| } |
| |
| bitmap_result.bitmap_data = base::RefCountedBytes::TakeVector( |
| &resized_bitmap_data); |
| callback.Run(bitmap_result); |
| } |