Index: components/favicon/core/favicon_selector.cc |
diff --git a/components/favicon/core/favicon_selector.cc b/components/favicon/core/favicon_selector.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..81c0b667864e31f4fb34441e844cd46214d33a16 |
--- /dev/null |
+++ b/components/favicon/core/favicon_selector.cc |
@@ -0,0 +1,244 @@ |
+// Copyright (c) 2017 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/favicon_selector.h" |
+ |
+#include <algorithm> |
+#include <functional> |
+#include <utility> |
+ |
+#include "components/favicon_base/favicon_util.h" |
+#include "components/favicon_base/select_favicon_frames.h" |
+#include "ui/gfx/favicon_size.h" |
+ |
+namespace favicon { |
+namespace { |
+ |
+// Size (along each axis) of a touch icon. |
+#if defined(OS_IOS) |
+// This currently corresponds to the apple touch icon for iPad. |
+const int kLargestIconSizeInPixels = 144; |
+#else |
+const int kLargestIconSizeInPixels = 192; |
+#endif |
+ |
+// Compare function used for std::stable_sort to sort as descend. |
+bool CompareScore(const FaviconSelector::Candidate& lhs, |
+ const FaviconSelector::Candidate& rhs) { |
+ return lhs.score > rhs.score; |
+} |
+ |
+bool ComparePixelSize(const FaviconSelector::TargetSizeSpec& lhs, |
+ const FaviconSelector::TargetSizeSpec& rhs) { |
+ return lhs.pixel_size() > rhs.pixel_size(); |
+} |
+ |
+// 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 |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(); |
+} |
+ |
+} // namespace |
+ |
+// static |
+FaviconSelector::TargetSizeSpec FaviconSelector::TargetSizeSpec::ForLargest() { |
+ FaviconSelector::TargetSizeSpec target_size_spec; |
+ target_size_spec.pixel_size_ = kLargestIconSizeInPixels; |
+ // 0 is special-cased in CreateFaviconImageSkiaWithScaleFactors(). |
+ target_size_spec.scale_factor_ = 0; |
+ target_size_spec.ensure_exact_size_ = false; |
+ return target_size_spec; |
+} |
+ |
+// static |
+std::vector<FaviconSelector::TargetSizeSpec> |
+FaviconSelector::TargetSizeSpec::For16x16Dips() { |
+ std::vector<FaviconSelector::TargetSizeSpec> target_size_specs; |
+ std::vector<float> scale_factors = favicon_base::GetFaviconScales(); |
+ std::sort(scale_factors.begin(), scale_factors.end(), std::greater<float>()); |
+ for (float scale : scale_factors) { |
+ FaviconSelector::TargetSizeSpec target_size_spec; |
+ target_size_spec.pixel_size_ = std::ceil(scale * gfx::kFaviconSize); |
+ target_size_spec.scale_factor_ = scale; |
+ target_size_spec.ensure_exact_size_ = true; |
+ target_size_specs.push_back(target_size_spec); |
+ } |
+ return target_size_specs; |
+} |
+ |
+FaviconSelector::TargetSizeSpec::TargetSizeSpec(const TargetSizeSpec&) = |
+ default; |
+ |
+FaviconSelector::TargetSizeSpec::~TargetSizeSpec() = default; |
+ |
+FaviconSelector::TargetSizeSpec::TargetSizeSpec(TargetSizeSpec&&) = default; |
+ |
+FaviconSelector::TargetSizeSpec& FaviconSelector::TargetSizeSpec::operator=( |
+ FaviconSelector::TargetSizeSpec&&) = default; |
+ |
+bool FaviconSelector::TargetSizeSpec::WantsBestBitmapOnly() const { |
+ return !ensure_exact_size_; |
+} |
+ |
+bool FaviconSelector::TargetSizeSpec::HasSatisfyingResult( |
+ const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) |
+ const { |
+ if (WantsBestBitmapOnly()) |
+ return HasValidResult(bitmap_results); |
+ |
+ const gfx::Size desired_size(pixel_size_, pixel_size_); |
+ for (const auto& bitmap_result : bitmap_results) { |
+ if (IsValid(bitmap_result) && bitmap_result.pixel_size == desired_size) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+std::list<FaviconSelector::Candidate> |
+FaviconSelector::TargetSizeSpec::SortAndPruneCandidates( |
+ const std::vector<favicon::FaviconURL>& favicon_urls) const { |
+ std::vector<Candidate> candidates; |
+ for (const favicon::FaviconURL& favicon_url : favicon_urls) |
+ candidates.push_back(Candidate::FromFaviconURL(favicon_url, pixel_size_)); |
+ |
+ std::stable_sort(candidates.begin(), candidates.end(), CompareScore); |
+ std::list<Candidate> result; |
+ std::move(candidates.begin(), candidates.end(), std::back_inserter(result)); |
+ return result; |
+} |
+ |
+FaviconSelector::TargetSizeSpec::TargetSizeSpec() = default; |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+// static |
+FaviconSelector::Candidate FaviconSelector::Candidate::FromFaviconURL( |
+ const favicon::FaviconURL& favicon_url, |
+ int target_pixel_size) { |
+ FaviconSelector::Candidate candidate; |
+ candidate.icon_url = favicon_url.icon_url; |
+ candidate.icon_type = favicon_url.icon_type; |
+ candidate.score = 0; |
+ for (const gfx::Size& favicon_size : favicon_url.icon_sizes) { |
pkotwicz
2017/03/20 03:25:16
If you passed in a vector of target sizes (std::ve
mastiz
2017/03/20 08:07:28
This deserves some discussion, because my proposal
|
+ candidate.score = |
+ std::max(candidate.score, |
+ GetFaviconCandidateScore(favicon_size, target_pixel_size)); |
+ } |
+ return candidate; |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+// static |
+std::vector<FaviconSelector> FaviconSelector::BuildMultiple( |
+ const std::vector<TargetSizeSpec>& target_size_specs, |
+ const std::vector<favicon::FaviconURL>& candidates) { |
+ DCHECK(std::is_sorted(target_size_specs.begin(), target_size_specs.end(), |
+ &ComparePixelSize)); |
+ std::vector<FaviconSelector> selectors; |
+ for (const TargetSizeSpec& target_size_spec : target_size_specs) |
+ selectors.emplace_back(target_size_spec, candidates); |
+ return selectors; |
+} |
+ |
+FaviconSelector::FaviconSelector( |
+ const FaviconSelector::TargetSizeSpec& target_size_spec, |
+ const std::vector<favicon::FaviconURL>& candidates) |
+ : target_size_spec_(target_size_spec), |
+ pending_candidates_(target_size_spec.SortAndPruneCandidates(candidates)) { |
+ DCHECK(!pending_candidates_.empty()); |
+} |
+ |
+FaviconSelector::~FaviconSelector() = default; |
+ |
+FaviconSelector::FaviconSelector(FaviconSelector&&) = default; |
+ |
+FaviconSelector& FaviconSelector::operator=(FaviconSelector&&) = default; |
+ |
+bool FaviconSelector::IsSatisfied() const { |
+ if (!best_candidate_) |
+ return false; |
+ |
+ if (target_size_spec_.WantsBestBitmapOnly()) { |
+ // The next candidate must have size attributes that are more promising than |
+ // the best candidate so far. Otherwise, all favicon without sizes attribute |
+ // are downloaded. |
+ // TODO(mastiz): This seems useful for sites that have reported the wrong |
+ // size. Consider simplifying and returning true here. |
+ return pending_candidates_.empty() || pending_candidates_.front().score <= |
+ best_candidate_->candidate.score; |
+ } else { |
+ // For exact sizes (i.e. not best only), we download all candidates until an |
+ // exact match is found. |
+ return best_candidate_->original_size.width() == |
+ target_size_spec_.pixel_size() && |
+ best_candidate_->original_size.height() == |
+ target_size_spec_.pixel_size(); |
+ } |
+} |
+ |
+// Returns the next candidate to be processed by populating |*candidate|. |
+// Returns false if the queue is empty. |
+base::Optional<FaviconSelector::Candidate> FaviconSelector::DequeueCandidate() { |
+ if (pending_candidates_.empty() || IsSatisfied()) |
+ return base::Optional<Candidate>(); |
+ |
+ base::Optional<Candidate> result = std::move(pending_candidates_.front()); |
+ pending_candidates_.pop_front(); |
+ return result; |
+} |
+ |
+const FaviconSelector::Candidate* FaviconSelector::CurrentCandidate() const { |
+ if (pending_candidates_.empty() || IsSatisfied()) |
+ return nullptr; |
+ |
+ return &pending_candidates_.front(); |
+} |
+ |
+bool FaviconSelector::ProcessDownloadedImage( |
+ const GURL& icon_url, |
+ favicon_base::IconType icon_type, |
+ const std::vector<SkBitmap>& bitmaps, |
+ const std::vector<gfx::Size>& original_sizes) { |
+ // Remove potentially queued candidates for |icon_url| (this happens if the |
+ // candidate was dequeued from another queue). |
+ pending_candidates_.remove_if([&icon_url](const Candidate& candidate) { |
+ return candidate.icon_url == icon_url; |
+ }); |
+ |
+ // Select the best bitmap, which to be relevant needs to be better than the |
+ // best known bitmap until now. |
+ float best_score = best_candidate_ ? best_candidate_->candidate.score : -1; |
+ size_t best_bitmap_index = bitmaps.size(); |
+ for (size_t i = 0; i < bitmaps.size(); ++i) { |
+ const SkBitmap& bitmap = bitmaps[i]; |
+ const float bitmap_score = |
+ GetFaviconCandidateScore(gfx::Size(bitmap.width(), bitmap.height()), |
+ target_size_spec_.pixel_size()); |
+ if (bitmap_score > best_score) { |
+ best_bitmap_index = i; |
+ best_score = bitmap_score; |
+ } |
+ } |
+ |
+ // No interesting bitmap, nothing to be done (|icon_url| has already been |
+ // removed from the queue). |
+ if (best_bitmap_index == bitmaps.size()) |
+ return false; |
+ |
+ Candidate candidate{icon_url, icon_type, best_score}; |
+ best_candidate_.emplace(BestCandidate{std::move(candidate), |
+ original_sizes[best_bitmap_index], |
+ bitmaps[best_bitmap_index]}); |
+ return true; |
+} |
+ |
+} // namespace favicon |