OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/favicon/core/favicon_handler.h" | 5 #include "components/favicon/core/favicon_handler.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
13 #include "base/memory/ref_counted_memory.h" | 13 #include "base/memory/ref_counted_memory.h" |
14 #include "build/build_config.h" | 14 #include "build/build_config.h" |
15 #include "components/favicon/core/favicon_service.h" | 15 #include "components/favicon/core/favicon_service.h" |
16 #include "components/favicon_base/favicon_util.h" | 16 #include "components/favicon_base/favicon_util.h" |
17 #include "components/favicon_base/select_favicon_frames.h" | 17 #include "components/favicon_base/select_favicon_frames.h" |
18 #include "skia/ext/image_operations.h" | 18 #include "skia/ext/image_operations.h" |
19 #include "ui/gfx/codec/png_codec.h" | 19 #include "ui/gfx/codec/png_codec.h" |
20 #include "ui/gfx/image/image_skia.h" | 20 #include "ui/gfx/image/image_skia.h" |
21 #include "ui/gfx/image/image_util.h" | 21 #include "ui/gfx/image/image_util.h" |
22 | 22 |
23 namespace favicon { | 23 namespace favicon { |
24 namespace { | 24 namespace { |
25 | 25 |
26 // Size (along each axis) of a touch icon. This currently corresponds to | 26 // Size (along each axis) of a touch icon. |
27 // the apple touch icon for iPad. | 27 #if defined(OS_IOS) |
28 const int kTouchIconSize = 144; | 28 // This currently corresponds to the apple touch icon for iPad. |
29 const int kLargestIconSizeInPixels = 144; | |
30 #else | |
31 const int kLargestIconSizeInPixels = 192; | |
32 #endif | |
29 | 33 |
30 bool DoUrlAndIconMatch(const FaviconURL& favicon_url, | 34 bool DoUrlAndIconMatch(const FaviconURL& favicon_url, |
31 const GURL& url, | 35 const GURL& url, |
32 favicon_base::IconType icon_type) { | 36 favicon_base::IconType icon_type) { |
33 return favicon_url.icon_url == url && favicon_url.icon_type == icon_type; | 37 return favicon_url.icon_url == url && favicon_url.icon_type == icon_type; |
34 } | 38 } |
35 | 39 |
36 // Returns true if all of the icon URLs and icon types in |bitmap_results| are | 40 // Returns true if all of the icon URLs and icon types in |bitmap_results| are |
37 // identical and if they match the icon URL and icon type in |favicon_url|. | 41 // identical and if they match the icon URL and icon type in |favicon_url|. |
38 // Returns false if |bitmap_results| is empty. | 42 // Returns false if |bitmap_results| is empty. |
(...skipping 17 matching lines...) Expand all Loading... | |
56 // Return true if |bitmap_result| is expired. | 60 // Return true if |bitmap_result| is expired. |
57 bool IsExpired(const favicon_base::FaviconRawBitmapResult& bitmap_result) { | 61 bool IsExpired(const favicon_base::FaviconRawBitmapResult& bitmap_result) { |
58 return bitmap_result.expired; | 62 return bitmap_result.expired; |
59 } | 63 } |
60 | 64 |
61 // Return true if |bitmap_result| is valid. | 65 // Return true if |bitmap_result| is valid. |
62 bool IsValid(const favicon_base::FaviconRawBitmapResult& bitmap_result) { | 66 bool IsValid(const favicon_base::FaviconRawBitmapResult& bitmap_result) { |
63 return bitmap_result.is_valid(); | 67 return bitmap_result.is_valid(); |
64 } | 68 } |
65 | 69 |
66 // Returns true if |bitmap_results| is non-empty and: | 70 // Returns true at least one of the bitmaps in |bitmap_results| is expired. |
67 // - At least one of the bitmaps in |bitmap_results| is expired | 71 bool HasExpiredResult( |
68 // OR | |
69 // - |bitmap_results| is missing favicons for |desired_size_in_dip| and one of | |
70 // the scale factors in favicon_base::GetFaviconScales(). | |
71 bool HasExpiredOrIncompleteResult( | |
72 int desired_size_in_dip, | |
73 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { | 72 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { |
74 if (bitmap_results.empty()) | 73 return std::find_if(bitmap_results.begin(), bitmap_results.end(), |
75 return false; | 74 IsExpired) != bitmap_results.end(); |
76 | |
77 // Check if at least one of the bitmaps is expired. | |
78 std::vector<favicon_base::FaviconRawBitmapResult>::const_iterator it = | |
79 std::find_if(bitmap_results.begin(), bitmap_results.end(), IsExpired); | |
80 if (it != bitmap_results.end()) | |
81 return true; | |
82 | |
83 // Any favicon size is good if the desired size is 0. | |
84 if (desired_size_in_dip == 0) | |
85 return false; | |
86 | |
87 // Check if the favicon for at least one of the scale factors is missing. | |
88 // |bitmap_results| should always be complete for data inserted by | |
89 // FaviconHandler as the FaviconHandler stores favicons resized to all | |
90 // of favicon_base::GetFaviconScales() into the history backend. | |
91 // Examples of when |bitmap_results| can be incomplete: | |
92 // - Favicons inserted into the history backend by sync. | |
93 // - Favicons for imported bookmarks. | |
94 std::vector<gfx::Size> favicon_sizes; | |
95 for (const auto& bitmap_result : bitmap_results) | |
96 favicon_sizes.push_back(bitmap_result.pixel_size); | |
97 | |
98 std::vector<float> favicon_scales = favicon_base::GetFaviconScales(); | |
99 for (float favicon_scale : favicon_scales) { | |
100 int edge_size_in_pixel = std::ceil(desired_size_in_dip * favicon_scale); | |
101 auto it = std::find(favicon_sizes.begin(), favicon_sizes.end(), | |
102 gfx::Size(edge_size_in_pixel, edge_size_in_pixel)); | |
103 if (it == favicon_sizes.end()) | |
104 return true; | |
105 } | |
106 return false; | |
107 } | 75 } |
108 | 76 |
109 // Returns true if at least one of |bitmap_results| is valid. | 77 // Returns true if at least one of |bitmap_results| is valid. |
110 bool HasValidResult( | 78 bool HasValidResult( |
111 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { | 79 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { |
112 return std::find_if(bitmap_results.begin(), bitmap_results.end(), IsValid) != | 80 return std::find_if(bitmap_results.begin(), bitmap_results.end(), IsValid) != |
113 bitmap_results.end(); | 81 bitmap_results.end(); |
114 } | 82 } |
115 | 83 |
116 // Returns the index of the entry with the largest area. | 84 // Returns the index of the entry with the best size match. |
117 int GetLargestSizeIndex(const std::vector<gfx::Size>& sizes) { | 85 int GetBestMatchSizeIndex(const FaviconHandler::TargetSpec& target_size, |
86 const std::vector<gfx::Size>& sizes) { | |
118 DCHECK(!sizes.empty()); | 87 DCHECK(!sizes.empty()); |
119 size_t ret = 0; | 88 size_t ret = 0; |
120 for (size_t i = 1; i < sizes.size(); ++i) { | 89 for (size_t i = 1; i < sizes.size(); ++i) { |
121 if (sizes[ret].GetArea() < sizes[i].GetArea()) | 90 if (target_size.IsBetterMatch(sizes[i], sizes[ret])) |
122 ret = i; | 91 ret = i; |
123 } | 92 } |
124 return static_cast<int>(ret); | 93 return static_cast<int>(ret); |
125 } | 94 } |
126 | 95 |
127 // Return the index of a size which is same as the given |size|, -1 returned if | 96 // Compare function used for std::stable_sort to sort as descend. |
128 // there is no such bitmap. | 97 bool CompareScore(const std::pair<float, FaviconURL>& b1, |
129 int GetIndexBySize(const std::vector<gfx::Size>& sizes, | 98 const std::pair<float, FaviconURL>& b2) { |
130 const gfx::Size& size) { | 99 return b1.first > b2.first; |
131 DCHECK(!sizes.empty()); | |
132 std::vector<gfx::Size>::const_iterator i = | |
133 std::find(sizes.begin(), sizes.end(), size); | |
134 if (i == sizes.end()) | |
135 return -1; | |
136 | |
137 return static_cast<int>(i - sizes.begin()); | |
138 } | 100 } |
139 | 101 |
140 // Compare function used for std::stable_sort to sort as descend. | 102 // Sorts the entries in |image_urls|, best icon size matches first for |
141 bool CompareIconSize(const FaviconURL& b1, const FaviconURL& b2) { | 103 // |target_size|. |
142 int area1 = 0; | 104 void SortImageUrls(const FaviconHandler::TargetSpec& target_size, |
143 if (!b1.icon_sizes.empty()) | 105 std::vector<FaviconURL>* image_urls) { |
144 area1 = b1.icon_sizes.front().GetArea(); | 106 std::vector<std::pair<float, FaviconURL>> scored_candidates; |
145 | 107 for (const FaviconURL& image_url : *image_urls) { |
146 int area2 = 0; | 108 float max_score = 0; |
147 if (!b2.icon_sizes.empty()) | 109 for (const gfx::Size& size : image_url.icon_sizes) { |
148 area2 = b2.icon_sizes.front().GetArea(); | 110 max_score = std::max( |
149 | 111 max_score, |
150 return area1 > area2; | 112 GetFaviconCandidateScore(size, target_size.GetMaxPixelSize())); |
pkotwicz
2017/03/15 03:49:39
You should use SelectFaviconFrameIndices() to comp
mastiz
2017/03/15 16:34:33
I believe this is a moot point now, because of the
| |
151 } | 113 } |
152 | 114 scored_candidates.emplace_back(max_score, std::move(image_url)); |
153 // Sorts the entries in |image_urls| by icon size in descending order. | |
154 // Discards all but the largest size for each FaviconURL. | |
155 void SortAndPruneImageUrls(std::vector<FaviconURL>* image_urls) { | |
156 // Not using const-reference since the loop mutates FaviconURL::icon_sizes. | |
157 for (FaviconURL& image_url : *image_urls) { | |
158 if (image_url.icon_sizes.empty()) | |
159 continue; | |
160 | |
161 gfx::Size largest = | |
162 image_url.icon_sizes[GetLargestSizeIndex(image_url.icon_sizes)]; | |
163 image_url.icon_sizes.clear(); | |
164 image_url.icon_sizes.push_back(largest); | |
165 } | 115 } |
166 std::stable_sort(image_urls->begin(), image_urls->end(), CompareIconSize); | 116 std::stable_sort(scored_candidates.begin(), scored_candidates.end(), |
117 CompareScore); | |
118 image_urls->clear(); | |
119 for (auto& scored_candidate : scored_candidates) | |
120 image_urls->push_back(std::move(scored_candidate.second)); | |
167 } | 121 } |
168 | 122 |
169 // Checks whether two FaviconURLs are equal ignoring the icon sizes. | 123 // Checks whether two FaviconURLs are equal ignoring the icon sizes. |
170 bool FaviconURLsEqualIgnoringSizes(const FaviconURL& u1, const FaviconURL& u2) { | 124 bool FaviconURLsEqualIgnoringSizes(const FaviconURL& u1, const FaviconURL& u2) { |
171 return u1.icon_type == u2.icon_type && u1.icon_url == u2.icon_url; | 125 return u1.icon_type == u2.icon_type && u1.icon_url == u2.icon_url; |
172 } | 126 } |
173 | 127 |
128 FaviconHandler::TargetSpec GetTargetSpec( | |
129 FaviconDriverObserver::NotificationIconType handler_type) { | |
130 switch (handler_type) { | |
131 case FaviconDriverObserver::NON_TOUCH_16_DIP: | |
132 return FaviconHandler::TargetSpec::For16x16Dips(); | |
133 case FaviconDriverObserver::NON_TOUCH_LARGEST: | |
134 case FaviconDriverObserver::TOUCH_LARGEST: | |
135 return FaviconHandler::TargetSpec::ForLargest(); | |
136 } | |
137 NOTREACHED(); | |
138 } | |
139 | |
174 } // namespace | 140 } // namespace |
175 | 141 |
176 //////////////////////////////////////////////////////////////////////////////// | 142 //////////////////////////////////////////////////////////////////////////////// |
177 | 143 |
144 // static | |
145 FaviconHandler::TargetSpec FaviconHandler::TargetSpec::ForLargest() { | |
146 FaviconHandler::TargetSpec target_size; | |
147 target_size.pixel_sizes_.insert({kLargestIconSizeInPixels, 1.0f}); | |
148 target_size.ensure_exact_size_ = false; | |
149 return target_size; | |
150 } | |
151 | |
152 // static | |
153 FaviconHandler::TargetSpec FaviconHandler::TargetSpec::For16x16Dips() { | |
154 FaviconHandler::TargetSpec target_size; | |
155 for (float scale : favicon_base::GetFaviconScales()) { | |
156 target_size.pixel_sizes_.insert( | |
157 {std::ceil(scale * gfx::kFaviconSize), scale}); | |
158 } | |
159 target_size.ensure_exact_size_ = true; | |
160 return target_size; | |
161 } | |
162 | |
163 FaviconHandler::TargetSpec::TargetSpec(const TargetSpec&) = default; | |
164 | |
165 FaviconHandler::TargetSpec::~TargetSpec() = default; | |
166 | |
167 std::vector<int> FaviconHandler::TargetSpec::GetPixelSizes() const { | |
168 std::vector<int> result; | |
169 for (const auto& pixel_size : pixel_sizes_) | |
170 result.push_back(pixel_size.first); | |
171 return result; | |
172 } | |
173 | |
174 int FaviconHandler::TargetSpec::GetMaxPixelSize() const { | |
175 return pixel_sizes_.rbegin()->first; | |
176 } | |
177 | |
178 bool FaviconHandler::TargetSpec::WantsBestBitmapOnly() const { | |
179 return !ensure_exact_size_; | |
180 } | |
181 | |
182 bool FaviconHandler::TargetSpec::HasCompleteResult( | |
183 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) | |
184 const { | |
185 if (WantsBestBitmapOnly()) | |
186 return HasValidResult(bitmap_results); | |
187 | |
188 // Check if the favicon for at least one of the scale factors is missing. | |
189 // |bitmap_results| should always be complete for data inserted by | |
190 // FaviconHandler as the FaviconHandler stores favicons resized to all | |
191 // of |favicon_scales| into the history backend. | |
192 // Examples of when |bitmap_results| can be incomplete: | |
193 // - Favicons inserted into the history backend by sync. | |
194 // - Favicons for imported bookmarks. | |
195 std::vector<gfx::Size> favicon_sizes; | |
196 for (const auto& bitmap_result : bitmap_results) { | |
197 if (IsValid(bitmap_result)) | |
198 favicon_sizes.push_back(bitmap_result.pixel_size); | |
199 } | |
200 | |
201 for (const auto& pixel_size : pixel_sizes_) { | |
202 const int edge_size_in_pixel = pixel_size.first; | |
203 auto it = std::find(favicon_sizes.begin(), favicon_sizes.end(), | |
204 gfx::Size(edge_size_in_pixel, edge_size_in_pixel)); | |
205 if (it == favicon_sizes.end()) | |
206 return false; | |
207 } | |
208 return true; | |
209 } | |
210 | |
211 bool FaviconHandler::TargetSpec::IsBetterMatch(const gfx::Size& size1, | |
212 const gfx::Size& size2) const { | |
213 return GetFaviconCandidateScore(size1, GetMaxPixelSize()) > | |
214 GetFaviconCandidateScore(size2, GetMaxPixelSize()); | |
215 } | |
216 | |
217 gfx::ImageSkia FaviconHandler::TargetSpec::CreateImageSkia( | |
218 const std::vector<SkBitmap>& bitmaps, | |
219 const std::vector<gfx::Size>& original_bitmap_sizes) const { | |
220 if (WantsBestBitmapOnly()) { | |
221 return gfx::ImageSkia(gfx::ImageSkiaRep( | |
222 bitmaps[GetBestMatchSizeIndex(*this, original_bitmap_sizes)], 1)); | |
223 } else { | |
224 return CreateFaviconImageSkia(bitmaps, original_bitmap_sizes, | |
225 gfx::kFaviconSize, /*score=*/nullptr); | |
226 } | |
227 } | |
228 | |
229 gfx::Image FaviconHandler::TargetSpec::CreateImageFromCache( | |
230 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) | |
231 const { | |
232 return favicon_base::SelectFaviconFramesForPixelSizesFromPNGs(bitmap_results, | |
233 pixel_sizes_); | |
234 } | |
235 | |
236 FaviconHandler::TargetSpec::TargetSpec() = default; | |
237 | |
238 //////////////////////////////////////////////////////////////////////////////// | |
239 | |
178 FaviconHandler::DownloadRequest::DownloadRequest() | 240 FaviconHandler::DownloadRequest::DownloadRequest() |
179 : icon_type(favicon_base::INVALID_ICON) { | 241 : icon_type(favicon_base::INVALID_ICON) { |
180 } | 242 } |
181 | 243 |
182 FaviconHandler::DownloadRequest::~DownloadRequest() { | 244 FaviconHandler::DownloadRequest::~DownloadRequest() { |
183 } | 245 } |
184 | 246 |
185 FaviconHandler::DownloadRequest::DownloadRequest( | 247 FaviconHandler::DownloadRequest::DownloadRequest( |
186 const GURL& image_url, | 248 const GURL& image_url, |
187 favicon_base::IconType icon_type) | 249 favicon_base::IconType icon_type) |
188 : image_url(image_url), icon_type(icon_type) { | 250 : image_url(image_url), icon_type(icon_type) { |
189 } | 251 } |
190 | 252 |
191 //////////////////////////////////////////////////////////////////////////////// | 253 //////////////////////////////////////////////////////////////////////////////// |
192 | 254 |
193 FaviconHandler::FaviconCandidate::FaviconCandidate() | 255 FaviconHandler::FaviconCandidate::FaviconCandidate() |
194 : score(0), icon_type(favicon_base::INVALID_ICON) { | 256 : icon_type(favicon_base::INVALID_ICON) {} |
195 } | |
196 | 257 |
197 FaviconHandler::FaviconCandidate::~FaviconCandidate() { | 258 FaviconHandler::FaviconCandidate::~FaviconCandidate() { |
198 } | 259 } |
199 | 260 |
200 FaviconHandler::FaviconCandidate::FaviconCandidate( | 261 FaviconHandler::FaviconCandidate::FaviconCandidate( |
201 const GURL& image_url, | 262 const GURL& image_url, |
202 const gfx::Image& image, | 263 const gfx::Image& image, |
203 float score, | |
204 favicon_base::IconType icon_type) | 264 favicon_base::IconType icon_type) |
205 : image_url(image_url), | 265 : image_url(image_url), |
206 image(image), | 266 image(image), |
207 score(score), | |
208 icon_type(icon_type) {} | 267 icon_type(icon_type) {} |
209 | 268 |
210 //////////////////////////////////////////////////////////////////////////////// | 269 //////////////////////////////////////////////////////////////////////////////// |
211 | 270 |
212 FaviconHandler::FaviconHandler( | 271 FaviconHandler::FaviconHandler( |
213 FaviconService* service, | 272 FaviconService* service, |
214 Delegate* delegate, | 273 Delegate* delegate, |
215 FaviconDriverObserver::NotificationIconType handler_type) | 274 FaviconDriverObserver::NotificationIconType handler_type) |
216 : handler_type_(handler_type), | 275 : handler_type_(handler_type), |
217 got_favicon_from_history_(false), | 276 got_favicon_from_history_(false), |
218 initial_history_result_expired_or_incomplete_(false), | 277 initial_history_result_expired_or_incomplete_(false), |
219 redownload_icons_(false), | 278 redownload_icons_(false), |
220 icon_types_(FaviconHandler::GetIconTypesFromHandlerType(handler_type)), | 279 icon_types_(FaviconHandler::GetIconTypesFromHandlerType(handler_type)), |
221 download_largest_icon_( | 280 target_spec_(GetTargetSpec(handler_type)), |
222 handler_type == FaviconDriverObserver::NON_TOUCH_LARGEST || | |
223 handler_type == FaviconDriverObserver::TOUCH_LARGEST), | |
224 notification_icon_type_(favicon_base::INVALID_ICON), | 281 notification_icon_type_(favicon_base::INVALID_ICON), |
225 service_(service), | 282 service_(service), |
226 delegate_(delegate), | 283 delegate_(delegate), |
227 current_candidate_index_(0u), | 284 current_candidate_index_(0u), |
228 weak_ptr_factory_(this) { | 285 weak_ptr_factory_(this) { |
229 DCHECK(delegate_); | 286 DCHECK(delegate_); |
230 } | 287 } |
231 | 288 |
232 FaviconHandler::~FaviconHandler() { | 289 FaviconHandler::~FaviconHandler() { |
233 } | 290 } |
(...skipping 24 matching lines...) Expand all Loading... | |
258 notification_icon_url_ = GURL(); | 315 notification_icon_url_ = GURL(); |
259 notification_icon_type_ = favicon_base::INVALID_ICON; | 316 notification_icon_type_ = favicon_base::INVALID_ICON; |
260 current_candidate_index_ = 0u; | 317 current_candidate_index_ = 0u; |
261 best_favicon_candidate_ = FaviconCandidate(); | 318 best_favicon_candidate_ = FaviconCandidate(); |
262 | 319 |
263 // Request the favicon from the history service. In parallel to this the | 320 // Request the favicon from the history service. In parallel to this the |
264 // renderer is going to notify us (well WebContents) when the favicon url is | 321 // renderer is going to notify us (well WebContents) when the favicon url is |
265 // available. | 322 // available. |
266 if (service_) { | 323 if (service_) { |
267 service_->GetFaviconForPageURL( | 324 service_->GetFaviconForPageURL( |
268 url_, icon_types_, preferred_icon_size(), | 325 url_, icon_types_, target_spec_.GetPixelSizes(), |
269 base::Bind( | 326 base::Bind( |
270 &FaviconHandler::OnFaviconDataForInitialURLFromFaviconService, | 327 &FaviconHandler::OnFaviconDataForInitialURLFromFaviconService, |
271 base::Unretained(this)), | 328 base::Unretained(this)), |
272 &cancelable_task_tracker_); | 329 &cancelable_task_tracker_); |
273 } | 330 } |
274 } | 331 } |
275 | 332 |
276 bool FaviconHandler::UpdateFaviconCandidate(const GURL& image_url, | 333 bool FaviconHandler::UpdateFaviconCandidate(const GURL& image_url, |
277 const gfx::Image& image, | 334 const gfx::Image& image, |
278 float score, | |
279 favicon_base::IconType icon_type) { | 335 favicon_base::IconType icon_type) { |
280 bool replace_best_favicon_candidate = false; | 336 // The size of the downloaded icon may not match the declared size so compare |
281 bool exact_match = false; | 337 // before overriding |best_favicon_candidate_|. |
282 if (download_largest_icon_) { | 338 if (target_spec_.IsBetterMatch(image.Size(), |
283 replace_best_favicon_candidate = | 339 best_favicon_candidate_.image.Size())) { |
284 image.Size().GetArea() > | 340 best_favicon_candidate_ = FaviconCandidate(image_url, image, icon_type); |
285 best_favicon_candidate_.image.Size().GetArea(); | 341 } |
286 | 342 |
287 gfx::Size largest = best_favicon_candidate_.image.Size(); | 343 // Stop downloading if current candidate is only candidate. |
288 if (replace_best_favicon_candidate) | 344 if (current_candidate_index_ + 1 >= image_urls_.size()) { |
289 largest = image.Size(); | 345 return true; |
346 } | |
347 const FaviconURL& next_image_url = image_urls_[current_candidate_index_ + 1]; | |
290 | 348 |
291 // The size of the downloaded icon may not match the declared size. Stop | 349 // For exact sizes (i.e. not best only), we download all candidates until an |
292 // downloading if: | 350 // exact match is found. |
293 // - current candidate is only candidate. | 351 if (!target_spec_.WantsBestBitmapOnly()) { |
294 // - next candidate doesn't have sizes attributes, in this case, the rest | 352 const int desired_max_size = target_spec_.GetMaxPixelSize(); |
295 // candidates don't have sizes attribute either, stop downloading now, | 353 return best_favicon_candidate_.image.Size() == |
296 // otherwise, all favicon without sizes attribute are downloaded. | 354 gfx::Size(desired_max_size, desired_max_size); |
297 // - next candidate has sizes attribute and it is not larger than largest, | |
298 // - current candidate is maximal one we want. | |
299 const int maximal_size = GetMaximalIconSize(icon_type); | |
300 if (current_candidate_index_ + 1 >= image_urls_.size()) { | |
301 exact_match = true; | |
302 } else { | |
303 FaviconURL next_image_url = image_urls_[current_candidate_index_ + 1]; | |
304 exact_match = next_image_url.icon_sizes.empty() || | |
305 next_image_url.icon_sizes[0].GetArea() <= largest.GetArea() || | |
306 (image.Size().width() == maximal_size && | |
307 image.Size().height() == maximal_size); | |
308 } | |
309 } else { | |
310 exact_match = score == 1 || preferred_icon_size() == 0; | |
311 replace_best_favicon_candidate = | |
312 exact_match || | |
313 best_favicon_candidate_.icon_type == favicon_base::INVALID_ICON || | |
314 score > best_favicon_candidate_.score; | |
315 } | 355 } |
316 if (replace_best_favicon_candidate) { | 356 |
317 best_favicon_candidate_ = | 357 // The size of the downloaded icon may not match the declared size. Stop |
318 FaviconCandidate(image_url, image, score, icon_type); | 358 // downloading if: |
319 } | 359 // - next candidate doesn't have sizes attributes, in this case, the rest |
320 return exact_match; | 360 // candidates don't have sizes attribute either, stop downloading now, |
361 // otherwise, all favicon without sizes attribute are downloaded. | |
362 // - next candidate has sizes attribute and it is not better than best, | |
363 if (next_image_url.icon_sizes.empty()) | |
364 return true; | |
365 | |
366 const gfx::Size& best_next_size = | |
367 next_image_url.icon_sizes[ | |
368 GetBestMatchSizeIndex(target_spec_, next_image_url.icon_sizes)]; | |
369 return !target_spec_.IsBetterMatch(best_next_size, | |
370 best_favicon_candidate_.image.Size()); | |
321 } | 371 } |
322 | 372 |
323 void FaviconHandler::SetFavicon(const GURL& icon_url, | 373 void FaviconHandler::SetFavicon(const GURL& icon_url, |
324 const gfx::Image& image, | 374 const gfx::Image& image, |
325 favicon_base::IconType icon_type) { | 375 favicon_base::IconType icon_type) { |
pkotwicz
2017/03/15 03:49:39
In order to future proof, we should make FaviconHa
| |
326 if (service_ && ShouldSaveFavicon()) | 376 if (service_ && ShouldSaveFavicon()) |
327 service_->SetFavicons(url_, icon_url, icon_type, image); | 377 service_->SetFavicons(url_, icon_url, icon_type, image); |
328 | 378 |
329 NotifyFaviconUpdated(icon_url, icon_type, image); | 379 NotifyFaviconUpdated(icon_url, icon_type, image); |
330 } | 380 } |
331 | 381 |
332 void FaviconHandler::NotifyFaviconUpdated( | 382 void FaviconHandler::NotifyFaviconUpdated( |
333 const std::vector<favicon_base::FaviconRawBitmapResult>& | 383 const std::vector<favicon_base::FaviconRawBitmapResult>& |
334 favicon_bitmap_results) { | 384 favicon_bitmap_results) { |
335 if (favicon_bitmap_results.empty()) | 385 if (favicon_bitmap_results.empty()) |
336 return; | 386 return; |
337 | 387 |
338 gfx::Image resized_image = favicon_base::SelectFaviconFramesFromPNGs( | 388 gfx::Image resized_image = |
339 favicon_bitmap_results, | 389 target_spec_.CreateImageFromCache(favicon_bitmap_results); |
340 favicon_base::GetFaviconScales(), | 390 |
341 preferred_icon_size()); | |
342 // The history service sends back results for a single icon URL and icon | 391 // The history service sends back results for a single icon URL and icon |
343 // type, so it does not matter which result we get |icon_url| and |icon_type| | 392 // type, so it does not matter which result we get |icon_url| and |icon_type| |
344 // from. | 393 // from. |
345 const GURL icon_url = favicon_bitmap_results[0].icon_url; | 394 const GURL icon_url = favicon_bitmap_results[0].icon_url; |
346 favicon_base::IconType icon_type = favicon_bitmap_results[0].icon_type; | 395 favicon_base::IconType icon_type = favicon_bitmap_results[0].icon_type; |
347 NotifyFaviconUpdated(icon_url, icon_type, resized_image); | 396 NotifyFaviconUpdated(icon_url, icon_type, resized_image); |
348 } | 397 } |
349 | 398 |
350 void FaviconHandler::NotifyFaviconUpdated(const GURL& icon_url, | 399 void FaviconHandler::NotifyFaviconUpdated(const GURL& icon_url, |
351 favicon_base::IconType icon_type, | 400 favicon_base::IconType icon_type, |
(...skipping 17 matching lines...) Expand all Loading... | |
369 const std::vector<FaviconURL>& candidates) { | 418 const std::vector<FaviconURL>& candidates) { |
370 if (page_url != url_) | 419 if (page_url != url_) |
371 return; | 420 return; |
372 | 421 |
373 std::vector<FaviconURL> pruned_candidates; | 422 std::vector<FaviconURL> pruned_candidates; |
374 for (const FaviconURL& candidate : candidates) { | 423 for (const FaviconURL& candidate : candidates) { |
375 if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) | 424 if (!candidate.icon_url.is_empty() && (candidate.icon_type & icon_types_)) |
376 pruned_candidates.push_back(candidate); | 425 pruned_candidates.push_back(candidate); |
377 } | 426 } |
378 | 427 |
379 if (download_largest_icon_) | 428 SortImageUrls(target_spec_, &pruned_candidates); |
380 SortAndPruneImageUrls(&pruned_candidates); | |
381 | 429 |
382 // Ignore FaviconURL::icon_sizes because FaviconURL::icon_sizes is not stored | 430 // Ignore FaviconURL::icon_sizes because FaviconURL::icon_sizes is not stored |
383 // in the history database. | 431 // in the history database. |
384 if (image_urls_.size() == pruned_candidates.size() && | 432 if (image_urls_.size() == pruned_candidates.size() && |
385 std::equal(pruned_candidates.begin(), pruned_candidates.end(), | 433 std::equal(pruned_candidates.begin(), pruned_candidates.end(), |
386 image_urls_.begin(), FaviconURLsEqualIgnoringSizes)) { | 434 image_urls_.begin(), FaviconURLsEqualIgnoringSizes)) { |
387 return; | 435 return; |
388 } | 436 } |
389 | 437 |
390 download_requests_.clear(); | 438 download_requests_.clear(); |
391 image_urls_ = pruned_candidates; | 439 image_urls_ = pruned_candidates; |
392 current_candidate_index_ = 0u; | 440 current_candidate_index_ = 0u; |
393 best_favicon_candidate_ = FaviconCandidate(); | 441 best_favicon_candidate_ = FaviconCandidate(); |
394 | 442 |
395 // TODO(davemoore) Should clear on empty url. Currently we ignore it. | 443 // TODO(davemoore) Should clear on empty url. Currently we ignore it. |
396 // This appears to be what FF does as well. | 444 // This appears to be what FF does as well. |
397 if (current_candidate() && got_favicon_from_history_) | 445 if (current_candidate() && got_favicon_from_history_) |
398 OnGotInitialHistoryDataAndIconURLCandidates(); | 446 OnGotInitialHistoryDataAndIconURLCandidates(); |
399 } | 447 } |
400 | 448 |
401 int FaviconHandler::GetMaximalIconSize(favicon_base::IconType icon_type) { | |
402 switch (icon_type) { | |
403 case favicon_base::FAVICON: | |
404 #if defined(OS_ANDROID) | |
405 return 192; | |
406 #else | |
407 return gfx::ImageSkia::GetMaxSupportedScale() * gfx::kFaviconSize; | |
408 #endif | |
409 case favicon_base::TOUCH_ICON: | |
410 case favicon_base::TOUCH_PRECOMPOSED_ICON: | |
411 return kTouchIconSize; | |
412 case favicon_base::INVALID_ICON: | |
413 return 0; | |
414 } | |
415 NOTREACHED(); | |
416 return 0; | |
417 } | |
418 | |
419 void FaviconHandler::OnGotInitialHistoryDataAndIconURLCandidates() { | 449 void FaviconHandler::OnGotInitialHistoryDataAndIconURLCandidates() { |
420 if (!initial_history_result_expired_or_incomplete_ && | 450 if (!initial_history_result_expired_or_incomplete_ && |
421 DoUrlAndIconMatch(*current_candidate(), notification_icon_url_, | 451 DoUrlAndIconMatch(*current_candidate(), notification_icon_url_, |
422 notification_icon_type_)) { | 452 notification_icon_type_)) { |
423 // - The data from history is valid and not expired. | 453 // - The data from history is valid and not expired. |
424 // - The icon URL of the history data matches one of the page's icon URLs. | 454 // - The icon URL of the history data matches one of the page's icon URLs. |
425 // - The icon URL of the history data matches the icon URL of the last | 455 // - The icon URL of the history data matches the icon URL of the last |
426 // OnFaviconAvailable() notification. | 456 // OnFaviconAvailable() notification. |
427 // We are done. No additional downloads or history requests are needed. | 457 // We are done. No additional downloads or history requests are needed. |
428 // TODO: Store all of the icon URLs associated with a page in history so | 458 // TODO: Store all of the icon URLs associated with a page in history so |
(...skipping 29 matching lines...) Expand all Loading... | |
458 | 488 |
459 if (!current_candidate() || | 489 if (!current_candidate() || |
460 !DoUrlAndIconMatch(*current_candidate(), | 490 !DoUrlAndIconMatch(*current_candidate(), |
461 image_url, | 491 image_url, |
462 download_request.icon_type)) { | 492 download_request.icon_type)) { |
463 return; | 493 return; |
464 } | 494 } |
465 | 495 |
466 bool request_next_icon = true; | 496 bool request_next_icon = true; |
467 if (!bitmaps.empty()) { | 497 if (!bitmaps.empty()) { |
468 float score = 0.0f; | 498 gfx::ImageSkia image_skia = |
469 gfx::ImageSkia image_skia; | 499 target_spec_.CreateImageSkia(bitmaps, original_bitmap_sizes); |
470 if (download_largest_icon_) { | |
471 int index = -1; | |
472 // Use the largest bitmap if FaviconURL doesn't have sizes attribute. | |
473 if (current_candidate()->icon_sizes.empty()) { | |
474 index = GetLargestSizeIndex(original_bitmap_sizes); | |
475 } else { | |
476 index = GetIndexBySize(original_bitmap_sizes, | |
477 current_candidate()->icon_sizes[0]); | |
478 // Find largest bitmap if there is no one exactly matched. | |
479 if (index == -1) | |
480 index = GetLargestSizeIndex(original_bitmap_sizes); | |
481 } | |
482 image_skia = gfx::ImageSkia(gfx::ImageSkiaRep(bitmaps[index], 1)); | |
483 } else { | |
484 image_skia = CreateFaviconImageSkia(bitmaps, | |
485 original_bitmap_sizes, | |
486 preferred_icon_size(), | |
487 &score); | |
488 } | |
489 | 500 |
490 if (!image_skia.isNull()) { | 501 if (!image_skia.isNull()) { |
491 gfx::Image image(image_skia); | 502 gfx::Image image(image_skia); |
492 // The downloaded icon is still valid when there is no FaviconURL update | 503 // The downloaded icon is still valid when there is no FaviconURL update |
493 // during the downloading. | 504 // during the downloading. |
494 request_next_icon = !UpdateFaviconCandidate(image_url, image, score, | 505 request_next_icon = |
495 download_request.icon_type); | 506 !UpdateFaviconCandidate(image_url, image, download_request.icon_type); |
pkotwicz
2017/03/15 03:49:39
UpdateFaviconCandidate() should be fed |bitmaps| a
| |
496 } | 507 } |
497 } | 508 } |
498 | 509 |
499 if (request_next_icon && current_candidate_index_ + 1 < image_urls_.size()) { | 510 if (request_next_icon && current_candidate_index_ + 1 < image_urls_.size()) { |
500 // Process the next candidate. | 511 // Process the next candidate. |
501 ++current_candidate_index_; | 512 ++current_candidate_index_; |
502 DownloadCurrentCandidateOrAskFaviconService(); | 513 DownloadCurrentCandidateOrAskFaviconService(); |
503 } else { | 514 } else { |
504 // We have either found the ideal candidate or run out of candidates. | 515 // We have either found the ideal candidate or run out of candidates. |
505 if (best_favicon_candidate_.icon_type != favicon_base::INVALID_ICON) { | 516 if (best_favicon_candidate_.icon_type != favicon_base::INVALID_ICON) { |
(...skipping 21 matching lines...) Expand all Loading... | |
527 // Always save favicon if the page is bookmarked. | 538 // Always save favicon if the page is bookmarked. |
528 return delegate_->IsBookmarked(url_); | 539 return delegate_->IsBookmarked(url_); |
529 } | 540 } |
530 | 541 |
531 void FaviconHandler::OnFaviconDataForInitialURLFromFaviconService( | 542 void FaviconHandler::OnFaviconDataForInitialURLFromFaviconService( |
532 const std::vector<favicon_base::FaviconRawBitmapResult>& | 543 const std::vector<favicon_base::FaviconRawBitmapResult>& |
533 favicon_bitmap_results) { | 544 favicon_bitmap_results) { |
534 got_favicon_from_history_ = true; | 545 got_favicon_from_history_ = true; |
535 bool has_valid_result = HasValidResult(favicon_bitmap_results); | 546 bool has_valid_result = HasValidResult(favicon_bitmap_results); |
536 initial_history_result_expired_or_incomplete_ = | 547 initial_history_result_expired_or_incomplete_ = |
537 !has_valid_result || | 548 !has_valid_result || HasExpiredResult(favicon_bitmap_results) || |
538 HasExpiredOrIncompleteResult(preferred_icon_size(), | 549 !target_spec_.HasCompleteResult(favicon_bitmap_results); |
539 favicon_bitmap_results); | |
540 redownload_icons_ = initial_history_result_expired_or_incomplete_ && | 550 redownload_icons_ = initial_history_result_expired_or_incomplete_ && |
541 !favicon_bitmap_results.empty(); | 551 !favicon_bitmap_results.empty(); |
542 | 552 |
543 if (has_valid_result && | 553 if (has_valid_result && |
544 (!current_candidate() || | 554 (!current_candidate() || |
545 DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results))) { | 555 DoUrlsAndIconsMatch(*current_candidate(), favicon_bitmap_results))) { |
546 // The db knows the favicon (although it may be out of date) and the entry | 556 // The db knows the favicon (although it may be out of date) and the entry |
547 // doesn't have an icon. Set the favicon now, and if the favicon turns out | 557 // doesn't have an icon. Set the favicon now, and if the favicon turns out |
548 // to be expired (or the wrong url) we'll fetch later on. This way the | 558 // to be expired (or the wrong url) we'll fetch later on. This way the |
549 // user doesn't see a flash of the default favicon. | 559 // user doesn't see a flash of the default favicon. |
(...skipping 10 matching lines...) Expand all Loading... | |
560 | 570 |
561 if (redownload_icons_) { | 571 if (redownload_icons_) { |
562 // We have the mapping, but the favicon is out of date. Download it now. | 572 // We have the mapping, but the favicon is out of date. Download it now. |
563 ScheduleDownload(icon_url, icon_type); | 573 ScheduleDownload(icon_url, icon_type); |
564 } else if (service_) { | 574 } else if (service_) { |
565 // We don't know the favicon, but we may have previously downloaded the | 575 // We don't know the favicon, but we may have previously downloaded the |
566 // favicon for another page that shares the same favicon. Ask for the | 576 // favicon for another page that shares the same favicon. Ask for the |
567 // favicon given the favicon URL. | 577 // favicon given the favicon URL. |
568 if (delegate_->IsOffTheRecord()) { | 578 if (delegate_->IsOffTheRecord()) { |
569 service_->GetFavicon( | 579 service_->GetFavicon( |
570 icon_url, icon_type, preferred_icon_size(), | 580 icon_url, icon_type, |
581 target_spec_.GetPixelSizes(), | |
571 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)), | 582 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)), |
572 &cancelable_task_tracker_); | 583 &cancelable_task_tracker_); |
573 } else { | 584 } else { |
574 // Ask the history service for the icon. This does two things: | 585 // Ask the history service for the icon. This does two things: |
575 // 1. Attempts to fetch the favicon data from the database. | 586 // 1. Attempts to fetch the favicon data from the database. |
576 // 2. If the favicon exists in the database, this updates the database to | 587 // 2. If the favicon exists in the database, this updates the database to |
577 // include the mapping between the page url and the favicon url. | 588 // include the mapping between the page url and the favicon url. |
578 // This is asynchronous. The history service will call back when done. | 589 // This is asynchronous. The history service will call back when done. |
579 // TODO(pkotwicz): pass in all of |image_urls_| to | 590 // TODO(pkotwicz): pass in all of |image_urls_| to |
580 // UpdateFaviconMappingsAndFetch(). | 591 // UpdateFaviconMappingsAndFetch(). |
581 service_->UpdateFaviconMappingsAndFetch( | 592 service_->UpdateFaviconMappingsAndFetch( |
582 url_, {icon_url}, icon_type, preferred_icon_size(), | 593 url_, {icon_url}, icon_type, target_spec_.GetPixelSizes(), |
583 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)), | 594 base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this)), |
584 &cancelable_task_tracker_); | 595 &cancelable_task_tracker_); |
585 } | 596 } |
586 } | 597 } |
587 } | 598 } |
588 | 599 |
589 void FaviconHandler::OnFaviconData(const std::vector< | 600 void FaviconHandler::OnFaviconData(const std::vector< |
590 favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) { | 601 favicon_base::FaviconRawBitmapResult>& favicon_bitmap_results) { |
591 bool has_results = !favicon_bitmap_results.empty(); | 602 bool has_results = !favicon_bitmap_results.empty(); |
592 bool has_valid_result = HasValidResult(favicon_bitmap_results); | 603 bool has_valid_result = HasValidResult(favicon_bitmap_results); |
593 bool has_expired_or_incomplete_result = | 604 bool has_expired_or_incomplete_result = |
594 !has_valid_result || HasExpiredOrIncompleteResult(preferred_icon_size(), | 605 !has_valid_result || HasExpiredResult(favicon_bitmap_results) || |
595 favicon_bitmap_results); | 606 !target_spec_.HasCompleteResult(favicon_bitmap_results); |
596 | 607 |
597 if (has_valid_result) { | 608 if (has_valid_result) { |
598 // There is a valid favicon. Notify any observers. It is useful to notify | 609 // There is a valid favicon. Notify any observers. It is useful to notify |
599 // the observers even if the favicon is expired or incomplete (incorrect | 610 // the observers even if the favicon is expired or incomplete (incorrect |
600 // size) because temporarily showing the user an expired favicon or | 611 // size) because temporarily showing the user an expired favicon or |
601 // streched favicon is preferable to showing the user the default favicon. | 612 // streched favicon is preferable to showing the user the default favicon. |
602 NotifyFaviconUpdated(favicon_bitmap_results); | 613 NotifyFaviconUpdated(favicon_bitmap_results); |
603 } | 614 } |
604 | 615 |
605 if (!current_candidate() || | 616 if (!current_candidate() || |
(...skipping 18 matching lines...) Expand all Loading... | |
624 // OnDidDownloadFavicon() from returning early. | 635 // OnDidDownloadFavicon() from returning early. |
625 download_requests_[0] = DownloadRequest(image_url, icon_type); | 636 download_requests_[0] = DownloadRequest(image_url, icon_type); |
626 OnDidDownloadFavicon(0, 0, image_url, std::vector<SkBitmap>(), | 637 OnDidDownloadFavicon(0, 0, image_url, std::vector<SkBitmap>(), |
627 std::vector<gfx::Size>()); | 638 std::vector<gfx::Size>()); |
628 return; | 639 return; |
629 } | 640 } |
630 // A max bitmap size is specified to avoid receiving huge bitmaps in | 641 // A max bitmap size is specified to avoid receiving huge bitmaps in |
631 // OnDidDownloadFavicon(). See FaviconDriver::StartDownload() | 642 // OnDidDownloadFavicon(). See FaviconDriver::StartDownload() |
632 // for more details about the max bitmap size. | 643 // for more details about the max bitmap size. |
633 const int download_id = | 644 const int download_id = |
634 delegate_->DownloadImage(image_url, GetMaximalIconSize(icon_type), | 645 delegate_->DownloadImage(image_url, target_spec_.GetMaxPixelSize(), |
635 base::Bind(&FaviconHandler::OnDidDownloadFavicon, | 646 base::Bind(&FaviconHandler::OnDidDownloadFavicon, |
636 weak_ptr_factory_.GetWeakPtr())); | 647 weak_ptr_factory_.GetWeakPtr())); |
637 DCHECK_NE(download_id, 0); | 648 DCHECK_NE(download_id, 0); |
638 | 649 |
639 // Download ids should be unique. | 650 // Download ids should be unique. |
640 DCHECK(download_requests_.find(download_id) == download_requests_.end()); | 651 DCHECK(download_requests_.find(download_id) == download_requests_.end()); |
641 download_requests_[download_id] = DownloadRequest(image_url, icon_type); | 652 download_requests_[download_id] = DownloadRequest(image_url, icon_type); |
642 } | 653 } |
643 | 654 |
644 } // namespace favicon | 655 } // namespace favicon |
OLD | NEW |