Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Unified Diff: components/favicon/core/favicon_selector.cc

Issue 2739173002: Always select best favicon bitmap (Closed)
Patch Set: Rebased. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698