diff options
author | sdefresne <sdefresne@chromium.org> | 2015-03-16 09:56:03 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-03-16 16:56:41 +0000 |
commit | fee69b4b384e94f03cd7d4dadf3140dd91bb9029 (patch) | |
tree | dc8bfb0c7de3326ed467998f34a5427f3eacf60d /components | |
parent | ee5ad4b13cc36b7ee494bc2b04a356d73ad32e3a (diff) | |
download | chromium_src-fee69b4b384e94f03cd7d4dadf3140dd91bb9029.zip chromium_src-fee69b4b384e94f03cd7d4dadf3140dd91bb9029.tar.gz chromium_src-fee69b4b384e94f03cd7d4dadf3140dd91bb9029.tar.bz2 |
Componentize FaviconService and FaviconHandler
Move FaviconService and FaviconHandler into //components/favicon/core/browser
as they have no problematic dependencies anymore.
Update DEPS and dependencies of favicon component.
BUG=359514,359513
Review URL: https://codereview.chromium.org/983043003
Cr-Commit-Position: refs/heads/master@{#320738}
Diffstat (limited to 'components')
-rw-r--r-- | components/BUILD.gn | 5 | ||||
-rw-r--r-- | components/components.gyp | 2 | ||||
-rw-r--r-- | components/favicon.gypi | 30 | ||||
-rw-r--r-- | components/favicon/DEPS | 3 | ||||
-rw-r--r-- | components/favicon/core/BUILD.gn | 3 | ||||
-rw-r--r-- | components/favicon/core/browser/BUILD.gn | 25 | ||||
-rw-r--r-- | components/favicon/core/browser/DEPS | 7 | ||||
-rw-r--r-- | components/favicon/core/browser/favicon_handler.cc | 693 | ||||
-rw-r--r-- | components/favicon/core/browser/favicon_handler.h | 309 | ||||
-rw-r--r-- | components/favicon/core/browser/favicon_service.cc | 391 | ||||
-rw-r--r-- | components/favicon/core/browser/favicon_service.h | 254 | ||||
-rw-r--r-- | components/favicon/core/browser/favicon_tab_helper_observer.h | 24 |
12 files changed, 1737 insertions, 9 deletions
diff --git a/components/BUILD.gn b/components/BUILD.gn index 12e023a..ed5b887 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn @@ -38,7 +38,7 @@ group("all_components") { "//components/dom_distiller/core", "//components/domain_reliability", "//components/enhanced_bookmarks", - "//components/favicon/core", + "//components/favicon/core/browser", "//components/favicon_base", "//components/feedback", "//components/gcm_driver", @@ -162,8 +162,9 @@ group("all_components") { "//components/data_reduction_proxy/core/common", # Should work, needs checking. "//components/dom_distiller/core", # Blocked on content. "//components/domain_reliability", # Blocked on content. + "//components/favicon/core/browser", # Blocked on keyed service. "//components/favicon_base", # Should work, needs checking. - "//components/favicon/core", # Blocked on keyed service. + "//components/feedback", # Blocked on content. "//components/gcm_driver", # Should work, needs checking. "//components/google/core/browser", # Should work, needs checking. "//components/history/core/browser", # Should work, needs checking. diff --git a/components/components.gyp b/components/components.gyp index bbf2571..5160960 100644 --- a/components/components.gyp +++ b/components/components.gyp @@ -27,7 +27,6 @@ 'domain_reliability.gypi', 'enhanced_bookmarks.gypi', 'error_page.gypi', - 'favicon.gypi', 'favicon_base.gypi', 'feedback.gypi', 'google.gypi', @@ -129,6 +128,7 @@ # Android WebView fails to build if a dependency on these targets is # introduced. 'includes': [ + 'favicon.gypi', 'gcm_driver.gypi', 'history.gypi', 'omnibox.gypi', diff --git a/components/favicon.gypi b/components/favicon.gypi index 262bd2b4..09a78f2 100644 --- a/components/favicon.gypi +++ b/components/favicon.gypi @@ -10,12 +10,11 @@ 'type': 'static_library', 'dependencies': [ '../ui/gfx/gfx.gyp:gfx_geometry', + '../url/url.gyp:url_lib', 'favicon_base', - 'keyed_service_core', ], 'sources': [ # Note: sources list duplicated in GN build. - 'favicon/core/browser/favicon_client.h', 'favicon/core/favicon_driver.h', 'favicon/core/favicon_url.cc', 'favicon/core/favicon_url.h', @@ -24,5 +23,32 @@ '..', ], }, + { + # GN version: //components/favicon/core/browser + 'target_name': 'favicon_core_browser', + 'type': 'static_library', + 'dependencies': [ + '../skia/skia.gyp:skia', + '../ui/gfx/gfx.gyp:gfx', + '../url/url.gyp:url_lib', + 'bookmarks_browser', + 'favicon_base', + 'favicon_core', + 'history_core_browser', + 'keyed_service_core', + ], + 'sources': [ + # Note: sources list duplicated in GN build. + 'favicon/core/browser/favicon_client.h', + 'favicon/core/browser/favicon_handler.cc', + 'favicon/core/browser/favicon_handler.h', + 'favicon/core/browser/favicon_service.cc', + 'favicon/core/browser/favicon_service.h', + 'favicon/core/browser/favicon_tab_helper_observer.h', + ], + 'include_dirs': [ + '..', + ], + }, ], } diff --git a/components/favicon/DEPS b/components/favicon/DEPS index 9b38a85..b6ea2b3 100644 --- a/components/favicon/DEPS +++ b/components/favicon/DEPS @@ -1,5 +1,4 @@ include_rules = [ "+components/favicon_base", - "+components/keyed_service/core", - "+ui/gfx/geometry", + "+ui/gfx", ] diff --git a/components/favicon/core/BUILD.gn b/components/favicon/core/BUILD.gn index 0f834cb..05bd40b 100644 --- a/components/favicon/core/BUILD.gn +++ b/components/favicon/core/BUILD.gn @@ -4,7 +4,6 @@ static_library("core") { sources = [ - "browser/favicon_client.h", "favicon_driver.h", "favicon_url.cc", "favicon_url.h", @@ -12,7 +11,7 @@ static_library("core") { deps = [ "//components/favicon_base", - "//components/keyed_service/core", "//ui/gfx/geometry", + "//url", ] } diff --git a/components/favicon/core/browser/BUILD.gn b/components/favicon/core/browser/BUILD.gn new file mode 100644 index 0000000..22e1246 --- /dev/null +++ b/components/favicon/core/browser/BUILD.gn @@ -0,0 +1,25 @@ +# Copyright 2015 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. + +static_library("browser") { + sources = [ + "favicon_client.h", + "favicon_handler.cc", + "favicon_handler.h", + "favicon_service.cc", + "favicon_service.h", + "favicon_tab_helper_observer.h", + ] + + deps = [ + "//components/bookmarks/browser", + "//components/favicon/core", + "//components/favicon_base", + "//components/history/core/browser", + "//components/keyed_service/core", + "//skia", + "//ui/gfx", + "//url", + ] +} diff --git a/components/favicon/core/browser/DEPS b/components/favicon/core/browser/DEPS new file mode 100644 index 0000000..8a86e5b --- /dev/null +++ b/components/favicon/core/browser/DEPS @@ -0,0 +1,7 @@ +include_rules = [ + "+components/bookmarks/browser", + "+components/history/core/browser", + "+components/keyed_service/core", + "+skia", + "+third_party/skia", +] diff --git a/components/favicon/core/browser/favicon_handler.cc b/components/favicon/core/browser/favicon_handler.cc new file mode 100644 index 0000000..1cff52c --- /dev/null +++ b/components/favicon/core/browser/favicon_handler.cc @@ -0,0 +1,693 @@ +// 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 "components/favicon/core/browser/favicon_handler.h" + +#include <algorithm> +#include <cmath> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/memory/ref_counted_memory.h" +#include "build/build_config.h" +#include "components/favicon/core/browser/favicon_client.h" +#include "components/favicon/core/browser/favicon_service.h" +#include "components/favicon/core/favicon_driver.h" +#include "components/favicon_base/favicon_util.h" +#include "components/favicon_base/select_favicon_frames.h" +#include "skia/ext/image_operations.h" +#include "ui/gfx/codec/png_codec.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/image/image_util.h" + +using favicon::FaviconURL; + +namespace { + +// Size (along each axis) of a touch icon. This currently corresponds to +// the apple touch icon for iPad. +const int kTouchIconSize = 144; + +bool DoUrlAndIconMatch(const FaviconURL& favicon_url, + const GURL& url, + favicon_base::IconType icon_type) { + return favicon_url.icon_url == url && favicon_url.icon_type == icon_type; +} + +// Returns true if all of the icon URLs and icon types in |bitmap_results| are +// identical and if they match the icon URL and icon type in |favicon_url|. +// Returns false if |bitmap_results| is empty. +bool DoUrlsAndIconsMatch( + const FaviconURL& favicon_url, + const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { + if (bitmap_results.empty()) + return false; + + const favicon_base::IconType icon_type = favicon_url.icon_type; + + for (const auto& bitmap_result : bitmap_results) { + if (favicon_url.icon_url != bitmap_result.icon_url || + icon_type != bitmap_result.icon_type) { + return false; + } + } + return true; +} + +std::string UrlWithoutFragment(const GURL& gurl) { + GURL::Replacements replacements; + replacements.ClearRef(); + return gurl.ReplaceComponents(replacements).spec(); +} + +bool UrlMatches(const GURL& gurl_a, const GURL& gurl_b) { + return UrlWithoutFragment(gurl_a) == UrlWithoutFragment(gurl_b); +} + +// Return true if |bitmap_result| is expired. +bool IsExpired(const favicon_base::FaviconRawBitmapResult& bitmap_result) { + return bitmap_result.expired; +} + +// Return true if |bitmap_result| is valid. +bool IsValid(const favicon_base::FaviconRawBitmapResult& bitmap_result) { + return bitmap_result.is_valid(); +} + +// Returns true if at least one of the bitmaps in |bitmap_results| is expired or +// if |bitmap_results| is missing favicons for |desired_size_in_dip| and one of +// the scale factors in favicon_base::GetFaviconScales(). +bool HasExpiredOrIncompleteResult( + int desired_size_in_dip, + const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { + // Check if at least one of the bitmaps is expired. + std::vector<favicon_base::FaviconRawBitmapResult>::const_iterator it = + std::find_if(bitmap_results.begin(), bitmap_results.end(), IsExpired); + if (it != bitmap_results.end()) + return true; + + // Any favicon size is good if the desired size is 0. + if (desired_size_in_dip == 0) + return false; + + // Check if the favicon for at least one of the scale factors is missing. + // |bitmap_results| should always be complete for data inserted by + // FaviconHandler as the FaviconHandler stores favicons resized to all + // of favicon_base::GetFaviconScales() into the history backend. + // Examples of when |bitmap_results| can be incomplete: + // - Favicons inserted into the history backend by sync. + // - Favicons for imported bookmarks. + std::vector<gfx::Size> favicon_sizes; + for (const auto& bitmap_result : bitmap_results) + favicon_sizes.push_back(bitmap_result.pixel_size); + + std::vector<float> favicon_scales = favicon_base::GetFaviconScales(); + for (float favicon_scale : favicon_scales) { + int edge_size_in_pixel = std::ceil(desired_size_in_dip * favicon_scale); + auto it = std::find(favicon_sizes.begin(), favicon_sizes.end(), + gfx::Size(edge_size_in_pixel, edge_size_in_pixel)); + if (it == favicon_sizes.end()) + return true; + } + return false; +} + +// Returns true if at least one of |bitmap_results| is valid. +bool HasValidResult( + const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { + return std::find_if(bitmap_results.begin(), bitmap_results.end(), IsValid) != + bitmap_results.end(); +} + +// Returns the index of the entry with the largest area. +int GetLargestSizeIndex(const std::vector<gfx::Size>& sizes) { + DCHECK(!sizes.empty()); + size_t ret = 0; + for (size_t i = 1; i < sizes.size(); ++i) { + if (sizes[ret].GetArea() < sizes[i].GetArea()) + ret = i; + } + return static_cast<int>(ret); +} + +// Return the index of a size which is same as the given |size|, -1 returned if +// there is no such bitmap. +int GetIndexBySize(const std::vector<gfx::Size>& sizes, + const gfx::Size& size) { + DCHECK(!sizes.empty()); + std::vector<gfx::Size>::const_iterator i = + std::find(sizes.begin(), sizes.end(), size); + if (i == sizes.end()) + return -1; + + return static_cast<int>(i - sizes.begin()); +} + +// Compare function used for std::stable_sort to sort as descend. +bool CompareIconSize(const FaviconURL& b1, const FaviconURL& b2) { + int area1 = 0; + if (!b1.icon_sizes.empty()) + area1 = b1.icon_sizes.front().GetArea(); + + int area2 = 0; + if (!b2.icon_sizes.empty()) + area2 = b2.icon_sizes.front().GetArea(); + + return area1 > area2; +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// + +FaviconHandler::DownloadRequest::DownloadRequest() + : icon_type(favicon_base::INVALID_ICON) { +} + +FaviconHandler::DownloadRequest::~DownloadRequest() { +} + +FaviconHandler::DownloadRequest::DownloadRequest( + const GURL& url, + const GURL& image_url, + favicon_base::IconType icon_type) + : url(url), image_url(image_url), icon_type(icon_type) { +} + +//////////////////////////////////////////////////////////////////////////////// + +FaviconHandler::FaviconCandidate::FaviconCandidate() + : score(0), icon_type(favicon_base::INVALID_ICON) { +} + +FaviconHandler::FaviconCandidate::~FaviconCandidate() { +} + +FaviconHandler::FaviconCandidate::FaviconCandidate( + const GURL& url, + const GURL& image_url, + const gfx::Image& image, + float score, + favicon_base::IconType icon_type) + : url(url), + image_url(image_url), + image(image), + score(score), + icon_type(icon_type) {} + +//////////////////////////////////////////////////////////////////////////////// + +FaviconHandler::FaviconHandler(FaviconService* service, + FaviconClient* client, + FaviconDriver* driver, + Type icon_type, + bool download_largest_icon) + : got_favicon_from_history_(false), + favicon_expired_or_incomplete_(false), + icon_types_(icon_type == FAVICON + ? favicon_base::FAVICON + : favicon_base::TOUCH_ICON | + favicon_base::TOUCH_PRECOMPOSED_ICON), + download_largest_icon_(download_largest_icon), + service_(service), + client_(client), + driver_(driver) { + DCHECK(driver_); +} + +FaviconHandler::~FaviconHandler() { +} + +void FaviconHandler::FetchFavicon(const GURL& url) { + cancelable_task_tracker_.TryCancelAll(); + + url_ = url; + + favicon_expired_or_incomplete_ = got_favicon_from_history_ = false; + image_urls_.clear(); + + // Request the favicon from the history service. In parallel to this the + // renderer is going to notify us (well WebContents) when the favicon url is + // available. + GetFaviconForURLFromFaviconService( + url_, icon_types_, + base::Bind(&FaviconHandler::OnFaviconDataForInitialURLFromFaviconService, + base::Unretained(this)), + &cancelable_task_tracker_); +} + +bool FaviconHandler::UpdateFaviconCandidate(const GURL& url, + const GURL& image_url, + const gfx::Image& image, + float score, + favicon_base::IconType icon_type) { + bool replace_best_favicon_candidate = false; + bool exact_match = false; + if (download_largest_icon_) { + replace_best_favicon_candidate = + image.Size().GetArea() > + best_favicon_candidate_.image.Size().GetArea(); + + gfx::Size largest = best_favicon_candidate_.image.Size(); + if (replace_best_favicon_candidate) + largest = image.Size(); + + // The size of the downloaded icon may not match the declared size. Stop + // downloading if: + // - current candidate is only candidate. + // - next candidate doesn't have sizes attributes, in this case, the rest + // candidates don't have sizes attribute either, stop downloading now, + // otherwise, all favicon without sizes attribute are downloaded. + // - next candidate has sizes attribute and it is not larger than largest, + // - current candidate is maximal one we want. + const int maximal_size = GetMaximalIconSize(icon_type); + exact_match = image_urls_.size() == 1 || + image_urls_[1].icon_sizes.empty() || + image_urls_[1].icon_sizes[0].GetArea() <= largest.GetArea() || + (image.Size().width() == maximal_size && + image.Size().height() == maximal_size); + } else { + exact_match = score == 1 || preferred_icon_size() == 0; + replace_best_favicon_candidate = + exact_match || + best_favicon_candidate_.icon_type == favicon_base::INVALID_ICON || + score > best_favicon_candidate_.score; + } + if (replace_best_favicon_candidate) { + best_favicon_candidate_ = FaviconCandidate( + url, image_url, image, score, icon_type); + } + return exact_match; +} + +void FaviconHandler::SetFavicon(const GURL& url, + const GURL& icon_url, + const gfx::Image& image, + favicon_base::IconType icon_type) { + if (ShouldSaveFavicon(url)) + SetHistoryFavicons(url, icon_url, icon_type, image); + + if (!UrlMatches(url, url_) || PageChangedSinceFaviconWasRequested()) + return; + + NotifyFaviconAvailable( + icon_url, + image, + icon_type == favicon_base::FAVICON && !download_largest_icon_); +} + +void FaviconHandler::NotifyFaviconAvailable( + const std::vector<favicon_base::FaviconRawBitmapResult>& + favicon_bitmap_results, + bool is_active_favicon) { + gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs( + favicon_bitmap_results, + favicon_base::GetFaviconScales(), + preferred_icon_size()); + // The history service sends back results for a single icon URL, so it does + // not matter which result we get the |icon_url| from. + const GURL icon_url = favicon_bitmap_results.empty() ? + GURL() : favicon_bitmap_results[0].icon_url; + NotifyFaviconAvailable(icon_url, resized_image, is_active_favicon); +} + +void FaviconHandler::NotifyFaviconAvailable(const GURL& icon_url, + const gfx::Image& image, + bool is_active_favicon) { + gfx::Image image_with_adjusted_colorspace = image; + favicon_base::SetFaviconColorSpace(&image_with_adjusted_colorspace); + + driver_->OnFaviconAvailable( + image_with_adjusted_colorspace, icon_url, is_active_favicon); +} + +void FaviconHandler::OnUpdateFaviconURL( + const std::vector<FaviconURL>& candidates) { + image_urls_.clear(); + best_favicon_candidate_ = FaviconCandidate(); + for (const FaviconURL& candidate : candidates) { + if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) + image_urls_.push_back(candidate); + } + + if (download_largest_icon_) + SortAndPruneImageUrls(); + + // TODO(davemoore) Should clear on empty url. Currently we ignore it. + // This appears to be what FF does as well. + if (!image_urls_.empty()) + ProcessCurrentUrl(); +} + +void FaviconHandler::ProcessCurrentUrl() { + DCHECK(!image_urls_.empty()); + + // current_candidate() may return NULL if download_largest_icon_ is true and + // all the sizes are larger than the max. + if (PageChangedSinceFaviconWasRequested() || !current_candidate()) + return; + + if (current_candidate()->icon_type == favicon_base::FAVICON && + !download_largest_icon_) { + if (!favicon_expired_or_incomplete_ && + driver_->GetActiveFaviconValidity() && + DoUrlAndIconMatch(*current_candidate(), + driver_->GetActiveFaviconURL(), + favicon_base::FAVICON)) + return; + } else if (!favicon_expired_or_incomplete_ && got_favicon_from_history_ && + HasValidResult(history_results_) && + DoUrlsAndIconsMatch(*current_candidate(), history_results_)) { + return; + } + + if (got_favicon_from_history_) + DownloadFaviconOrAskFaviconService(driver_->GetActiveURL(), + current_candidate()->icon_url, + current_candidate()->icon_type); +} + +void FaviconHandler::OnDidDownloadFavicon( + int id, + const GURL& image_url, + const std::vector<SkBitmap>& bitmaps, + const std::vector<gfx::Size>& original_bitmap_sizes) { + DownloadRequests::iterator i = download_requests_.find(id); + if (i == download_requests_.end()) { + // Currently WebContents notifies us of ANY downloads so that it is + // possible to get here. + return; + } + + DownloadRequest download_request = i->second; + download_requests_.erase(i); + + if (current_candidate() && + DoUrlAndIconMatch(*current_candidate(), + image_url, + download_request.icon_type)) { + bool request_next_icon = true; + float score = 0.0f; + gfx::ImageSkia image_skia; + if (download_largest_icon_ && !bitmaps.empty()) { + int index = -1; + // Use the largest bitmap if FaviconURL doesn't have sizes attribute. + if (current_candidate()->icon_sizes.empty()) { + index = GetLargestSizeIndex(original_bitmap_sizes); + } else { + index = GetIndexBySize(original_bitmap_sizes, + current_candidate()->icon_sizes[0]); + // Find largest bitmap if there is no one exactly matched. + if (index == -1) + index = GetLargestSizeIndex(original_bitmap_sizes); + } + image_skia = gfx::ImageSkia(gfx::ImageSkiaRep(bitmaps[index], 1)); + } else { + image_skia = CreateFaviconImageSkia(bitmaps, + original_bitmap_sizes, + preferred_icon_size(), + &score); + } + + if (!image_skia.isNull()) { + gfx::Image image(image_skia); + // The downloaded icon is still valid when there is no FaviconURL update + // during the downloading. + if (!bitmaps.empty()) { + request_next_icon = !UpdateFaviconCandidate( + download_request.url, image_url, image, score, + download_request.icon_type); + } + } + if (request_next_icon && !PageChangedSinceFaviconWasRequested() && + image_urls_.size() > 1) { + // Remove the first member of image_urls_ and process the remaining. + image_urls_.erase(image_urls_.begin()); + ProcessCurrentUrl(); + } else if (best_favicon_candidate_.icon_type != + favicon_base::INVALID_ICON) { + // No more icons to request, set the favicon from the candidate. + SetFavicon(best_favicon_candidate_.url, + best_favicon_candidate_.image_url, + best_favicon_candidate_.image, + best_favicon_candidate_.icon_type); + // Reset candidate. + image_urls_.clear(); + download_requests_.clear(); + best_favicon_candidate_ = FaviconCandidate(); + } + } +} + +bool FaviconHandler::PageChangedSinceFaviconWasRequested() { + if (UrlMatches(driver_->GetActiveURL(), url_) && url_.is_valid()) { + return false; + } + // If the URL has changed out from under us (as will happen with redirects) + // return true. + return true; +} + +int FaviconHandler::DownloadFavicon(const GURL& image_url, + int max_bitmap_size) { + if (!image_url.is_valid()) { + NOTREACHED(); + return 0; + } + return driver_->StartDownload(image_url, max_bitmap_size); +} + +void FaviconHandler::UpdateFaviconMappingAndFetch( + const GURL& page_url, + const GURL& icon_url, + favicon_base::IconType icon_type, + const favicon_base::FaviconResultsCallback& callback, + base::CancelableTaskTracker* tracker) { + // TODO(pkotwicz): pass in all of |image_urls_| to + // UpdateFaviconMappingsAndFetch(). + if (service_) { + std::vector<GURL> icon_urls; + icon_urls.push_back(icon_url); + service_->UpdateFaviconMappingsAndFetch(page_url, icon_urls, icon_type, + preferred_icon_size(), callback, + tracker); + } +} + +void FaviconHandler::GetFaviconFromFaviconService( + const GURL& icon_url, + favicon_base::IconType icon_type, + const favicon_base::FaviconResultsCallback& callback, + base::CancelableTaskTracker* tracker) { + if (service_) { + service_->GetFavicon(icon_url, icon_type, preferred_icon_size(), callback, + tracker); + } +} + +void FaviconHandler::GetFaviconForURLFromFaviconService( + const GURL& page_url, + int icon_types, + const favicon_base::FaviconResultsCallback& callback, + base::CancelableTaskTracker* tracker) { + if (service_) { + service_->GetFaviconForPageURL(page_url, icon_types, preferred_icon_size(), + callback, tracker); + } +} + +void FaviconHandler::SetHistoryFavicons(const GURL& page_url, + const GURL& icon_url, + favicon_base::IconType icon_type, + const gfx::Image& image) { + if (service_) { + service_->SetFavicons(page_url, icon_url, icon_type, image); + } +} + +bool FaviconHandler::ShouldSaveFavicon(const GURL& url) { + if (!driver_->IsOffTheRecord()) + return true; + + // Otherwise store the favicon if the page is bookmarked. + return client_->IsBookmarked(url); +} + +int FaviconHandler::GetMaximalIconSize(favicon_base::IconType icon_type) { + switch (icon_type) { + case favicon_base::FAVICON: +#if defined(OS_ANDROID) + return 192; +#else + return gfx::ImageSkia::GetMaxSupportedScale() * gfx::kFaviconSize; +#endif + case favicon_base::TOUCH_ICON: + case favicon_base::TOUCH_PRECOMPOSED_ICON: + return kTouchIconSize; + case favicon_base::INVALID_ICON: + return 0; + } + NOTREACHED(); + return 0; +} + +void FaviconHandler::OnFaviconDataForInitialURLFromFaviconService( + const std::vector<favicon_base::FaviconRawBitmapResult>& + favicon_bitmap_results) { + if (PageChangedSinceFaviconWasRequested()) + return; + got_favicon_from_history_ = true; + history_results_ = favicon_bitmap_results; + bool has_results = !favicon_bitmap_results.empty(); + favicon_expired_or_incomplete_ = has_results && HasExpiredOrIncompleteResult( + preferred_icon_size(), favicon_bitmap_results); + bool has_valid_result = HasValidResult(favicon_bitmap_results); + + if (has_results && icon_types_ == favicon_base::FAVICON && + !download_largest_icon_ && !driver_->GetActiveFaviconValidity() && + (!current_candidate() || + DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results))) { + if (has_valid_result) { + // The db knows the favicon (although it may be out of date) and the entry + // doesn't have an icon. Set the favicon now, and if the favicon turns out + // to be expired (or the wrong url) we'll fetch later on. This way the + // user doesn't see a flash of the default favicon. + NotifyFaviconAvailable(favicon_bitmap_results, true); + } else { + // If |favicon_bitmap_results| does not have any valid results, treat the + // favicon as if it's expired. + // TODO(pkotwicz): Do something better. + favicon_expired_or_incomplete_ = true; + } + } + if (has_results && !favicon_expired_or_incomplete_) { + if (current_candidate() && + !DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results)) { + // Mapping in the database is wrong. DownloadFavIconOrAskHistory will + // update the mapping for this url and download the favicon if we don't + // already have it. + DownloadFaviconOrAskFaviconService(driver_->GetActiveURL(), + current_candidate()->icon_url, + current_candidate()->icon_type); + } + } else if (current_candidate()) { + // We know the official url for the favicon, but either don't have the + // favicon or it's expired. Continue on to DownloadFaviconOrAskHistory to + // either download or check history again. + DownloadFaviconOrAskFaviconService(driver_->GetActiveURL(), + current_candidate()->icon_url, + current_candidate()->icon_type); + } + // else we haven't got the icon url. When we get it we'll ask the + // renderer to download the icon. + + if (has_valid_result && + (icon_types_ != favicon_base::FAVICON || download_largest_icon_)) + NotifyFaviconAvailable(favicon_bitmap_results, false); +} + +void FaviconHandler::DownloadFaviconOrAskFaviconService( + const GURL& page_url, + const GURL& icon_url, + favicon_base::IconType icon_type) { + if (favicon_expired_or_incomplete_) { + // We have the mapping, but the favicon is out of date. Download it now. + ScheduleDownload(page_url, icon_url, icon_type); + } else { + // We don't know the favicon, but we may have previously downloaded the + // favicon for another page that shares the same favicon. Ask for the + // favicon given the favicon URL. + if (driver_->IsOffTheRecord()) { + GetFaviconFromFaviconService( + icon_url, icon_type, + base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)), + &cancelable_task_tracker_); + } else { + // Ask the history service for the icon. This does two things: + // 1. Attempts to fetch the favicon data from the database. + // 2. If the favicon exists in the database, this updates the database to + // include the mapping between the page url and the favicon url. + // This is asynchronous. The history service will call back when done. + UpdateFaviconMappingAndFetch( + page_url, icon_url, icon_type, + base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)), + &cancelable_task_tracker_); + } + } +} + +void FaviconHandler::OnFaviconData(const std::vector< + favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) { + if (PageChangedSinceFaviconWasRequested()) + return; + + bool has_results = !favicon_bitmap_results.empty(); + bool has_expired_or_incomplete_result = HasExpiredOrIncompleteResult( + preferred_icon_size(), favicon_bitmap_results); + bool has_valid_result = HasValidResult(favicon_bitmap_results); + + if (has_results && icon_types_ == favicon_base::FAVICON && + !download_largest_icon_) { + if (has_valid_result) { + // There is a favicon, set it now. If expired we'll download the current + // one again, but at least the user will get some icon instead of the + // default and most likely the current one is fine anyway. + NotifyFaviconAvailable(favicon_bitmap_results, true); + } + if (has_expired_or_incomplete_result) { + // The favicon is out of date. Request the current one. + ScheduleDownload(driver_->GetActiveURL(), + driver_->GetActiveFaviconURL(), + favicon_base::FAVICON); + } + } else if (current_candidate() && + (!has_results || has_expired_or_incomplete_result || + !(DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results)))) { + // We don't know the favicon, it is out of date or its type is not same as + // one got from page. Request the current one. + ScheduleDownload(driver_->GetActiveURL(), + current_candidate()->icon_url, + current_candidate()->icon_type); + } + history_results_ = favicon_bitmap_results; + + if (has_valid_result && + (icon_types_ != favicon_base::FAVICON || download_largest_icon_)) { + NotifyFaviconAvailable(favicon_bitmap_results, false); + } +} + +int FaviconHandler::ScheduleDownload(const GURL& url, + const GURL& image_url, + favicon_base::IconType icon_type) { + // A max bitmap size is specified to avoid receiving huge bitmaps in + // OnDidDownloadFavicon(). See FaviconDriver::StartDownload() + // for more details about the max bitmap size. + const int download_id = DownloadFavicon(image_url, + GetMaximalIconSize(icon_type)); + if (download_id) { + // Download ids should be unique. + DCHECK(download_requests_.find(download_id) == download_requests_.end()); + download_requests_[download_id] = + DownloadRequest(url, image_url, icon_type); + } + + return download_id; +} + +void FaviconHandler::SortAndPruneImageUrls() { + // Not using const-reference since the loop mutates FaviconURL::icon_sizes. + for (favicon::FaviconURL& image_url : image_urls_) { + if (image_url.icon_sizes.empty()) + continue; + + gfx::Size largest = + image_url.icon_sizes[GetLargestSizeIndex(image_url.icon_sizes)]; + image_url.icon_sizes.clear(); + image_url.icon_sizes.push_back(largest); + } + std::stable_sort(image_urls_.begin(), image_urls_.end(), + CompareIconSize); +} diff --git a/components/favicon/core/browser/favicon_handler.h b/components/favicon/core/browser/favicon_handler.h new file mode 100644 index 0000000..5d890c0 --- /dev/null +++ b/components/favicon/core/browser/favicon_handler.h @@ -0,0 +1,309 @@ +// 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. + +#ifndef COMPONENTS_FAVICON_CORE_BROWSER_FAVICON_HANDLER_H_ +#define COMPONENTS_FAVICON_CORE_BROWSER_FAVICON_HANDLER_H_ + +#include <deque> +#include <map> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/memory/ref_counted.h" +#include "base/task/cancelable_task_tracker.h" +#include "components/favicon/core/favicon_url.h" +#include "components/favicon_base/favicon_callback.h" +#include "ui/gfx/favicon_size.h" +#include "ui/gfx/image/image.h" +#include "url/gurl.h" + +class FaviconClient; +class FaviconDriver; +class FaviconService; +class SkBitmap; + +namespace base { +class RefCountedMemory; +} + +// FaviconHandler works with FaviconDriver to fetch the specific type of +// favicon. +// +// FetchFavicon requests the favicon from the favicon service which in turn +// requests the favicon from the history database. At this point +// we only know the URL of the page, and not necessarily the url of the +// favicon. To ensure we handle reloading stale favicons as well as +// reloading a favicon on page reload we always request the favicon from +// history regardless of whether the active favicon is valid. +// +// After the navigation two types of events are delivered (which is +// first depends upon who is faster): notification from the history +// db on our request for the favicon +// (OnFaviconDataForInitialURLFromFaviconService), or a message from the +// renderer giving us the URL of the favicon for the page (SetFaviconURL). +// . If the history db has a valid up to date favicon for the page, we update +// the current page and use the favicon. +// . When we receive the favicon url if it matches that of the current page +// and the current page's favicon is set, we do nothing (everything is +// ok). +// . On the other hand if the database does not know the favicon for url, or +// the favicon is out date, or the URL from the renderer does not match that +// of the current page we proceed to DownloadFaviconOrAskHistory. Before we +// invoke DownloadFaviconOrAskHistory we wait until we've received both +// the favicon url and the callback from history. We wait to ensure we +// truly know both the favicon url and the state of the database. +// +// DownloadFaviconOrAskHistory does the following: +// . If we have a valid favicon, but it is expired we ask the renderer to +// download the favicon. +// . Otherwise we ask the history database to update the mapping from +// page url to favicon url and call us back with the favicon. Remember, it is +// possible for the db to already have the favicon, just not the mapping +// between page to favicon url. The callback for this is OnFaviconData. +// +// OnFaviconData either updates the favicon of the current page (if the +// db knew about the favicon), or requests the renderer to download the +// favicon. +// +// When the renderer downloads favicons, it considers the entire list of +// favicon candidates, if |download_largest_favicon_| is true, the largest +// favicon will be used, otherwise the one that best matches the preferred size +// is chosen (or the first one if there is no preferred size). Once the +// matching favicon has been determined, SetFavicon is called which updates +// the page's favicon and notifies the database to save the favicon. + +class FaviconHandler { + public: + enum Type { FAVICON, TOUCH }; + + FaviconHandler(FaviconService* service, + FaviconClient* client, + FaviconDriver* driver, + Type icon_type, + bool download_largest_icon); + virtual ~FaviconHandler(); + + // Initiates loading the favicon for the specified url. + void FetchFavicon(const GURL& url); + + // Message Handler. Must be public, because also called from + // PrerenderContents. Collects the |image_urls| list. + void OnUpdateFaviconURL(const std::vector<favicon::FaviconURL>& candidates); + + // Processes the current image_irls_ entry, requesting the image from the + // history / download service. + void ProcessCurrentUrl(); + + // Message handler for ImageHostMsg_DidDownloadImage. Called when the image + // at |image_url| has been downloaded. + // |bitmaps| is a list of all the frames of the image at |image_url|. + // |original_bitmap_sizes| are the sizes of |bitmaps| before they were resized + // to the maximum bitmap size passed to DownloadFavicon(). + void OnDidDownloadFavicon( + int id, + const GURL& image_url, + const std::vector<SkBitmap>& bitmaps, + const std::vector<gfx::Size>& original_bitmap_sizes); + + // For testing. + const std::vector<favicon::FaviconURL>& image_urls() const { + return image_urls_; + } + + protected: + // These virtual methods make FaviconHandler testable and are overridden by + // TestFaviconHandler. + + // Asks the render to download favicon, returns the request id. + virtual int DownloadFavicon(const GURL& image_url, int max_bitmap_size); + + // Ask the favicon from history + virtual void UpdateFaviconMappingAndFetch( + const GURL& page_url, + const GURL& icon_url, + favicon_base::IconType icon_type, + const favicon_base::FaviconResultsCallback& callback, + base::CancelableTaskTracker* tracker); + + virtual void GetFaviconFromFaviconService( + const GURL& icon_url, + favicon_base::IconType icon_type, + const favicon_base::FaviconResultsCallback& callback, + base::CancelableTaskTracker* tracker); + + virtual void GetFaviconForURLFromFaviconService( + const GURL& page_url, + int icon_types, + const favicon_base::FaviconResultsCallback& callback, + base::CancelableTaskTracker* tracker); + + virtual void SetHistoryFavicons(const GURL& page_url, + const GURL& icon_url, + favicon_base::IconType icon_type, + const gfx::Image& image); + + // Returns true if the favicon should be saved. + virtual bool ShouldSaveFavicon(const GURL& url); + + private: + // For testing: + friend class FaviconTabHelperTest; + friend class TestFaviconHandler; + + // Represents an in progress download of an image from the renderer. + struct DownloadRequest { + DownloadRequest(); + ~DownloadRequest(); + + DownloadRequest(const GURL& url, + const GURL& image_url, + favicon_base::IconType icon_type); + + GURL url; + GURL image_url; + favicon_base::IconType icon_type; + }; + + // Used to track a candidate for the favicon. + struct FaviconCandidate { + FaviconCandidate(); + ~FaviconCandidate(); + + FaviconCandidate(const GURL& url, + const GURL& image_url, + const gfx::Image& image, + float score, + favicon_base::IconType icon_type); + + GURL url; + GURL image_url; + gfx::Image image; + float score; + favicon_base::IconType icon_type; + }; + + // Get the maximal icon size in pixels for a icon of type |icon_type| for the + // current platform. + static int GetMaximalIconSize(favicon_base::IconType icon_type); + + // See description above class for details. + void OnFaviconDataForInitialURLFromFaviconService(const std::vector< + favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results); + + // If the favicon has expired, asks the renderer to download the favicon. + // Otherwise asks history to update the mapping between page url and icon + // url with a callback to OnFaviconData when done. + void DownloadFaviconOrAskFaviconService(const GURL& page_url, + const GURL& icon_url, + favicon_base::IconType icon_type); + + // See description above class for details. + void OnFaviconData(const std::vector<favicon_base::FaviconRawBitmapResult>& + favicon_bitmap_results); + + // Schedules a download for the specified entry. This adds the request to + // download_requests_. + int ScheduleDownload(const GURL& url, + const GURL& image_url, + favicon_base::IconType icon_type); + + // Updates |favicon_candidate_| and returns true if it is an exact match. + bool UpdateFaviconCandidate(const GURL& url, + const GURL& image_url, + const gfx::Image& image, + float score, + favicon_base::IconType icon_type); + + // Sets the image data for the favicon. + void SetFavicon(const GURL& url, + const GURL& icon_url, + const gfx::Image& image, + favicon_base::IconType icon_type); + + // Notifies |driver_| favicon available. See + // FaviconDriver::NotifyFaviconAvailable() for |is_active_favicon| in detail. + void NotifyFaviconAvailable( + const std::vector<favicon_base::FaviconRawBitmapResult>& + favicon_bitmap_results, + bool is_active_favicon); + void NotifyFaviconAvailable(const GURL& icon_url, + const gfx::Image& image, + bool is_active_favicon); + + // Return the current candidate if any. + favicon::FaviconURL* current_candidate() { + return (!image_urls_.empty()) ? &image_urls_.front() : NULL; + } + + // Returns whether the page's url changed since the favicon was requested. + bool PageChangedSinceFaviconWasRequested(); + + // Returns the preferred size of the image. 0 means no preference (any size + // will do). + int preferred_icon_size() const { + if (download_largest_icon_) + return 0; + return icon_types_ == favicon_base::FAVICON ? gfx::kFaviconSize : 0; + } + + // Sorts the entries in |image_urls_| by icon size in descending order. + // Additionally removes any entries whose sizes are all greater than the max + // allowed size. + void SortAndPruneImageUrls(); + + // Used for FaviconService requests. + base::CancelableTaskTracker cancelable_task_tracker_; + + // URL of the page we're requesting the favicon for. + GURL url_; + + // Whether we are waiting for data from the FaviconService. + bool waiting_for_favicon_service_data_; + + // Whether we got data back for the initial request to the FaviconService. + bool got_favicon_from_history_; + + // Whether the favicon is out of date or the favicon data in + // |history_results_| is known to be incomplete. If true, it means history + // knows about the favicon, but we need to download the favicon because the + // icon has expired or the data in the database is incomplete. + bool favicon_expired_or_incomplete_; + + // Requests to the renderer to download favicons. + typedef std::map<int, DownloadRequest> DownloadRequests; + DownloadRequests download_requests_; + + // The combination of the supported icon types. + const int icon_types_; + + // Whether the largest icon should be downloaded. + const bool download_largest_icon_; + + // The prioritized favicon candidates from the page back from the renderer. + std::vector<favicon::FaviconURL> image_urls_; + + // The FaviconRawBitmapResults from history. + std::vector<favicon_base::FaviconRawBitmapResult> history_results_; + + // The FaviconService which implements favicon operations. May be null during + // testing. + FaviconService* service_; + + // The client which implements embedder-specific Favicon operations. + FaviconClient* client_; // weak + + // This handler's driver. + FaviconDriver* driver_; // weak + + // Best image we've seen so far. As images are downloaded from the page they + // are stored here. When there is an exact match, or no more images are + // available the favicon service and the current page are updated (assuming + // the image is for a favicon). + FaviconCandidate best_favicon_candidate_; + + DISALLOW_COPY_AND_ASSIGN(FaviconHandler); +}; + +#endif // COMPONENTS_FAVICON_CORE_BROWSER_FAVICON_HANDLER_H_ diff --git a/components/favicon/core/browser/favicon_service.cc b/components/favicon/core/browser/favicon_service.cc new file mode 100644 index 0000000..f0173e4 --- /dev/null +++ b/components/favicon/core/browser/favicon_service.cc @@ -0,0 +1,391 @@ +// 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 "components/favicon/core/browser/favicon_service.h" + +#include <cmath> + +#include "base/hash.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "components/favicon/core/browser/favicon_client.h" +#include "components/favicon_base/favicon_util.h" +#include "components/favicon_base/select_favicon_frames.h" +#include "components/history/core/browser/history_service.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" +#include "url/gurl.h" + +using base::Bind; + +namespace { + +// 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) { + scoped_refptr<base::SingleThreadTaskRunner> thread_runner( + base::ThreadTaskRunnerHandle::Get()); + return tracker->PostTask( + thread_runner.get(), FROM_HERE, + Bind(callback, std::vector<favicon_base::FaviconRawBitmapResult>())); +} + +// 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(FaviconClient* favicon_client, + history::HistoryService* history_service) + : history_service_(history_service), favicon_client_(favicon_client) { +} + +FaviconService::~FaviconService() { +} + +// 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 (favicon_client_ && favicon_client_->IsNativeApplicationURL(page_url)) { + std::vector<int> desired_sizes_in_pixel; + desired_sizes_in_pixel.push_back(0); + return favicon_client_->GetFaviconForNativeApplicationURL( + 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 favicon_base::FaviconUsageDataList& 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<SkBitmap> bitmaps; + 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 scales. + if (std::find(favicon_scales.begin(), + favicon_scales.end(), + image_reps[i].scale()) == favicon_scales.end()) { + continue; + } + bitmaps.push_back(image_reps[i].sk_bitmap()); + } + history_service_->SetFavicons(page_url, icon_type, icon_url, bitmaps); +} + +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(); +} + +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 (favicon_client_ && favicon_client_->IsNativeApplicationURL(page_url)) { + return favicon_client_->GetFaviconForNativeApplicationURL( + 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); +} diff --git a/components/favicon/core/browser/favicon_service.h b/components/favicon/core/browser/favicon_service.h new file mode 100644 index 0000000..4149344 --- /dev/null +++ b/components/favicon/core/browser/favicon_service.h @@ -0,0 +1,254 @@ +// 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. + +#ifndef COMPONENTS_FAVICON_CORE_BROWSER_FAVICON_SERVICE_H_ +#define COMPONENTS_FAVICON_CORE_BROWSER_FAVICON_SERVICE_H_ + +#include <vector> + +#include "base/callback.h" +#include "base/containers/hash_tables.h" +#include "base/memory/ref_counted.h" +#include "base/task/cancelable_task_tracker.h" +#include "components/favicon_base/favicon_callback.h" +#include "components/favicon_base/favicon_types.h" +#include "components/favicon_base/favicon_usage_data.h" +#include "components/keyed_service/core/keyed_service.h" + +class FaviconClient; +class GURL; + +namespace history { +class HistoryService; +} + +// The favicon service provides methods to access favicons. It calls the history +// backend behind the scenes. The callbacks are run asynchronously, even in the +// case of an error. +class FaviconService : public KeyedService { + public: + // The FaviconClient must outlive the constructed FaviconService. + FaviconService(FaviconClient* favicon_client, + history::HistoryService* history_service); + + ~FaviconService() override; + + // We usually pass parameters with pointer to avoid copy. This function is a + // helper to run FaviconResultsCallback with pointer parameters. + static void FaviconResultsCallbackRunner( + const favicon_base::FaviconResultsCallback& callback, + const std::vector<favicon_base::FaviconRawBitmapResult>* results); + + ////////////////////////////////////////////////////////////////////////////// + // Methods to request favicon bitmaps from the history backend for |icon_url|. + // |icon_url| is the URL of the icon itself. + // (e.g. <http://www.google.com/favicon.ico>) + + // Requests the favicon at |icon_url| of type favicon_base::FAVICON and of + // size gfx::kFaviconSize. The returned gfx::Image is populated with + // representations for all of the scale factors supported by the platform + // (e.g. MacOS). If data is unavailable for some or all of the scale factors, + // the bitmaps with the best matching sizes are resized. + base::CancelableTaskTracker::TaskId GetFaviconImage( + const GURL& icon_url, + const favicon_base::FaviconImageCallback& callback, + base::CancelableTaskTracker* tracker); + + // Requests the favicon at |icon_url| of |icon_type| of size + // |desired_size_in_pixel|. If there is no favicon of size + // |desired_size_in_pixel|, the favicon bitmap which best matches + // |desired_size_in_pixel| is resized. If |desired_size_in_pixel| is 0, + // the largest favicon bitmap is returned. + base::CancelableTaskTracker::TaskId GetRawFavicon( + const GURL& icon_url, + favicon_base::IconType icon_type, + int desired_size_in_pixel, + const favicon_base::FaviconRawBitmapCallback& callback, + base::CancelableTaskTracker* tracker); + + // The first argument for |callback| is the set of bitmaps for the passed in + // URL and icon types whose pixel sizes best match the passed in + // |desired_size_in_dip| at the resource scale factors supported by the + // current platform (eg MacOS) in addition to 1x. The vector has at most one + // result for each of the resource scale factors. There are less entries if a + // single/ result is the best bitmap to use for several resource scale + // factors. + base::CancelableTaskTracker::TaskId GetFavicon( + const GURL& icon_url, + favicon_base::IconType icon_type, + int desired_size_in_dip, + const favicon_base::FaviconResultsCallback& callback, + base::CancelableTaskTracker* tracker); + + ////////////////////////////////////////////////////////////////////////////// + // Methods to request favicon bitmaps from the history backend for |page_url|. + // |page_url| is the web page the favicon is associated with. + // (e.g. <http://www.google.com>) + + // Requests the favicon for the page at |page_url| of type + // favicon_base::FAVICON and of size gfx::kFaviconSize. The returned + // gfx::Image is populated with representations for all of the scale factors + // supported by the platform (e.g. MacOS). If data is unavailable for some or + // all of the scale factors, the bitmaps with the best matching sizes are + // resized. + base::CancelableTaskTracker::TaskId GetFaviconImageForPageURL( + const GURL& page_url, + const favicon_base::FaviconImageCallback& callback, + base::CancelableTaskTracker* tracker); + + // Requests the favicon for the page at |page_url| with one of |icon_types| + // and with |desired_size_in_pixel|. |icon_types| can be any combination of + // IconTypes. If favicon bitmaps for several IconTypes are available, the + // favicon bitmap is chosen in the priority of TOUCH_PRECOMPOSED_ICON, + // TOUCH_ICON and FAVICON. If there is no favicon bitmap of size + // |desired_size_in_pixel|, the favicon bitmap which best matches + // |desired_size_in_pixel| is resized. If |desired_size_in_pixel| is 0, + // the largest favicon bitmap is returned. Results with a higher priority + // IconType are preferred over an exact match of the favicon bitmap size. + base::CancelableTaskTracker::TaskId GetRawFaviconForPageURL( + const GURL& page_url, + int icon_types, + int desired_size_in_pixel, + const favicon_base::FaviconRawBitmapCallback& callback, + base::CancelableTaskTracker* tracker); + + // See HistoryService::GetLargestFaviconForPageURL(). + 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); + + base::CancelableTaskTracker::TaskId GetFaviconForPageURL( + const GURL& page_url, + int icon_types, + int desired_size_in_dip, + const favicon_base::FaviconResultsCallback& callback, + base::CancelableTaskTracker* tracker); + + // Set the favicon mappings to |page_url| for |icon_types| in the history + // database. + // Sample |icon_urls|: + // { ICON_URL1 -> TOUCH_ICON, known to the database, + // ICON_URL2 -> TOUCH_ICON, not known to the database, + // ICON_URL3 -> TOUCH_PRECOMPOSED_ICON, known to the database } + // The new mappings are computed from |icon_urls| with these rules: + // 1) Any urls in |icon_urls| which are not already known to the database are + // rejected. + // Sample new mappings to |page_url|: { ICON_URL1, ICON_URL3 } + // 2) If |icon_types| has multiple types, the mappings are only set for the + // largest icon type. + // Sample new mappings to |page_url|: { ICON_URL3 } + // |icon_types| can only have multiple IconTypes if + // |icon_types| == TOUCH_ICON | TOUCH_PRECOMPOSED_ICON. + // The favicon bitmaps which most closely match |desired_size_in_dip| + // at the reosurce scale factors supported by the current platform (eg MacOS) + // in addition to 1x from the favicons which were just mapped to |page_url| + // are returned. If |desired_size_in_dip| is 0, the largest favicon bitmap is + // returned. + 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); + + // Used to request a bitmap for the favicon with |favicon_id| which is not + // resized from the size it is stored at in the database. If there are + // multiple favicon bitmaps for |favicon_id|, the largest favicon bitmap is + // returned. + base::CancelableTaskTracker::TaskId GetLargestRawFaviconForID( + favicon_base::FaviconID favicon_id, + const favicon_base::FaviconRawBitmapCallback& callback, + base::CancelableTaskTracker* tracker); + + // Marks all types of favicon for the page as being out of date. + void SetFaviconOutOfDateForPage(const GURL& page_url); + + // Clones all icons from an existing page. This associates the icons from + // |old_page_url| with |new_page_url|, provided |new_page_url| has no + // recorded associations to any other icons. + // Needed if you want to declare favicons (tentatively) in advance, before a + // page is ever visited. + void CloneFavicon(const GURL& old_page_url, const GURL& new_page_url); + + // Allows the importer to set many favicons for many pages at once. The pages + // must exist, any favicon sets for unknown pages will be discarded. Existing + // favicons will not be overwritten. + void SetImportedFavicons( + const favicon_base::FaviconUsageDataList& favicon_usage); + + // Set the favicon for |page_url| for |icon_type| in the thumbnail database. + // Unlike SetFavicons(), this method will not delete preexisting bitmap data + // which is associated to |page_url| if at all possible. Use this method if + // the favicon bitmaps for any of ui::GetSupportedScaleFactors() are not + // known. + void 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); + + // Set the favicon for |page_url| for |icon_type| in the thumbnail database. + // |icon_url| is the single favicon to map to |page_url|. Mappings from + // |page_url| to favicons at different icon URLs will be deleted. + // A favicon bitmap is added for each image rep in |image|. Any preexisting + // bitmap data for |icon_url| is deleted. It is important that |image| + // contains image reps for all of ui::GetSupportedScaleFactors(). Use + // MergeFavicon() if it does not. + // TODO(pkotwicz): Save unresized favicon bitmaps to the database. + // TODO(pkotwicz): Support adding favicons for multiple icon URLs to the + // thumbnail database. + void SetFavicons(const GURL& page_url, + const GURL& icon_url, + favicon_base::IconType icon_type, + const gfx::Image& image); + + // Avoid repeated requests to download missing favicon. + void UnableToDownloadFavicon(const GURL& icon_url); + bool WasUnableToDownloadFavicon(const GURL& icon_url) const; + void ClearUnableToDownloadFavicons(); + + private: + typedef uint32 MissingFaviconURLHash; + + // Helper function for GetFaviconImageForPageURL(), GetRawFaviconForPageURL() + // and GetFaviconForPageURL(). + base::CancelableTaskTracker::TaskId GetFaviconForPageURLImpl( + const GURL& page_url, + int icon_types, + const std::vector<int>& desired_sizes_in_pixel, + const favicon_base::FaviconResultsCallback& callback, + base::CancelableTaskTracker* tracker); + + // Intermediate callback for GetFaviconImage() and GetFaviconImageForPageURL() + // so that history service can deal solely with FaviconResultsCallback. + // Builds favicon_base::FaviconImageResult from |favicon_bitmap_results| and + // runs |callback|. + void RunFaviconImageCallbackWithBitmapResults( + const favicon_base::FaviconImageCallback& callback, + int desired_size_in_dip, + const std::vector<favicon_base::FaviconRawBitmapResult>& + favicon_bitmap_results); + + // Intermediate callback for GetRawFavicon() and GetRawFaviconForPageURL() + // so that history service can deal solely with FaviconResultsCallback. + // Resizes favicon_base::FaviconRawBitmapResult if necessary and runs + // |callback|. + void RunFaviconRawBitmapCallbackWithBitmapResults( + const favicon_base::FaviconRawBitmapCallback& callback, + int desired_size_in_pixel, + const std::vector<favicon_base::FaviconRawBitmapResult>& + favicon_bitmap_results); + + base::hash_set<MissingFaviconURLHash> missing_favicon_urls_; + history::HistoryService* history_service_; + FaviconClient* favicon_client_; + + DISALLOW_COPY_AND_ASSIGN(FaviconService); +}; + +#endif // COMPONENTS_FAVICON_CORE_BROWSER_FAVICON_SERVICE_H_ diff --git a/components/favicon/core/browser/favicon_tab_helper_observer.h b/components/favicon/core/browser/favicon_tab_helper_observer.h new file mode 100644 index 0000000..bc109ff --- /dev/null +++ b/components/favicon/core/browser/favicon_tab_helper_observer.h @@ -0,0 +1,24 @@ +// Copyright 2014 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. + +#ifndef COMPONENTS_FAVICON_CORE_BROWSER_FAVICON_TAB_HELPER_OBSERVER_H_ +#define COMPONENTS_FAVICON_CORE_BROWSER_FAVICON_TAB_HELPER_OBSERVER_H_ + +namespace gfx { +class Image; +} + +// An observer implemented by classes which are interested in envent in +// FaviconTabHelper. +class FaviconTabHelperObserver { + public: + // Called when favicon |image| is retrieved from either web site or history + // backend. + virtual void OnFaviconAvailable(const gfx::Image& image) = 0; + + protected: + virtual ~FaviconTabHelperObserver() {} +}; + +#endif // COMPONENTS_FAVICON_CORE_BROWSER_FAVICON_TAB_HELPER_OBSERVER_H_ |