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
3 // found in the LICENSE file.
4
5 #include "components/favicon/core/favicon_handler.h"
6
7 #include <stddef.h>
8
9 #include <map>
10 #include <memory>
11 #include <utility>
12 #include <vector>
13
14 #include "base/bind.h"
15 #include "base/bind_helpers.h"
16 #include "base/macros.h"
17 #include "base/run_loop.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/test/metrics/histogram_tester.h"
20 #include "base/test/scoped_feature_list.h"
21 #include "base/test/task_environment.h"
22 #include "base/test/test_simple_task_runner.h"
23 #include "components/favicon/core/favicon_driver.h"
24 #include "components/favicon/core/features.h"
25 #include "components/favicon/core/test/mock_favicon_service.h"
26 #include "skia/ext/image_operations.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "third_party/skia/include/core/SkBitmap.h"
30 #include "third_party/skia/include/core/SkColor.h"
31 #include "ui/base/layout.h"
32 #include "ui/gfx/codec/png_codec.h"
33 #include "ui/gfx/favicon_size.h"
34 #include "ui/gfx/image/image.h"
35
36 namespace favicon {
37 namespace {
38
39 using favicon_base::FaviconRawBitmapResult;
40 using testing::_;
41 using testing::AnyNumber;
42 using testing::Assign;
43 using testing::Contains;
44 using testing::ElementsAre;
45 using testing::InSequence;
46 using testing::Invoke;
47 using testing::IsEmpty;
48 using testing::Not;
49 using testing::Return;
50 using testing::SizeIs;
51
52 using IntVector = std::vector<int>;
53 using URLVector = std::vector<GURL>;
54 using BitmapVector = std::vector<SkBitmap>;
55 using SizeVector = std::vector<gfx::Size>;
56
57 constexpr favicon_base::IconType kFavicon = favicon_base::IconType::kFavicon;
58 constexpr favicon_base::IconType kTouchIcon =
59 favicon_base::IconType::kTouchIcon;
60 constexpr favicon_base::IconType kTouchPrecomposedIcon =
61 favicon_base::IconType::kTouchPrecomposedIcon;
62 constexpr favicon_base::IconType kWebManifestIcon =
63 favicon_base::IconType::kWebManifestIcon;
64
65 MATCHER_P2(ImageSizeIs, width, height, "") {
66 *result_listener << "where size is " << arg.Width() << "x" << arg.Height();
67 return arg.Size() == gfx::Size(width, height);
68 }
69
70 // |arg| is a gfx::Image.
71 MATCHER_P(ImageColorIs, expected_color, "") {
72 SkBitmap bitmap = arg.AsBitmap();
73 if (bitmap.empty()) {
74 *result_listener << "expected color but no bitmap data available";
75 return false;
76 }
77
78 SkColor actual_color = bitmap.getColor(1, 1);
79 if (actual_color != expected_color) {
80 *result_listener << "expected color "
81 << base::StringPrintf("%08X", expected_color)
82 << " but actual color is "
83 << base::StringPrintf("%08X", actual_color);
84 return false;
85 }
86 return true;
87 }
88
CreateBitmapWithEdgeSize(int size,SkColor color)89 SkBitmap CreateBitmapWithEdgeSize(int size, SkColor color) {
90 SkBitmap bmp;
91 bmp.allocN32Pixels(size, size);
92 bmp.eraseColor(color);
93 return bmp;
94 }
95
96 // Fill the given data buffer with valid png data.
FillBitmapWithEdgeSize(int size,SkColor color)97 std::vector<unsigned char> FillBitmapWithEdgeSize(int size, SkColor color) {
98 SkBitmap bitmap = CreateBitmapWithEdgeSize(size, color);
99 std::vector<unsigned char> output;
100 gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &output);
101 return output;
102 }
103
CreateRawBitmapResult(const GURL & icon_url,favicon_base::IconType icon_type=kFavicon,bool expired=false,int edge_size=gfx::kFaviconSize,SkColor color=SK_ColorRED)104 std::vector<FaviconRawBitmapResult> CreateRawBitmapResult(
105 const GURL& icon_url,
106 favicon_base::IconType icon_type = kFavicon,
107 bool expired = false,
108 int edge_size = gfx::kFaviconSize,
109 SkColor color = SK_ColorRED) {
110 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
111 data->data() = FillBitmapWithEdgeSize(edge_size, color);
112 FaviconRawBitmapResult bitmap_result;
113 bitmap_result.expired = expired;
114 bitmap_result.bitmap_data = data;
115 // Use a pixel size other than (0,0) as (0,0) has a special meaning.
116 bitmap_result.pixel_size = gfx::Size(edge_size, edge_size);
117 bitmap_result.icon_type = icon_type;
118 bitmap_result.icon_url = icon_url;
119 return {bitmap_result};
120 }
121
122 // Fake that implements the calls to FaviconHandler::Delegate's DownloadImage(),
123 // delegated to this class through MockDelegate.
124 class FakeImageDownloader {
125 public:
126 struct Response {
127 int http_status_code = 404;
128 BitmapVector bitmaps;
129 SizeVector original_bitmap_sizes;
130 };
131
132 // |downloads| must not be nullptr and must outlive this object.
FakeImageDownloader(URLVector * downloads)133 explicit FakeImageDownloader(URLVector* downloads)
134 : downloads_(downloads), next_download_id_(1) {}
135
136 // Implementation of FaviconHalder::Delegate's DownloadImage(). If a given
137 // URL is not known (i.e. not previously added via Add()), it produces 404s.
DownloadImage(const GURL & url,int max_image_size,FaviconHandler::Delegate::ImageDownloadCallback callback)138 int DownloadImage(const GURL& url,
139 int max_image_size,
140 FaviconHandler::Delegate::ImageDownloadCallback callback) {
141 downloads_->push_back(url);
142
143 Response response = responses_[url];
144 DCHECK_EQ(response.bitmaps.size(), response.original_bitmap_sizes.size());
145 // Apply maximum image size.
146 for (size_t i = 0; i < response.bitmaps.size(); ++i) {
147 if (response.bitmaps[i].width() > max_image_size ||
148 response.bitmaps[i].height() > max_image_size) {
149 response.bitmaps[i] = skia::ImageOperations::Resize(
150 response.bitmaps[i], skia::ImageOperations::RESIZE_LANCZOS3,
151 max_image_size, max_image_size);
152 }
153 }
154
155 int download_id = next_download_id_++;
156 base::OnceClosure bound_callback = base::BindOnce(
157 std::move(callback), download_id, response.http_status_code, url,
158 response.bitmaps, response.original_bitmap_sizes);
159 if (url == manual_callback_url_)
160 manual_callbacks_.push_back(std::move(bound_callback));
161 else
162 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
163 std::move(bound_callback));
164 return download_id;
165 }
166
Add(const GURL & icon_url,const IntVector & original_sizes,SkColor color=SK_ColorRED)167 void Add(const GURL& icon_url,
168 const IntVector& original_sizes,
169 SkColor color = SK_ColorRED) {
170 Response response;
171 response.http_status_code = 200;
172 for (int size : original_sizes) {
173 response.original_bitmap_sizes.push_back(gfx::Size(size, size));
174 response.bitmaps.push_back(CreateBitmapWithEdgeSize(size, color));
175 }
176 responses_[icon_url] = response;
177 }
178
AddError(const GURL & icon_url,int http_status_code)179 void AddError(const GURL& icon_url, int http_status_code) {
180 Response response;
181 response.http_status_code = http_status_code;
182 responses_[icon_url] = response;
183 }
184
185 // Disables automatic callback for |url|. This is useful for emulating a
186 // download taking a long time. The callback for DownloadImage() will be
187 // stored in |manual_callbacks_|.
SetRunCallbackManuallyForUrl(const GURL & url)188 void SetRunCallbackManuallyForUrl(const GURL& url) {
189 manual_callback_url_ = url;
190 }
191
192 // Returns whether an ongoing download exists for a url previously selected
193 // via SetRunCallbackManuallyForUrl().
HasPendingManualCallback()194 bool HasPendingManualCallback() { return !manual_callbacks_.empty(); }
195
196 // Triggers responses for downloads previously selected for manual triggering
197 // via SetRunCallbackManuallyForUrl().
RunCallbackManually()198 bool RunCallbackManually() {
199 if (!HasPendingManualCallback())
200 return false;
201 for (auto& callback : std::move(manual_callbacks_))
202 std::move(callback).Run();
203 return true;
204 }
205
206 private:
207 URLVector* downloads_;
208 int next_download_id_;
209
210 // URL to disable automatic callbacks for.
211 GURL manual_callback_url_;
212
213 // Callback for DownloadImage() request for |manual_callback_url_|.
214 std::vector<base::OnceClosure> manual_callbacks_;
215
216 // Registered responses.
217 std::map<GURL, Response> responses_;
218
219 DISALLOW_COPY_AND_ASSIGN(FakeImageDownloader);
220 };
221
222 // Fake that implements the calls to FaviconHandler::Delegate's
223 // DownloadManifest(), delegated to this class through MockDelegate.
224 class FakeManifestDownloader {
225 public:
226 struct Response {
227 std::vector<favicon::FaviconURL> favicon_urls;
228 };
229
230 // |downloads| must not be nullptr and must outlive this object.
FakeManifestDownloader(URLVector * downloads)231 explicit FakeManifestDownloader(URLVector* downloads)
232 : downloads_(downloads) {}
233
234 // Implementation of FaviconHalder::Delegate's DownloadManifest(). If a given
235 // URL is not known (i.e. not previously added via Add()), it produces 404s.
DownloadManifest(const GURL & url,FaviconHandler::Delegate::ManifestDownloadCallback callback)236 void DownloadManifest(
237 const GURL& url,
238 FaviconHandler::Delegate::ManifestDownloadCallback callback) {
239 downloads_->push_back(url);
240
241 const Response& response = responses_[url];
242 base::OnceClosure bound_callback =
243 base::BindOnce(std::move(callback), response.favicon_urls);
244 if (url == manual_callback_url_)
245 manual_callbacks_.push_back(std::move(bound_callback));
246 else
247 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
248 std::move(bound_callback));
249 }
250
Add(const GURL & manifest_url,const std::vector<favicon::FaviconURL> & favicon_urls)251 void Add(const GURL& manifest_url,
252 const std::vector<favicon::FaviconURL>& favicon_urls) {
253 Response response;
254 response.favicon_urls = favicon_urls;
255 responses_[manifest_url] = response;
256 }
257
AddError(const GURL & manifest_url)258 void AddError(const GURL& manifest_url) {
259 responses_[manifest_url] = Response();
260 }
261
262 // Disables automatic callback for |url|. This is useful for emulating a
263 // download taking a long time. The callback for DownloadManifest() will be
264 // stored in |manual_callback_|.
SetRunCallbackManuallyForUrl(const GURL & url)265 void SetRunCallbackManuallyForUrl(const GURL& url) {
266 manual_callback_url_ = url;
267 }
268
269 // Returns whether an ongoing download exists for a url previously selected
270 // via SetRunCallbackManuallyForUrl().
HasPendingManualCallback()271 bool HasPendingManualCallback() { return !manual_callbacks_.empty(); }
272
273 // Triggers responses for downloads previously selected for manual triggering
274 // via SetRunCallbackManuallyForUrl().
RunCallbackManually()275 bool RunCallbackManually() {
276 if (!HasPendingManualCallback())
277 return false;
278 for (auto& callback : std::move(manual_callbacks_))
279 std::move(callback).Run();
280 return true;
281 }
282
283 private:
284 URLVector* downloads_;
285
286 // URL to disable automatic callbacks for.
287 GURL manual_callback_url_;
288
289 // Callback for DownloadManifest() request for |manual_callback_url_|.
290 std::vector<base::OnceClosure> manual_callbacks_;
291
292 // Registered responses.
293 std::map<GURL, Response> responses_;
294
295 DISALLOW_COPY_AND_ASSIGN(FakeManifestDownloader);
296 };
297
298 class MockDelegate : public FaviconHandler::Delegate {
299 public:
MockDelegate()300 MockDelegate()
301 : fake_image_downloader_(&downloads_),
302 fake_manifest_downloader_(&downloads_) {
303 }
304
DownloadImage(const GURL & url,int max_image_size,ImageDownloadCallback callback)305 int DownloadImage(const GURL& url,
306 int max_image_size,
307 ImageDownloadCallback callback) override {
308 return fake_image_downloader_.DownloadImage(url, max_image_size,
309 std::move(callback));
310 }
311
DownloadManifest(const GURL & url,ManifestDownloadCallback callback)312 void DownloadManifest(const GURL& url,
313 ManifestDownloadCallback callback) override {
314 fake_manifest_downloader_.DownloadManifest(url, std::move(callback));
315 }
316
317 MOCK_METHOD0(IsOffTheRecord, bool());
318 MOCK_METHOD1(IsBookmarked, bool(const GURL& url));
319 MOCK_METHOD5(OnFaviconUpdated,
320 void(const GURL& page_url,
321 FaviconDriverObserver::NotificationIconType type,
322 const GURL& icon_url,
323 bool icon_url_changed,
324 const gfx::Image& image));
325 MOCK_METHOD2(OnFaviconDeleted,
326 void(const GURL& page_url,
327 FaviconDriverObserver::NotificationIconType type));
328
fake_image_downloader()329 FakeImageDownloader& fake_image_downloader() {
330 return fake_image_downloader_;
331 }
332
fake_manifest_downloader()333 FakeManifestDownloader& fake_manifest_downloader() {
334 return fake_manifest_downloader_;
335 }
336
337 // Returns pending and completed download URLs.
downloads() const338 const URLVector& downloads() const { return downloads_; }
339
ClearDownloads()340 void ClearDownloads() { downloads_.clear(); }
341
342 private:
343 // Pending and completed download URLs.
344 URLVector downloads_;
345 FakeImageDownloader fake_image_downloader_;
346 FakeManifestDownloader fake_manifest_downloader_;
347 };
348
349 // FakeFaviconService mimics a FaviconService backend that allows setting up
350 // test data stored via Store(). If Store() has not been called for a
351 // particular URL, the callback is called with empty database results.
352 class FakeFaviconService {
353 public:
FakeFaviconService()354 FakeFaviconService()
355 : manual_callback_task_runner_(new base::TestSimpleTaskRunner()) {}
356
357 // Stores favicon with bitmap data in |results| at |page_url| and |icon_url|.
Store(const GURL & page_url,const GURL & icon_url,const std::vector<favicon_base::FaviconRawBitmapResult> & result)358 void Store(const GURL& page_url,
359 const GURL& icon_url,
360 const std::vector<favicon_base::FaviconRawBitmapResult>& result) {
361 results_[icon_url] = result;
362 results_[page_url] = result;
363 }
364
365 // Returns pending and completed database request URLs.
db_requests() const366 const URLVector& db_requests() const { return db_requests_; }
367
ClearDbRequests()368 void ClearDbRequests() { db_requests_.clear(); }
369
GetFavicon(const GURL & icon_url,favicon_base::IconType icon_type,int desired_size_in_dip,favicon_base::FaviconResultsCallback callback,base::CancelableTaskTracker * tracker)370 base::CancelableTaskTracker::TaskId GetFavicon(
371 const GURL& icon_url,
372 favicon_base::IconType icon_type,
373 int desired_size_in_dip,
374 favicon_base::FaviconResultsCallback callback,
375 base::CancelableTaskTracker* tracker) {
376 return GetFaviconForPageOrIconURL(icon_url, std::move(callback), tracker);
377 }
378
GetFaviconForPageURL(const GURL & page_url,const favicon_base::IconTypeSet & icon_types,int desired_size_in_dip,favicon_base::FaviconResultsCallback callback,base::CancelableTaskTracker * tracker)379 base::CancelableTaskTracker::TaskId GetFaviconForPageURL(
380 const GURL& page_url,
381 const favicon_base::IconTypeSet& icon_types,
382 int desired_size_in_dip,
383 favicon_base::FaviconResultsCallback callback,
384 base::CancelableTaskTracker* tracker) {
385 return GetFaviconForPageOrIconURL(page_url, std::move(callback), tracker);
386 }
387
UpdateFaviconMappingsAndFetch(const base::flat_set<GURL> & page_urls,const GURL & icon_url,favicon_base::IconType icon_type,int desired_size_in_dip,favicon_base::FaviconResultsCallback callback,base::CancelableTaskTracker * tracker)388 base::CancelableTaskTracker::TaskId UpdateFaviconMappingsAndFetch(
389 const base::flat_set<GURL>& page_urls,
390 const GURL& icon_url,
391 favicon_base::IconType icon_type,
392 int desired_size_in_dip,
393 favicon_base::FaviconResultsCallback callback,
394 base::CancelableTaskTracker* tracker) {
395 return GetFaviconForPageOrIconURL(icon_url, std::move(callback), tracker);
396 }
397
398 // Disables automatic callback for |url|. This is useful for emulating a
399 // DB lookup taking a long time. The callback for
400 // GetFaviconForPageOrIconURL() will be stored in |manual_callback_|.
SetRunCallbackManuallyForUrl(const GURL & url)401 void SetRunCallbackManuallyForUrl(const GURL& url) {
402 manual_callback_url_ = url;
403 }
404
405 // Returns whether an ongoing lookup exists for a url previously selected
406 // via SetRunCallbackManuallyForUrl().
HasPendingManualCallback()407 bool HasPendingManualCallback() {
408 return manual_callback_task_runner_->HasPendingTask();
409 }
410
411 // Triggers the response for a lookup previously selected for manual
412 // triggering via SetRunCallbackManuallyForUrl().
RunCallbackManually()413 bool RunCallbackManually() {
414 if (!HasPendingManualCallback())
415 return false;
416 manual_callback_task_runner_->RunPendingTasks();
417 return true;
418 }
419
420 private:
GetFaviconForPageOrIconURL(const GURL & page_or_icon_url,favicon_base::FaviconResultsCallback callback,base::CancelableTaskTracker * tracker)421 base::CancelableTaskTracker::TaskId GetFaviconForPageOrIconURL(
422 const GURL& page_or_icon_url,
423 favicon_base::FaviconResultsCallback callback,
424 base::CancelableTaskTracker* tracker) {
425 db_requests_.push_back(page_or_icon_url);
426
427 base::OnceClosure bound_callback =
428 base::BindOnce(std::move(callback), results_[page_or_icon_url]);
429
430 // In addition to checking the URL against |manual_callback_url_|, we also
431 // defer responses if there are already pending responses (i.e. a previous
432 // lookup matched |manual_callback_url_|), because requests to the history
433 // should be executed sequentially.
434 if (page_or_icon_url != manual_callback_url_ &&
435 !HasPendingManualCallback()) {
436 return tracker->PostTask(base::ThreadTaskRunnerHandle::Get().get(),
437 FROM_HERE, std::move(bound_callback));
438 }
439
440 // We use PostTaskAndReply() to cause |callback| being run in the current
441 // TaskRunner.
442 return tracker->PostTaskAndReply(manual_callback_task_runner_.get(),
443 FROM_HERE, base::DoNothing(),
444 std::move(bound_callback));
445 }
446
447 std::map<GURL, std::vector<favicon_base::FaviconRawBitmapResult>> results_;
448 URLVector db_requests_;
449
450 // URL to disable automatic callbacks for.
451 GURL manual_callback_url_;
452
453 // Callback for GetFaviconForPageOrIconURL() request for
454 // |manual_callback_url_|.
455 scoped_refptr<base::TestSimpleTaskRunner> manual_callback_task_runner_;
456
457 DISALLOW_COPY_AND_ASSIGN(FakeFaviconService);
458 };
459
460 // MockFaviconService subclass that delegates DB reads to FakeFaviconService.
461 class MockFaviconServiceWithFake : public MockFaviconService {
462 public:
MockFaviconServiceWithFake()463 MockFaviconServiceWithFake() {
464 // Delegate the various methods that read from the DB.
465 ON_CALL(*this, GetFavicon(_, _, _, _, _))
466 .WillByDefault(Invoke(&fake_, &FakeFaviconService::GetFavicon));
467 ON_CALL(*this, GetFaviconForPageURL(_, _, _, _, _))
468 .WillByDefault(
469 Invoke(&fake_, &FakeFaviconService::GetFaviconForPageURL));
470 ON_CALL(*this, UpdateFaviconMappingsAndFetch(_, _, _, _, _, _))
471 .WillByDefault(
472 Invoke(&fake_, &FakeFaviconService::UpdateFaviconMappingsAndFetch));
473 }
474
fake()475 FakeFaviconService* fake() { return &fake_; }
476
477 private:
478 FakeFaviconService fake_;
479
480 DISALLOW_COPY_AND_ASSIGN(MockFaviconServiceWithFake);
481 };
482
483 class FaviconHandlerTest : public testing::Test {
484 protected:
485 const std::vector<gfx::Size> kEmptySizes;
486
487 // Some known icons for which download will succeed.
488 const GURL kPageURL = GURL("http://www.google.com");
489 const GURL kIconURL10x10 = GURL("http://www.google.com/favicon10x10");
490 const GURL kIconURL12x12 = GURL("http://www.google.com/favicon12x12");
491 const GURL kIconURL16x16 = GURL("http://www.google.com/favicon16x16");
492 const GURL kIconURL64x64 = GURL("http://www.google.com/favicon64x64");
493
FaviconHandlerTest()494 FaviconHandlerTest()
495 : task_environment_(
496 base::test::SingleThreadTaskEnvironment::MainThreadType::UI) {
497 // Register various known icon URLs.
498 delegate_.fake_image_downloader().Add(kIconURL10x10, IntVector{10});
499 delegate_.fake_image_downloader().Add(kIconURL12x12, IntVector{12});
500 delegate_.fake_image_downloader().Add(kIconURL16x16, IntVector{16});
501 delegate_.fake_image_downloader().Add(kIconURL64x64, IntVector{64});
502
503 // The score computed by SelectFaviconFrames() is dependent on the supported
504 // scale factors of the platform. It is used for determining the goodness of
505 // a downloaded bitmap in FaviconHandler::OnDidDownloadFavicon().
506 // Force the values of the scale factors so that the tests produce the same
507 // results on all platforms.
508 scoped_set_supported_scale_factors_.reset(
509 new ui::test::ScopedSetSupportedScaleFactors({ui::SCALE_FACTOR_100P}));
510 }
511
VerifyAndClearExpectations()512 bool VerifyAndClearExpectations() {
513 base::RunLoop().RunUntilIdle();
514 favicon_service_.fake()->ClearDbRequests();
515 delegate_.ClearDownloads();
516 return testing::Mock::VerifyAndClearExpectations(&favicon_service_) &&
517 testing::Mock::VerifyAndClearExpectations(&delegate_);
518 }
519
520 // Creates a new handler and feeds in the page URL and the candidates.
521 // Returns the handler in case tests want to exercise further steps.
RunHandlerWithCandidates(FaviconDriverObserver::NotificationIconType handler_type,const std::vector<FaviconURL> & candidates,const GURL & manifest_url=GURL ())522 std::unique_ptr<FaviconHandler> RunHandlerWithCandidates(
523 FaviconDriverObserver::NotificationIconType handler_type,
524 const std::vector<FaviconURL>& candidates,
525 const GURL& manifest_url = GURL()) {
526 auto handler = std::make_unique<FaviconHandler>(&favicon_service_,
527 &delegate_, handler_type);
528 handler->FetchFavicon(kPageURL, /*is_same_document=*/false);
529 // The first RunUntilIdle() causes the FaviconService lookups be faster than
530 // OnUpdateCandidates(), which is the most likely scenario.
531 base::RunLoop().RunUntilIdle();
532 handler->OnUpdateCandidates(kPageURL, candidates, manifest_url);
533 base::RunLoop().RunUntilIdle();
534 return handler;
535 }
536
537 // Same as above, but for the simplest case where all types are kFavicon and
538 // no sizes are provided, using a FaviconHandler of type NON_TOUCH_16_DIP.
RunHandlerWithSimpleFaviconCandidates(const std::vector<GURL> & urls,const GURL & manifest_url=GURL ())539 std::unique_ptr<FaviconHandler> RunHandlerWithSimpleFaviconCandidates(
540 const std::vector<GURL>& urls,
541 const GURL& manifest_url = GURL()) {
542 std::vector<favicon::FaviconURL> candidates;
543 for (const GURL& url : urls) {
544 candidates.emplace_back(url, kFavicon, kEmptySizes);
545 }
546 return RunHandlerWithCandidates(FaviconDriverObserver::NON_TOUCH_16_DIP,
547 candidates, manifest_url);
548 }
549
550 base::test::SingleThreadTaskEnvironment task_environment_;
551 std::unique_ptr<ui::test::ScopedSetSupportedScaleFactors>
552 scoped_set_supported_scale_factors_;
553 testing::NiceMock<MockFaviconServiceWithFake> favicon_service_;
554 testing::NiceMock<MockDelegate> delegate_;
555 };
556
TEST_F(FaviconHandlerTest,GetFaviconFromHistory)557 TEST_F(FaviconHandlerTest, GetFaviconFromHistory) {
558 base::HistogramTester histogram_tester;
559 const GURL kIconURL("http://www.google.com/favicon");
560
561 favicon_service_.fake()->Store(kPageURL, kIconURL,
562 CreateRawBitmapResult(kIconURL));
563
564 EXPECT_CALL(delegate_, OnFaviconUpdated(
565 kPageURL, FaviconDriverObserver::NON_TOUCH_16_DIP,
566 kIconURL, /*icon_url_changed=*/true, _));
567
568 RunHandlerWithSimpleFaviconCandidates({kIconURL});
569 EXPECT_THAT(delegate_.downloads(), IsEmpty());
570 }
571
572 // Test that UpdateFaviconsAndFetch() is called with the appropriate parameters
573 // when there is no data in the database for the page URL.
TEST_F(FaviconHandlerTest,UpdateFaviconMappingsAndFetch)574 TEST_F(FaviconHandlerTest, UpdateFaviconMappingsAndFetch) {
575 EXPECT_CALL(favicon_service_,
576 UpdateFaviconMappingsAndFetch(base::flat_set<GURL>{kPageURL},
577 kIconURL16x16, kFavicon,
578 /*desired_size_in_dip=*/16, _, _));
579
580 RunHandlerWithSimpleFaviconCandidates({kIconURL16x16});
581 }
582
583 // Test that we don't try to delete favicon mappings when a page URL is not in
584 // history even if the page lists no favicons.
TEST_F(FaviconHandlerTest,DoNotDeleteFaviconMappingsIfNotInHistory)585 TEST_F(FaviconHandlerTest, DoNotDeleteFaviconMappingsIfNotInHistory) {
586 const GURL kIconURL("http://www.google.com/favicon");
587
588 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
589 EXPECT_CALL(delegate_, OnFaviconDeleted(_, _)).Times(0);
590
591 RunHandlerWithSimpleFaviconCandidates(URLVector());
592 }
593
594 // Test that favicon mappings are deleted when:
595 // - There is data in the favicon database for the page URL.
596 // - The page lists no candidates.
597 // AND
598 // - FaviconService::OnFaviconDataForManifestFromFaviconService() runs before
599 // FaviconHandler::OnUpdateCandidates() is called.
TEST_F(FaviconHandlerTest,DeleteFaviconMappingsIfCandidatesSlower)600 TEST_F(FaviconHandlerTest, DeleteFaviconMappingsIfCandidatesSlower) {
601 const GURL kIconURL("http://www.google.com/favicon");
602
603 favicon_service_.fake()->Store(kPageURL, kIconURL,
604 CreateRawBitmapResult(kIconURL));
605
606 // Defer the database lookup completion to control the exact timing.
607 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
608
609 EXPECT_CALL(delegate_, OnFaviconDeleted(_, _)).Times(0);
610 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
611
612 FaviconHandler handler(&favicon_service_, &delegate_,
613 FaviconDriverObserver::NON_TOUCH_16_DIP);
614 handler.FetchFavicon(kPageURL, /*is_same_document=*/false);
615 base::RunLoop().RunUntilIdle();
616 // Database lookup for |kPageURL| is ongoing.
617 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
618 // Causes FaviconService lookups be faster than OnUpdateCandidates().
619 ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
620 ASSERT_TRUE(VerifyAndClearExpectations());
621
622 EXPECT_CALL(
623 delegate_,
624 OnFaviconDeleted(kPageURL, FaviconDriverObserver::NON_TOUCH_16_DIP));
625 EXPECT_CALL(favicon_service_,
626 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kFavicon));
627
628 // Feed in (zero) candidates now that the database lookup is completed.
629 handler.OnUpdateCandidates(kPageURL, std::vector<FaviconURL>(),
630 /*manifest_url=*/GURL());
631 base::RunLoop().RunUntilIdle();
632 }
633
634 // Test that favicon mappings are deleted when:
635 // - There is data in the favicon database for the page URL.
636 // - The page lists no candidates.
637 // AND
638 // - FaviconHandler::OnUpdateCandidates() is called before
639 // FaviconService::OnFaviconDataForManifestFromFaviconService() runs.
TEST_F(FaviconHandlerTest,DeleteFaviconMappingsIfCandidatesFaster)640 TEST_F(FaviconHandlerTest, DeleteFaviconMappingsIfCandidatesFaster) {
641 const GURL kIconURL("http://www.google.com/favicon");
642
643 favicon_service_.fake()->Store(kPageURL, kIconURL,
644 CreateRawBitmapResult(kIconURL));
645
646 // Defer the database lookup completion to control the exact timing.
647 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
648
649 EXPECT_CALL(delegate_, OnFaviconDeleted(_, _)).Times(0);
650 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
651
652 FaviconHandler handler(&favicon_service_, &delegate_,
653 FaviconDriverObserver::NON_TOUCH_16_DIP);
654 handler.FetchFavicon(kPageURL, /*is_same_document=*/false);
655 base::RunLoop().RunUntilIdle();
656 // Feed in (zero) candidates before completing the database lookup.
657 handler.OnUpdateCandidates(kPageURL, std::vector<FaviconURL>(),
658 /*manifest_url=*/GURL());
659 ASSERT_TRUE(VerifyAndClearExpectations());
660 // Database lookup for |kPageURL| is ongoing.
661 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
662
663 EXPECT_CALL(
664 delegate_,
665 OnFaviconDeleted(kPageURL, FaviconDriverObserver::NON_TOUCH_16_DIP));
666 EXPECT_CALL(favicon_service_,
667 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kFavicon));
668
669 // Complete the lookup for |kPageURL|.
670 ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
671 base::RunLoop().RunUntilIdle();
672 }
673
674 // Test that favicon mappings are deleted when a page in history lists a
675 // candidate that is expired and is known to return a 404.
TEST_F(FaviconHandlerTest,DeleteFaviconMappingsDespitePrior404)676 TEST_F(FaviconHandlerTest, DeleteFaviconMappingsDespitePrior404) {
677 const GURL kIconURL("http://www.google.com/favicon");
678
679 favicon_service_.fake()->Store(
680 kPageURL, kIconURL,
681 CreateRawBitmapResult(kIconURL, kFavicon, /*expired=*/true));
682
683 ON_CALL(favicon_service_, WasUnableToDownloadFavicon(kIconURL))
684 .WillByDefault(Return(true));
685
686 EXPECT_CALL(
687 delegate_,
688 OnFaviconDeleted(kPageURL, FaviconDriverObserver::NON_TOUCH_16_DIP));
689 EXPECT_CALL(favicon_service_,
690 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kFavicon));
691
692 RunHandlerWithSimpleFaviconCandidates({kIconURL});
693 }
694
695 // Test that favicon mappings are deleted for a page in history, when all icons
696 // listed in the page return a 404.
TEST_F(FaviconHandlerTest,DeleteFaviconMappingsDueTo404)697 TEST_F(FaviconHandlerTest, DeleteFaviconMappingsDueTo404) {
698 const GURL kIconURLInHistory("http://www.google.com/favicon-in-history");
699 const GURL k404IconURL("http://www.google.com/404.png");
700
701 favicon_service_.fake()->Store(kPageURL, kIconURLInHistory,
702 CreateRawBitmapResult(kIconURLInHistory));
703
704 EXPECT_CALL(favicon_service_,
705 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kFavicon));
706
707 RunHandlerWithSimpleFaviconCandidates({k404IconURL});
708 }
709
710 // Test that we don't try to delete favicon mappings when a page URL is not in
711 // history even if all icons listed in the page return a 404.
TEST_F(FaviconHandlerTest,DoNotDeleteFaviconMappingsIfNotInHistoryDespite404)712 TEST_F(FaviconHandlerTest, DoNotDeleteFaviconMappingsIfNotInHistoryDespite404) {
713 const GURL k404IconURL("http://www.google.com/404.png");
714
715 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
716
717 RunHandlerWithSimpleFaviconCandidates({k404IconURL});
718 }
719
720 // Test that favicon mappings are not deleted for a page in history when all
721 // icons listed in the page return a 503.
TEST_F(FaviconHandlerTest,DoNotDeleteFaviconMappingsDueTo503)722 TEST_F(FaviconHandlerTest, DoNotDeleteFaviconMappingsDueTo503) {
723 const GURL kIconURLInHistory("http://www.google.com/favicon-in-history");
724 const GURL k503IconURL("http://www.google.com/503.png");
725
726 delegate_.fake_image_downloader().AddError(k503IconURL, 503);
727
728 favicon_service_.fake()->Store(kPageURL, kIconURLInHistory,
729 CreateRawBitmapResult(kIconURLInHistory));
730
731 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
732
733 RunHandlerWithSimpleFaviconCandidates({k503IconURL});
734 }
735
736 // Test that UpdateFaviconsAndFetch() is called with the appropriate parameters
737 // when there is no data in the database for the page URL, for the case where
738 // multiple page URLs exist due to a quick in-same-document navigation (e.g.
739 // fragment navigation).
TEST_F(FaviconHandlerTest,UpdateFaviconMappingsAndFetchWithMultipleURLs)740 TEST_F(FaviconHandlerTest, UpdateFaviconMappingsAndFetchWithMultipleURLs) {
741 const GURL kDifferentPageURL = GURL("http://www.google.com/other");
742
743 EXPECT_CALL(favicon_service_,
744 UpdateFaviconMappingsAndFetch(
745 base::flat_set<GURL>{kPageURL, kDifferentPageURL},
746 kIconURL16x16, _, _, _, _));
747
748 std::unique_ptr<FaviconHandler> handler = std::make_unique<FaviconHandler>(
749 &favicon_service_, &delegate_, FaviconDriverObserver::NON_TOUCH_16_DIP);
750 handler->FetchFavicon(kPageURL, /*is_same_document=*/false);
751 base::RunLoop().RunUntilIdle();
752 // Load a new URL (same document) without feeding any candidates for the first
753 // URL.
754 handler->FetchFavicon(kDifferentPageURL, /*is_same_document=*/true);
755 base::RunLoop().RunUntilIdle();
756 // Feed in candidates for the second URL.
757 handler->OnUpdateCandidates(
758 kDifferentPageURL, {FaviconURL(kIconURL16x16, kFavicon, kEmptySizes)},
759 /*manifest_url=*/GURL());
760 base::RunLoop().RunUntilIdle();
761 }
762
763 // Test that CloneFaviconMappingsForPages() is called for the simplest case,
764 // i.e. a single page without redirect URLs to update mappings for (no known
765 // in-same-document navigation). This is important in case there are server-side
766 // redirects to update (that are only known within HistoryService).
TEST_F(FaviconHandlerTest,CloneFaviconMappingsForPageInHistory)767 TEST_F(FaviconHandlerTest, CloneFaviconMappingsForPageInHistory) {
768 favicon_service_.fake()->Store(kPageURL, kIconURL16x16,
769 CreateRawBitmapResult(kIconURL16x16));
770
771 EXPECT_CALL(favicon_service_,
772 CloneFaviconMappingsForPages(
773 kPageURL, favicon_base::IconTypeSet({kFavicon}),
774 base::flat_set<GURL>({kPageURL})));
775
776 std::unique_ptr<FaviconHandler> handler = std::make_unique<FaviconHandler>(
777 &favicon_service_, &delegate_, FaviconDriverObserver::NON_TOUCH_16_DIP);
778 handler->FetchFavicon(kPageURL, /*is_same_document=*/false);
779 base::RunLoop().RunUntilIdle();
780 }
781
782 // Test that CloneFaviconMappingsForPages() is called when there is data in the
783 // database for the page URL, for the case where multiple page URLs exist due to
784 // a quick in-same-document navigation (e.g. fragment navigation).
785 // FaviconService should be told to propagate the mappings from the last page
786 // URL (lookup hit) to the rest of the URLs.
TEST_F(FaviconHandlerTest,CloneFaviconMappingsWithMultipleURLs)787 TEST_F(FaviconHandlerTest, CloneFaviconMappingsWithMultipleURLs) {
788 const GURL kPageURLInHistory = GURL("http://www.google.com/other");
789
790 favicon_service_.fake()->Store(kPageURLInHistory, kIconURL16x16,
791 CreateRawBitmapResult(kIconURL16x16));
792
793 std::unique_ptr<FaviconHandler> handler = std::make_unique<FaviconHandler>(
794 &favicon_service_, &delegate_, FaviconDriverObserver::NON_TOUCH_16_DIP);
795 handler->FetchFavicon(kPageURL, /*is_same_document=*/false);
796 base::RunLoop().RunUntilIdle();
797
798 EXPECT_CALL(favicon_service_,
799 CloneFaviconMappingsForPages(
800 kPageURLInHistory, favicon_base::IconTypeSet({kFavicon}),
801 base::flat_set<GURL>({kPageURL, kPageURLInHistory})));
802
803 handler->FetchFavicon(kPageURLInHistory, /*is_same_document=*/true);
804 base::RunLoop().RunUntilIdle();
805 }
806
807 // Test that CloneFaviconMappingsForPages() is not called for incognito tabs.
TEST_F(FaviconHandlerTest,NotCloneFaviconMappingsInIncognito)808 TEST_F(FaviconHandlerTest, NotCloneFaviconMappingsInIncognito) {
809 ON_CALL(delegate_, IsOffTheRecord()).WillByDefault(Return(true));
810
811 favicon_service_.fake()->Store(kPageURL, kIconURL16x16,
812 CreateRawBitmapResult(kIconURL16x16));
813
814 EXPECT_CALL(favicon_service_, CloneFaviconMappingsForPages(_, _, _)).Times(0);
815
816 std::unique_ptr<FaviconHandler> handler = std::make_unique<FaviconHandler>(
817 &favicon_service_, &delegate_, FaviconDriverObserver::NON_TOUCH_16_DIP);
818 handler->FetchFavicon(kPageURL, /*is_same_document=*/false);
819 base::RunLoop().RunUntilIdle();
820 }
821
822 // Test that the FaviconHandler process finishes when:
823 // - There is data in the database for neither the page URL nor the icon URL.
824 // AND
825 // - FaviconService::GetFaviconForPageURL() callback returns before
826 // FaviconHandler::OnUpdateCandidates() is called.
TEST_F(FaviconHandlerTest,DownloadUnknownFaviconIfCandidatesSlower)827 TEST_F(FaviconHandlerTest, DownloadUnknownFaviconIfCandidatesSlower) {
828 // Defer the database lookup completion to control the exact timing.
829 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
830
831 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
832 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0);
833
834 FaviconHandler handler(&favicon_service_, &delegate_,
835 FaviconDriverObserver::NON_TOUCH_16_DIP);
836 handler.FetchFavicon(kPageURL, /*is_same_document=*/false);
837 base::RunLoop().RunUntilIdle();
838 // Database lookup for |kPageURL| is ongoing.
839 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
840 // Causes FaviconService lookups be faster than OnUpdateCandidates().
841 ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
842 ASSERT_TRUE(VerifyAndClearExpectations());
843
844 EXPECT_CALL(favicon_service_,
845 SetFavicons(base::flat_set<GURL>{kPageURL}, kIconURL16x16,
846 kFavicon, ImageSizeIs(16, 16)));
847 EXPECT_CALL(delegate_, OnFaviconUpdated(
848 kPageURL, FaviconDriverObserver::NON_TOUCH_16_DIP,
849 kIconURL16x16, /*icon_url_changed=*/true, _));
850 // Feed in favicons now that the database lookup is completed.
851 handler.OnUpdateCandidates(
852 kPageURL, {FaviconURL(kIconURL16x16, kFavicon, kEmptySizes)}, GURL());
853 base::RunLoop().RunUntilIdle();
854
855 EXPECT_THAT(favicon_service_.fake()->db_requests(),
856 ElementsAre(kIconURL16x16));
857 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL16x16));
858 }
859
860 // Test that the FaviconHandler process finishes when:
861 // - There is data in the database for neither the page URL nor the icon URL.
862 // AND
863 // - FaviconService::GetFaviconForPageURL() callback returns after
864 // FaviconHandler::OnUpdateCandidates() is called.
TEST_F(FaviconHandlerTest,DownloadUnknownFaviconIfCandidatesFaster)865 TEST_F(FaviconHandlerTest, DownloadUnknownFaviconIfCandidatesFaster) {
866 // Defer the database lookup completion to control the exact timing.
867 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
868
869 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
870 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0);
871
872 FaviconHandler handler(&favicon_service_, &delegate_,
873 FaviconDriverObserver::NON_TOUCH_16_DIP);
874 handler.FetchFavicon(kPageURL, /*is_same_document=*/false);
875 base::RunLoop().RunUntilIdle();
876 // Feed in favicons before completing the database lookup.
877 handler.OnUpdateCandidates(
878 kPageURL, {FaviconURL(kIconURL16x16, kFavicon, kEmptySizes)}, GURL());
879
880 ASSERT_TRUE(VerifyAndClearExpectations());
881 // Database lookup for |kPageURL| is ongoing.
882 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
883
884 EXPECT_CALL(favicon_service_,
885 SetFavicons(base::flat_set<GURL>{kPageURL}, kIconURL16x16,
886 kFavicon, ImageSizeIs(16, 16)));
887 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL16x16, _, _));
888
889 // Complete the lookup for |kPageURL|.
890 ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
891 base::RunLoop().RunUntilIdle();
892
893 EXPECT_THAT(favicon_service_.fake()->db_requests(),
894 ElementsAre(kIconURL16x16));
895 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL16x16));
896 }
897
898 // Test that the FaviconHandler process does not save anything to the database
899 // for incognito tabs.
TEST_F(FaviconHandlerTest,DownloadUnknownFaviconInIncognito)900 TEST_F(FaviconHandlerTest, DownloadUnknownFaviconInIncognito) {
901 ON_CALL(delegate_, IsOffTheRecord()).WillByDefault(Return(true));
902
903 // No writes expected.
904 EXPECT_CALL(favicon_service_, UpdateFaviconMappingsAndFetch(_, _, _, _, _, _))
905 .Times(0);
906 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
907
908 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL16x16, _, _));
909
910 RunHandlerWithSimpleFaviconCandidates({kIconURL16x16});
911 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL16x16));
912 EXPECT_THAT(favicon_service_.fake()->db_requests(),
913 ElementsAre(kPageURL, kIconURL16x16));
914 }
915
916 // Test that favicon mappings are not deleted in incognito even if the page
917 // lists no candidates.
TEST_F(FaviconHandlerTest,DoNotDeleteFaviconMappingsInIncognito)918 TEST_F(FaviconHandlerTest, DoNotDeleteFaviconMappingsInIncognito) {
919 const GURL kIconURL("http://www.google.com/favicon");
920
921 ON_CALL(delegate_, IsOffTheRecord()).WillByDefault(Return(true));
922 favicon_service_.fake()->Store(kPageURL, kIconURL,
923 CreateRawBitmapResult(kIconURL));
924
925 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
926
927 EXPECT_CALL(
928 delegate_,
929 OnFaviconDeleted(kPageURL, FaviconDriverObserver::NON_TOUCH_16_DIP));
930
931 RunHandlerWithSimpleFaviconCandidates(URLVector());
932 }
933
934 // Test that the icon is redownloaded if the icon cached for the page URL
935 // expired.
TEST_F(FaviconHandlerTest,RedownloadExpiredPageUrlFavicon)936 TEST_F(FaviconHandlerTest, RedownloadExpiredPageUrlFavicon) {
937 const GURL kIconURL("http://www.google.com/favicon");
938 const SkColor kOldColor = SK_ColorBLUE;
939 const SkColor kNewColor = SK_ColorGREEN;
940
941 favicon_service_.fake()->Store(
942 kPageURL, kIconURL,
943 CreateRawBitmapResult(kIconURL, kFavicon, /*expired=*/true,
944 gfx::kFaviconSize, kOldColor));
945
946 delegate_.fake_image_downloader().Add(kIconURL, IntVector{gfx::kFaviconSize},
947 kNewColor);
948
949 EXPECT_CALL(favicon_service_,
950 SetFavicons(_, kIconURL, _, ImageColorIs(kNewColor)));
951
952 InSequence seq;
953 EXPECT_CALL(delegate_,
954 OnFaviconUpdated(_, _, kIconURL, _, ImageColorIs(kOldColor)));
955 EXPECT_CALL(delegate_,
956 OnFaviconUpdated(_, _, kIconURL, _, ImageColorIs(kNewColor)));
957
958 RunHandlerWithSimpleFaviconCandidates({kIconURL});
959 // We know from the |kPageUrl| database request that |kIconURL| has expired. A
960 // second request for |kIconURL| should not have been made because it is
961 // redundant.
962 EXPECT_THAT(favicon_service_.fake()->db_requests(), ElementsAre(kPageURL));
963 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL));
964 }
965
966 // Test that FaviconHandler requests the new data when:
967 // - There is valid data in the database for the page URL.
968 // AND
969 // - The icon URL used by the page has changed.
970 // AND
971 // - There is no data in database for the new icon URL.
TEST_F(FaviconHandlerTest,UpdateAndDownloadFavicon)972 TEST_F(FaviconHandlerTest, UpdateAndDownloadFavicon) {
973 const GURL kOldIconURL("http://www.google.com/old_favicon");
974 const GURL kNewIconURL = kIconURL16x16;
975
976 favicon_service_.fake()->Store(kPageURL, kOldIconURL,
977 CreateRawBitmapResult(kOldIconURL));
978
979 InSequence seq;
980 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kOldIconURL, _, _));
981 EXPECT_CALL(favicon_service_, SetFavicons(_, kNewIconURL, _, _));
982 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kNewIconURL, _, _));
983
984 RunHandlerWithSimpleFaviconCandidates({kNewIconURL});
985 EXPECT_THAT(delegate_.downloads(), ElementsAre(kNewIconURL));
986 }
987
988 // If there is data for the page URL in history which is invalid, test that:
989 // - The invalid data is not sent to the UI.
990 // - The icon is redownloaded.
TEST_F(FaviconHandlerTest,FaviconInHistoryInvalid)991 TEST_F(FaviconHandlerTest, FaviconInHistoryInvalid) {
992 const GURL kIconURL("http://www.google.com/favicon");
993
994 delegate_.fake_image_downloader().Add(kIconURL, IntVector{gfx::kFaviconSize},
995 SK_ColorBLUE);
996
997 // Set non-empty but invalid data.
998 std::vector<FaviconRawBitmapResult> bitmap_result =
999 CreateRawBitmapResult(kIconURL);
1000 // Empty bitmap data is invalid.
1001 bitmap_result[0].bitmap_data = new base::RefCountedBytes();
1002
1003 favicon_service_.fake()->Store(kPageURL, kIconURL, bitmap_result);
1004
1005 EXPECT_CALL(delegate_,
1006 OnFaviconUpdated(_, _, kIconURL, _, ImageColorIs(SK_ColorBLUE)));
1007
1008 RunHandlerWithSimpleFaviconCandidates({kIconURL});
1009
1010 EXPECT_THAT(favicon_service_.fake()->db_requests(), ElementsAre(kPageURL));
1011 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL));
1012 }
1013
1014 // Test that no downloads are done if a user visits a page which changed its
1015 // favicon URL to a favicon URL which is already cached in the database.
TEST_F(FaviconHandlerTest,UpdateFavicon)1016 TEST_F(FaviconHandlerTest, UpdateFavicon) {
1017 const GURL kSomePreviousPageURL("https://www.google.com/previous");
1018 const GURL kIconURL("http://www.google.com/favicon");
1019 const GURL kNewIconURL("http://www.google.com/new_favicon");
1020
1021 favicon_service_.fake()->Store(kPageURL, kIconURL,
1022 CreateRawBitmapResult(kIconURL));
1023 favicon_service_.fake()->Store(kSomePreviousPageURL, kNewIconURL,
1024 CreateRawBitmapResult(kNewIconURL));
1025
1026 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
1027
1028 InSequence seq;
1029 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL, _, _));
1030 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kNewIconURL, _, _));
1031
1032 RunHandlerWithSimpleFaviconCandidates({kNewIconURL});
1033 EXPECT_THAT(favicon_service_.fake()->db_requests(),
1034 ElementsAre(kPageURL, kNewIconURL));
1035 EXPECT_THAT(delegate_.downloads(), IsEmpty());
1036 }
1037
TEST_F(FaviconHandlerTest,Download2ndFaviconURLCandidate)1038 TEST_F(FaviconHandlerTest, Download2ndFaviconURLCandidate) {
1039 const GURL kIconURLReturning500("http://www.google.com/500.png");
1040
1041 delegate_.fake_image_downloader().AddError(kIconURLReturning500, 500);
1042
1043 favicon_service_.fake()->Store(
1044 kPageURL, kIconURL64x64,
1045 CreateRawBitmapResult(kIconURL64x64, kTouchIcon,
1046 /*expired=*/true));
1047
1048 EXPECT_CALL(delegate_,
1049 OnFaviconUpdated(kPageURL, FaviconDriverObserver::TOUCH_LARGEST,
1050 kIconURL64x64, /*icon_url_changed=*/true, _));
1051 EXPECT_CALL(delegate_,
1052 OnFaviconUpdated(kPageURL, FaviconDriverObserver::TOUCH_LARGEST,
1053 kIconURL64x64, /*icon_url_changed=*/false, _));
1054
1055 RunHandlerWithCandidates(
1056 FaviconDriverObserver::TOUCH_LARGEST,
1057 {
1058 FaviconURL(kIconURLReturning500, kTouchPrecomposedIcon, kEmptySizes),
1059 FaviconURL(kIconURL64x64, kTouchIcon, kEmptySizes),
1060 });
1061 // First download fails, second succeeds.
1062 EXPECT_THAT(delegate_.downloads(),
1063 ElementsAre(kIconURLReturning500, kIconURL64x64));
1064 }
1065
1066 // Test that download data for icon URLs other than the current favicon
1067 // candidate URLs is ignored. This test tests the scenario where a download is
1068 // in flight when FaviconHandler::OnUpdateCandidates() is called.
1069 // TODO(mastiz): Make this test deal with FaviconURLs of type
1070 // favicon_base::IconType::kFavicon and add new ones like
1071 // OnlyDownloadMatchingIconType and CallSetFaviconsWithCorrectIconType.
TEST_F(FaviconHandlerTest,UpdateDuringDownloading)1072 TEST_F(FaviconHandlerTest, UpdateDuringDownloading) {
1073 const GURL kIconURL1("http://www.google.com/favicon");
1074 const GURL kIconURL2 = kIconURL16x16;
1075 const GURL kIconURL3 = kIconURL12x12;
1076
1077 // Defer the download completion such that RunUntilIdle() doesn't complete
1078 // the download.
1079 delegate_.fake_image_downloader().SetRunCallbackManuallyForUrl(kIconURL1);
1080
1081 delegate_.fake_image_downloader().Add(kIconURL1, IntVector{16});
1082 delegate_.fake_image_downloader().Add(kIconURL3, IntVector{12});
1083
1084 std::unique_ptr<FaviconHandler> handler =
1085 RunHandlerWithSimpleFaviconCandidates({kIconURL1, kIconURL2});
1086
1087 ASSERT_TRUE(VerifyAndClearExpectations());
1088 ASSERT_TRUE(delegate_.fake_image_downloader().HasPendingManualCallback());
1089
1090 // Favicon update should invalidate the ongoing download.
1091 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL3, _, _));
1092 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL3, _, _));
1093
1094 handler->OnUpdateCandidates(
1095 kPageURL, {FaviconURL(kIconURL3, kFavicon, kEmptySizes)}, GURL());
1096
1097 // Finalizes download, which should be thrown away as the favicon URLs were
1098 // updated.
1099 EXPECT_TRUE(delegate_.fake_image_downloader().RunCallbackManually());
1100 base::RunLoop().RunUntilIdle();
1101
1102 EXPECT_THAT(favicon_service_.fake()->db_requests(), ElementsAre(kIconURL3));
1103 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL3));
1104 }
1105
1106 // Test that sending an icon URL update different to the previous icon URL
1107 // update during a database lookup ignores the first icon URL and processes the
1108 // second.
TEST_F(FaviconHandlerTest,UpdateDuringDatabaseLookup)1109 TEST_F(FaviconHandlerTest, UpdateDuringDatabaseLookup) {
1110 const GURL kIconURL1 = kIconURL16x16;
1111 const GURL kIconURL2 = kIconURL12x12;
1112
1113 // Defer the lookup completion such that RunUntilIdle() doesn't complete the
1114 // lookup.
1115 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kIconURL1);
1116
1117 delegate_.fake_image_downloader().Add(kIconURL1, IntVector{16});
1118 delegate_.fake_image_downloader().Add(kIconURL2, IntVector{12});
1119
1120 std::unique_ptr<FaviconHandler> handler =
1121 RunHandlerWithSimpleFaviconCandidates(URLVector{kIconURL1});
1122
1123 ASSERT_TRUE(VerifyAndClearExpectations());
1124 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
1125
1126 // SetFavicons() and OnFaviconUpdated() should be called for the new icon URL
1127 // and not |kIconURL1|.
1128 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL2, _, _));
1129 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL2, _, _));
1130
1131 handler->OnUpdateCandidates(
1132 kPageURL, {FaviconURL(kIconURL2, kFavicon, kEmptySizes)}, GURL());
1133
1134 // Finalizes the DB lookup, which should be thrown away as the favicon URLs
1135 // were updated.
1136 EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
1137 base::RunLoop().RunUntilIdle();
1138
1139 EXPECT_THAT(favicon_service_.fake()->db_requests(), ElementsAre(kIconURL2));
1140 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL2));
1141 }
1142
1143 // Test that sending an icon URL update identical to the previous icon URL
1144 // update during image download is a no-op.
TEST_F(FaviconHandlerTest,UpdateSameIconURLsWhileDownloadingShouldBeNoop)1145 TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileDownloadingShouldBeNoop) {
1146 const GURL kSlowLoadingIconURL("http://www.google.com/slow_favicon");
1147
1148 const std::vector<FaviconURL> favicon_urls = {
1149 FaviconURL(kIconURL12x12, kFavicon, kEmptySizes),
1150 FaviconURL(kSlowLoadingIconURL, kFavicon, kEmptySizes),
1151 };
1152
1153 // Defer the download completion such that RunUntilIdle() doesn't complete
1154 // the download.
1155 delegate_.fake_image_downloader().SetRunCallbackManuallyForUrl(
1156 kSlowLoadingIconURL);
1157 delegate_.fake_image_downloader().Add(kSlowLoadingIconURL, IntVector{16});
1158
1159 std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates(
1160 FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls);
1161
1162 ASSERT_THAT(favicon_service_.fake()->db_requests(),
1163 ElementsAre(kPageURL, kIconURL12x12, kSlowLoadingIconURL));
1164 ASSERT_TRUE(VerifyAndClearExpectations());
1165 ASSERT_TRUE(delegate_.fake_image_downloader().HasPendingManualCallback());
1166
1167 // Calling OnUpdateCandidates() with the same icon URLs should have no effect,
1168 // despite the ongoing download.
1169 handler->OnUpdateCandidates(kPageURL, favicon_urls, GURL());
1170 base::RunLoop().RunUntilIdle();
1171 EXPECT_THAT(favicon_service_.fake()->db_requests(), IsEmpty());
1172 EXPECT_THAT(delegate_.downloads(), IsEmpty());
1173
1174 // Complete the download.
1175 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _));
1176 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _));
1177 EXPECT_TRUE(delegate_.fake_image_downloader().RunCallbackManually());
1178 base::RunLoop().RunUntilIdle();
1179 EXPECT_THAT(delegate_.downloads(), IsEmpty());
1180 }
1181
1182 // Test that sending an icon URL update identical to the previous icon URL
1183 // update during a database lookup is a no-op.
TEST_F(FaviconHandlerTest,UpdateSameIconURLsWhileDatabaseLookupShouldBeNoop)1184 TEST_F(FaviconHandlerTest, UpdateSameIconURLsWhileDatabaseLookupShouldBeNoop) {
1185 const std::vector<FaviconURL> favicon_urls = {
1186 FaviconURL(kIconURL12x12, kFavicon, kEmptySizes),
1187 };
1188
1189 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kIconURL12x12);
1190
1191 std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates(
1192 FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls);
1193
1194 // Ongoing database lookup.
1195 ASSERT_THAT(favicon_service_.fake()->db_requests(),
1196 ElementsAre(kPageURL, kIconURL12x12));
1197 ASSERT_THAT(delegate_.downloads(), IsEmpty());
1198 ASSERT_TRUE(VerifyAndClearExpectations());
1199 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
1200
1201 // Calling OnUpdateCandidates() with the same icon URLs should have no effect,
1202 // despite the ongoing DB lookup.
1203 handler->OnUpdateCandidates(kPageURL, favicon_urls, GURL());
1204 base::RunLoop().RunUntilIdle();
1205 EXPECT_THAT(favicon_service_.fake()->db_requests(), IsEmpty());
1206 EXPECT_THAT(delegate_.downloads(), IsEmpty());
1207
1208 // Complete the lookup.
1209 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _));
1210 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _));
1211 EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
1212 base::RunLoop().RunUntilIdle();
1213 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL12x12));
1214 }
1215
1216 // Test that calling OnUpdateFaviconUrl() with the same icon URLs as before is a
1217 // no-op. This is important because OnUpdateFaviconUrl() is called when the page
1218 // finishes loading. This can occur several times for pages with iframes.
TEST_F(FaviconHandlerTest,UpdateSameIconURLsAfterFinishedShouldBeNoop)1219 TEST_F(FaviconHandlerTest, UpdateSameIconURLsAfterFinishedShouldBeNoop) {
1220 const std::vector<FaviconURL> favicon_urls = {
1221 FaviconURL(kIconURL10x10, kFavicon, kEmptySizes),
1222 FaviconURL(kIconURL16x16, kFavicon, kEmptySizes),
1223 };
1224
1225 std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates(
1226 FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls);
1227
1228 ASSERT_TRUE(VerifyAndClearExpectations());
1229
1230 // Calling OnUpdateCandidates() with identical data should be a no-op.
1231 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0);
1232 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
1233
1234 handler->OnUpdateCandidates(kPageURL, favicon_urls, GURL());
1235 base::RunLoop().RunUntilIdle();
1236 EXPECT_THAT(favicon_service_.fake()->db_requests(), IsEmpty());
1237 EXPECT_THAT(delegate_.downloads(), IsEmpty());
1238 }
1239
1240 // Fixes crbug.com/544560
1241 // Tests that Delegate::OnFaviconUpdated() is called if:
1242 // - The best icon on the initial page is not the last icon.
1243 // - All of the initial page's icons are downloaded.
1244 // AND
1245 // - JavaScript modifies the page's <link rel="icon"> tags to contain only the
1246 // last icon.
TEST_F(FaviconHandlerTest,OnFaviconAvailableNotificationSentAfterIconURLChange)1247 TEST_F(FaviconHandlerTest,
1248 OnFaviconAvailableNotificationSentAfterIconURLChange) {
1249 const GURL kIconURL1(
1250 "http://wwww.page_which_animates_favicon.com/frame1.png");
1251 const GURL kIconURL2(
1252 "http://wwww.page_which_animates_favicon.com/frame2.png");
1253
1254 // |kIconURL1| is the better match.
1255 delegate_.fake_image_downloader().Add(kIconURL1, IntVector{15});
1256 delegate_.fake_image_downloader().Add(kIconURL2, IntVector{10});
1257
1258 // Two FaviconDriver::OnFaviconUpdated() notifications should be sent for
1259 // |kIconURL1|, one before and one after the download.
1260 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL1, _, _));
1261
1262 std::unique_ptr<FaviconHandler> handler =
1263 RunHandlerWithSimpleFaviconCandidates({kIconURL1, kIconURL2});
1264
1265 // Both |kIconURL1| and |kIconURL2| should have been requested from the
1266 // database and downloaded. |kIconURL2| should have been fetched from the
1267 // database and downloaded last.
1268 ASSERT_THAT(delegate_.downloads(), ElementsAre(kIconURL1, kIconURL2));
1269 ASSERT_THAT(favicon_service_.fake()->db_requests(),
1270 ElementsAre(kPageURL, kIconURL1, kIconURL2));
1271 ASSERT_TRUE(VerifyAndClearExpectations());
1272
1273 // Simulate the page changing it's icon URL to just |kIconURL2| via
1274 // Javascript.
1275 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL2, _, _));
1276 handler->OnUpdateCandidates(
1277 kPageURL, {FaviconURL(kIconURL2, kFavicon, kEmptySizes)}, GURL());
1278 base::RunLoop().RunUntilIdle();
1279 }
1280
1281 // Test that favicon mappings are removed if the page initially lists a favicon
1282 // and later uses Javascript to remove it.
TEST_F(FaviconHandlerTest,RemoveFaviconViaJavascript)1283 TEST_F(FaviconHandlerTest, RemoveFaviconViaJavascript) {
1284 EXPECT_CALL(favicon_service_, SetFavicons(base::flat_set<GURL>{kPageURL},
1285 kIconURL16x16, kFavicon, _));
1286
1287 // Setup: the page initially lists a favicon.
1288 std::unique_ptr<FaviconHandler> handler =
1289 RunHandlerWithSimpleFaviconCandidates(URLVector{kIconURL16x16});
1290 ASSERT_TRUE(VerifyAndClearExpectations());
1291
1292 // Simulate the page removing its icon URL via Javascript.
1293 EXPECT_CALL(favicon_service_,
1294 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kFavicon));
1295 handler->OnUpdateCandidates(kPageURL, std::vector<FaviconURL>(), GURL());
1296 base::RunLoop().RunUntilIdle();
1297 }
1298
1299 // Tests that there is not crash and SetFavicons() is called with the
1300 // appropriate icon URL in the following scenario:
1301 // - The database initially has a cached but expired icon for the page.
1302 // - Initial favicon candidates are received fast, before the history lookup
1303 // completes.
1304 // - Before the history lookup completes, favicon candidates are updated via
1305 // javascript to include a different set of icons.
TEST_F(FaviconHandlerTest,UpdateIconsViaJavascriptAfterFastCandidatesAndExpiredIcon)1306 TEST_F(FaviconHandlerTest,
1307 UpdateIconsViaJavascriptAfterFastCandidatesAndExpiredIcon) {
1308 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
1309 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL64x64, _, _));
1310
1311 // Initial database contains a cached by expired icon for |kPageURL|.
1312 favicon_service_.fake()->Store(
1313 kPageURL, kIconURL16x16,
1314 CreateRawBitmapResult(kIconURL16x16, kTouchIcon, /*expired=*/true));
1315
1316 // Initial candidates are received before the history lookup for |kPageURL| is
1317 // finished.
1318 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
1319 std::unique_ptr<FaviconHandler> handler =
1320 RunHandlerWithSimpleFaviconCandidates(URLVector{kIconURL16x16});
1321 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
1322 // Update candidates, now containing a different set of icons.
1323 handler->OnUpdateCandidates(
1324 kPageURL, {FaviconURL(kIconURL64x64, kFavicon, kEmptySizes)},
1325 /*manifest_url=*/GURL());
1326 base::RunLoop().RunUntilIdle();
1327 // Complete the history lookup for |kPageURL| now.
1328 ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
1329 base::RunLoop().RunUntilIdle();
1330
1331 EXPECT_THAT(favicon_service_.fake()->db_requests(), ElementsAre(kPageURL));
1332 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL64x64));
1333 }
1334
1335 // Test the favicon which is selected when the web page provides several
1336 // favicons and none of the favicons are cached in history.
1337 // The goal of this test is to be more of an integration test than
1338 // SelectFaviconFramesTest.*.
1339 class FaviconHandlerMultipleFaviconsTest : public FaviconHandlerTest {
1340 protected:
FaviconHandlerMultipleFaviconsTest()1341 FaviconHandlerMultipleFaviconsTest() {
1342 // Set the supported scale factors to 1x and 2x. This affects the behavior
1343 // of SelectFaviconFrames().
1344 scoped_set_supported_scale_factors_.reset(); // Need to delete first.
1345 scoped_set_supported_scale_factors_.reset(
1346 new ui::test::ScopedSetSupportedScaleFactors(
1347 {ui::SCALE_FACTOR_100P, ui::SCALE_FACTOR_200P}));
1348 }
1349
1350 // Simulates requesting a favicon for |page_url| given:
1351 // - We have not previously cached anything in history for |page_url| or for
1352 // any of candidates.
1353 // - The page provides favicons with edge pixel sizes of
1354 // |candidate_icon_sizes|.
1355 // - Candidates are assumed of type kFavicon and the URLs are generated
1356 // internally for testing purposes.
1357 //
1358 // Returns the chosen size among |candidate_icon_sizes| or -1 if none was
1359 // chosen.
DownloadTillDoneIgnoringHistory(const IntVector & candidate_icon_sizes)1360 int DownloadTillDoneIgnoringHistory(const IntVector& candidate_icon_sizes) {
1361 std::vector<FaviconURL> candidate_icons;
1362 int chosen_icon_size = -1;
1363
1364 for (int icon_size : candidate_icon_sizes) {
1365 const GURL icon_url(base::StringPrintf(
1366 "https://www.google.com/generated/%dx%d", icon_size, icon_size));
1367 // Set up 200 responses for all images, and the corresponding size.
1368 delegate_.fake_image_downloader().Add(icon_url, IntVector{icon_size});
1369 // Create test candidates of type kFavicon and a fake URL.
1370 candidate_icons.emplace_back(icon_url, kFavicon, kEmptySizes);
1371
1372 ON_CALL(delegate_, OnFaviconUpdated(_, _, icon_url, _, _))
1373 .WillByDefault(Assign(&chosen_icon_size, icon_size));
1374 }
1375
1376 RunHandlerWithCandidates(FaviconDriverObserver::NON_TOUCH_16_DIP,
1377 candidate_icons);
1378 return chosen_icon_size;
1379 }
1380 };
1381
1382 // Tests that running FaviconHandler
1383 // - On an OS which supports the 1x and 2x scale factor
1384 // - On a page with <link rel="icon"> tags with no "sizes" information.
1385 // Selects the largest exact match. Note that a 32x32 PNG image is not a "true
1386 // exact match" on an OS which supports an 1x and 2x. A "true exact match" is
1387 // a .ico file with 16x16 and 32x32 bitmaps.
TEST_F(FaviconHandlerMultipleFaviconsTest,ChooseLargestExactMatch)1388 TEST_F(FaviconHandlerMultipleFaviconsTest, ChooseLargestExactMatch) {
1389 EXPECT_EQ(32,
1390 DownloadTillDoneIgnoringHistory(IntVector{16, 24, 32, 48, 256}));
1391 }
1392
1393 // Test that if there are several single resolution favicons to choose
1394 // from, the exact match is preferred even if it results in upsampling.
TEST_F(FaviconHandlerMultipleFaviconsTest,ChooseExactMatchDespiteUpsampling)1395 TEST_F(FaviconHandlerMultipleFaviconsTest, ChooseExactMatchDespiteUpsampling) {
1396 EXPECT_EQ(16, DownloadTillDoneIgnoringHistory(IntVector{16, 24, 48, 256}));
1397 }
1398
1399 // Test that favicons which need to be upsampled a little or downsampled
1400 // a little are preferred over huge favicons.
TEST_F(FaviconHandlerMultipleFaviconsTest,ChooseMinorDownsamplingOverHugeIcon)1401 TEST_F(FaviconHandlerMultipleFaviconsTest,
1402 ChooseMinorDownsamplingOverHugeIcon) {
1403 EXPECT_EQ(48, DownloadTillDoneIgnoringHistory(IntVector{256, 48}));
1404 }
1405
TEST_F(FaviconHandlerMultipleFaviconsTest,ChooseMinorUpsamplingOverHugeIcon)1406 TEST_F(FaviconHandlerMultipleFaviconsTest, ChooseMinorUpsamplingOverHugeIcon) {
1407 EXPECT_EQ(17, DownloadTillDoneIgnoringHistory(IntVector{17, 256}));
1408 }
1409
1410 // Test a page with multiple favicon candidates with explicit sizes information.
1411 // Only the best one should be downloaded.
TEST_F(FaviconHandlerMultipleFaviconsTest,StopsDownloadingWhenRemainingCandidatesWorse)1412 TEST_F(FaviconHandlerMultipleFaviconsTest,
1413 StopsDownloadingWhenRemainingCandidatesWorse) {
1414 RunHandlerWithCandidates(FaviconDriverObserver::NON_TOUCH_16_DIP,
1415 {
1416 FaviconURL(kIconURL16x16, kFavicon,
1417 SizeVector(1U, gfx::Size(16, 16))),
1418 FaviconURL(kIconURL64x64, kFavicon,
1419 SizeVector(1U, gfx::Size(64, 64))),
1420 });
1421
1422 EXPECT_THAT(delegate_.downloads(), SizeIs(1));
1423 }
1424
1425 // Mostly for behavioral documentation purposes: test that downloads stops when
1426 // remaining candidates are worse or equal, for the following advanced scenario:
1427 // - The page provides multiple favicons: various with explicit sizes
1428 // information and one without.
1429 // - Among the ones with explicit sizes information, downloading the best
1430 // returns a 404.
1431 // - The remaining ones (with explicit sizes information) are worse than the one
1432 // without sizes information, and shouldn't be downloaded.
TEST_F(FaviconHandlerTest,StopsDownloadingWhenRemainingCandidatesWorseDespite404)1433 TEST_F(FaviconHandlerTest,
1434 StopsDownloadingWhenRemainingCandidatesWorseDespite404) {
1435 const GURL k404IconURL("http://www.google.com/404.png");
1436 const GURL kIconURL192x192 = GURL("http://www.google.com/favicon192x192");
1437
1438 RunHandlerWithCandidates(
1439 FaviconDriverObserver::NON_TOUCH_16_DIP,
1440 {
1441 FaviconURL(kIconURL64x64, kFavicon, kEmptySizes),
1442 FaviconURL(k404IconURL, kFavicon, SizeVector(1U, gfx::Size(32, 32))),
1443 FaviconURL(kIconURL192x192, kFavicon,
1444 SizeVector(1U, gfx::Size(192, 192))),
1445 });
1446
1447 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL64x64, k404IconURL));
1448 }
1449
TEST_F(FaviconHandlerMultipleFaviconsTest,DownloadsAllIconsWithoutSizesAttributeIfNotWantsLargest)1450 TEST_F(FaviconHandlerMultipleFaviconsTest,
1451 DownloadsAllIconsWithoutSizesAttributeIfNotWantsLargest) {
1452 RunHandlerWithCandidates(FaviconDriverObserver::NON_TOUCH_16_DIP,
1453 {
1454 FaviconURL(kIconURL16x16, kFavicon, kEmptySizes),
1455 FaviconURL(kIconURL64x64, kFavicon, kEmptySizes),
1456 });
1457
1458 EXPECT_THAT(delegate_.downloads(), SizeIs(2));
1459 }
1460
TEST_F(FaviconHandlerMultipleFaviconsTest,DownloadsOnlyOneIconWithoutSizesAttributeIfWantsLargest)1461 TEST_F(FaviconHandlerMultipleFaviconsTest,
1462 DownloadsOnlyOneIconWithoutSizesAttributeIfWantsLargest) {
1463 RunHandlerWithCandidates(FaviconDriverObserver::NON_TOUCH_LARGEST,
1464 {
1465 FaviconURL(kIconURL16x16, kFavicon, kEmptySizes),
1466 FaviconURL(kIconURL64x64, kFavicon, kEmptySizes),
1467 });
1468
1469 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL16x16));
1470 }
1471
TEST_F(FaviconHandlerTest,Report404)1472 TEST_F(FaviconHandlerTest, Report404) {
1473 const GURL k404IconURL("http://www.google.com/404.png");
1474
1475 EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(k404IconURL));
1476
1477 RunHandlerWithSimpleFaviconCandidates({k404IconURL});
1478 EXPECT_THAT(delegate_.downloads(), ElementsAre(k404IconURL));
1479 }
1480
1481 // Test that WasUnableToDownloadFavicon() is not called if a download returns
1482 // HTTP status 503.
TEST_F(FaviconHandlerTest,NotReport503)1483 TEST_F(FaviconHandlerTest, NotReport503) {
1484 const GURL k503IconURL("http://www.google.com/503.png");
1485
1486 delegate_.fake_image_downloader().AddError(k503IconURL, 503);
1487
1488 EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(_)).Times(0);
1489
1490 RunHandlerWithSimpleFaviconCandidates({k503IconURL});
1491 EXPECT_THAT(delegate_.downloads(), ElementsAre(k503IconURL));
1492 }
1493
1494 // Test that the best favicon is selected when:
1495 // - The page provides several favicons.
1496 // - Downloading one of the page's icon URLs previously returned a 404.
1497 // - None of the favicons are cached in the Favicons database.
TEST_F(FaviconHandlerTest,MultipleFavicons404)1498 TEST_F(FaviconHandlerTest, MultipleFavicons404) {
1499 const GURL k404IconURL("http://www.google.com/404.png");
1500
1501 ON_CALL(favicon_service_, WasUnableToDownloadFavicon(k404IconURL))
1502 .WillByDefault(Return(true));
1503
1504 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL12x12, _, _));
1505 RunHandlerWithSimpleFaviconCandidates({k404IconURL, kIconURL12x12});
1506 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL12x12));
1507 }
1508
1509 // Test that the best favicon is selected when:
1510 // - The page provides several favicons.
1511 // - Downloading the last page icon URL previously returned a 404.
1512 // - None of the favicons are cached in the Favicons database.
1513 // - All of the icons are downloaded because none of the icons have the ideal
1514 // size.
1515 // - The 404 icon is last.
TEST_F(FaviconHandlerTest,MultipleFaviconsLast404)1516 TEST_F(FaviconHandlerTest, MultipleFaviconsLast404) {
1517 const GURL k404IconURL("http://www.google.com/404.png");
1518
1519 ON_CALL(favicon_service_, WasUnableToDownloadFavicon(k404IconURL))
1520 .WillByDefault(Return(true));
1521
1522 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL12x12, _, _));
1523 RunHandlerWithSimpleFaviconCandidates({kIconURL12x12, k404IconURL});
1524 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL12x12));
1525 }
1526
1527 // Test that no favicon is selected when:
1528 // - The page provides several favicons.
1529 // - Downloading the page's icons has previously returned a 404.
1530 // - None of the favicons are cached in the Favicons database.
TEST_F(FaviconHandlerTest,MultipleFaviconsAll404)1531 TEST_F(FaviconHandlerTest, MultipleFaviconsAll404) {
1532 const GURL k404IconURL1("http://www.google.com/a/404.png");
1533 const GURL k404IconURL2("http://www.google.com/b/404.png");
1534
1535 ON_CALL(favicon_service_, WasUnableToDownloadFavicon(k404IconURL1))
1536 .WillByDefault(Return(true));
1537 ON_CALL(favicon_service_, WasUnableToDownloadFavicon(k404IconURL2))
1538 .WillByDefault(Return(true));
1539
1540 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0);
1541 RunHandlerWithSimpleFaviconCandidates({k404IconURL1, k404IconURL2});
1542 EXPECT_THAT(delegate_.downloads(), IsEmpty());
1543 }
1544
1545 // Test that favicon mappings are removed if the page initially lists a favicon
1546 // and later uses Javascript to change it to another icon that returns a 404.
TEST_F(FaviconHandlerTest,ChangeFaviconViaJavascriptTo404)1547 TEST_F(FaviconHandlerTest, ChangeFaviconViaJavascriptTo404) {
1548 const GURL k404IconURL("http://www.google.com/404.png");
1549
1550 EXPECT_CALL(favicon_service_, SetFavicons(base::flat_set<GURL>{kPageURL},
1551 kIconURL16x16, kFavicon, _));
1552
1553 // Setup: the page initially lists a favicon.
1554 std::unique_ptr<FaviconHandler> handler =
1555 RunHandlerWithSimpleFaviconCandidates(URLVector{kIconURL16x16});
1556 ASSERT_TRUE(VerifyAndClearExpectations());
1557
1558 // Simulate the page changing its icon URL via Javascript, using a URL that
1559 // returns a 404 (the most likely scenario for this is the implicit
1560 // /favicon.ico path that the site didn't actually list).
1561 EXPECT_CALL(favicon_service_,
1562 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kFavicon));
1563 handler->OnUpdateCandidates(
1564 kPageURL, {FaviconURL(k404IconURL, kFavicon, kEmptySizes)}, GURL());
1565 base::RunLoop().RunUntilIdle();
1566 }
1567
1568 // Test that favicon mappings are not removed in incognito if the page initially
1569 // lists a favicon and later uses Javascript to change it to another icon that
1570 // returns a 404.
TEST_F(FaviconHandlerTest,ChangeFaviconViaJavascriptTo404InIncognito)1571 TEST_F(FaviconHandlerTest, ChangeFaviconViaJavascriptTo404InIncognito) {
1572 const GURL k404IconURL("http://www.google.com/404.png");
1573
1574 ON_CALL(delegate_, IsOffTheRecord()).WillByDefault(Return(true));
1575 favicon_service_.fake()->Store(kPageURL, kIconURL16x16,
1576 CreateRawBitmapResult(kIconURL16x16));
1577
1578 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
1579
1580 // Setup: the page initially lists a favicon.
1581 std::unique_ptr<FaviconHandler> handler =
1582 RunHandlerWithSimpleFaviconCandidates(URLVector{kIconURL16x16});
1583
1584 // Simulate the page changing its icon URL via Javascript, using a URL that
1585 // returns a 404 (the most likely scenario for this is the implicit
1586 // /favicon.ico path that the site didn't actually list).
1587 handler->OnUpdateCandidates(
1588 kPageURL, {FaviconURL(k404IconURL, kFavicon, kEmptySizes)}, GURL());
1589 base::RunLoop().RunUntilIdle();
1590 }
1591
1592 // Test that no favicon is selected when the page's only icon uses an invalid
1593 // URL syntax.
TEST_F(FaviconHandlerTest,FaviconInvalidURL)1594 TEST_F(FaviconHandlerTest, FaviconInvalidURL) {
1595 const GURL kInvalidFormatURL("invalid");
1596 ASSERT_TRUE(kInvalidFormatURL.is_empty());
1597
1598 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0);
1599
1600 RunHandlerWithSimpleFaviconCandidates({kInvalidFormatURL});
1601 EXPECT_THAT(delegate_.downloads(), IsEmpty());
1602 }
1603
TEST_F(FaviconHandlerTest,TestSortFavicon)1604 TEST_F(FaviconHandlerTest, TestSortFavicon) {
1605 // Names represent the bitmap sizes per icon.
1606 const GURL kIconURL1_17("http://www.google.com/a");
1607 const GURL kIconURL1024_512("http://www.google.com/b");
1608 const GURL kIconURL16_14("http://www.google.com/c");
1609 const GURL kIconURLWithoutSize1("http://www.google.com/d");
1610 const GURL kIconURLWithoutSize2("http://www.google.com/e");
1611
1612 const std::vector<favicon::FaviconURL> kSourceIconURLs{
1613 FaviconURL(kIconURL1_17, kFavicon, {gfx::Size(1, 1), gfx::Size(17, 17)}),
1614 FaviconURL(kIconURL1024_512, kFavicon,
1615 {gfx::Size(1024, 1024), gfx::Size(512, 512)}),
1616 FaviconURL(kIconURL16_14, kFavicon,
1617 {gfx::Size(16, 16), gfx::Size(14, 14)}),
1618 FaviconURL(kIconURLWithoutSize1, kFavicon, kEmptySizes),
1619 FaviconURL(kIconURLWithoutSize2, kFavicon, kEmptySizes)};
1620
1621 std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates(
1622 FaviconDriverObserver::NON_TOUCH_LARGEST, kSourceIconURLs);
1623
1624 EXPECT_THAT(
1625 handler->GetIconURLs(),
1626 ElementsAre(
1627 // The 512x512 bitmap is the best match for the desired size.
1628 kIconURL1024_512, kIconURL1_17, kIconURL16_14,
1629 // The rest of bitmaps come in order, there is no "sizes" attribute.
1630 kIconURLWithoutSize1, kIconURLWithoutSize2));
1631 }
1632
TEST_F(FaviconHandlerTest,TestSortTouchIconLargest)1633 TEST_F(FaviconHandlerTest, TestSortTouchIconLargest) {
1634 const GURL kIconURLWithoutSize("http://www.google.com/touchicon-nosize");
1635 const GURL kIconURL144x144("http://www.google.com/touchicon144x144");
1636 const GURL kIconURL192x192("http://www.google.com/touchicon192x192");
1637
1638 const std::vector<favicon::FaviconURL> kSourceIconURLs{
1639 FaviconURL(kIconURLWithoutSize, kTouchIcon, kEmptySizes),
1640 FaviconURL(kIconURL144x144, kTouchIcon,
1641 SizeVector(1U, gfx::Size(144, 144))),
1642 FaviconURL(kIconURL192x192, kTouchIcon,
1643 SizeVector(1U, gfx::Size(192, 192))),
1644 };
1645
1646 std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates(
1647 FaviconDriverObserver::TOUCH_LARGEST, kSourceIconURLs);
1648
1649 EXPECT_THAT(
1650 handler->GetIconURLs(),
1651 ElementsAre(kIconURL192x192, kIconURL144x144, kIconURLWithoutSize));
1652 }
1653
TEST_F(FaviconHandlerTest,TestDownloadLargestFavicon)1654 TEST_F(FaviconHandlerTest, TestDownloadLargestFavicon) {
1655 // Names represent the bitmap sizes per icon.
1656 const GURL kIconURL1024_512("http://www.google.com/a");
1657 const GURL kIconURL15_14("http://www.google.com/b");
1658 const GURL kIconURL16_512("http://www.google.com/c");
1659 const GURL kIconURLWithoutSize1("http://www.google.com/d");
1660 const GURL kIconURLWithoutSize2("http://www.google.com/e");
1661
1662 RunHandlerWithCandidates(
1663 FaviconDriverObserver::NON_TOUCH_LARGEST,
1664 {FaviconURL(kIconURL1024_512, kFavicon,
1665 {gfx::Size(1024, 1024), gfx::Size(512, 512)}),
1666 FaviconURL(kIconURL15_14, kFavicon,
1667 {gfx::Size(15, 15), gfx::Size(14, 14)}),
1668 FaviconURL(kIconURL16_512, kFavicon,
1669 {gfx::Size(16, 16), gfx::Size(512, 512)}),
1670 FaviconURL(kIconURLWithoutSize1, kFavicon, kEmptySizes),
1671 FaviconURL(kIconURLWithoutSize2, kFavicon, kEmptySizes)});
1672
1673 // Icon URLs are not registered and hence 404s will be produced, which
1674 // allows checking whether the icons were requested according to their size.
1675 // The favicons should have been requested in decreasing order of their sizes.
1676 // Favicons without any <link sizes=""> attribute should have been downloaded
1677 // last.
1678 EXPECT_THAT(delegate_.downloads(),
1679 ElementsAre(kIconURL1024_512, kIconURL16_512, kIconURL15_14,
1680 kIconURLWithoutSize1, kIconURLWithoutSize2));
1681 }
1682
TEST_F(FaviconHandlerTest,TestSelectLargestFavicon)1683 TEST_F(FaviconHandlerTest, TestSelectLargestFavicon) {
1684 const GURL kIconURL1("http://www.google.com/b");
1685 const GURL kIconURL2("http://www.google.com/c");
1686
1687 delegate_.fake_image_downloader().Add(kIconURL1, IntVector{15});
1688 delegate_.fake_image_downloader().Add(kIconURL2, IntVector{14, 16});
1689
1690 // Verify NotifyFaviconAvailable().
1691 EXPECT_CALL(delegate_,
1692 OnFaviconUpdated(_, FaviconDriverObserver::NON_TOUCH_LARGEST,
1693 kIconURL2, _, _));
1694
1695 RunHandlerWithCandidates(
1696 FaviconDriverObserver::NON_TOUCH_LARGEST,
1697 {FaviconURL(kIconURL1, kFavicon, {gfx::Size(15, 15)}),
1698 FaviconURL(kIconURL2, kFavicon,
1699 {gfx::Size(14, 14), gfx::Size(16, 16)})});
1700
1701 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL2));
1702 }
1703
TEST_F(FaviconHandlerTest,TestFaviconWasScaledAfterDownload)1704 TEST_F(FaviconHandlerTest, TestFaviconWasScaledAfterDownload) {
1705 const int kMaximalSize = FaviconHandler::GetMaximalIconSize(
1706 FaviconDriverObserver::NON_TOUCH_LARGEST,
1707 /*candidates_from_web_manifest=*/false);
1708
1709 const GURL kIconURL1("http://www.google.com/b");
1710 const GURL kIconURL2("http://www.google.com/c");
1711
1712 const int kOriginalSize1 = kMaximalSize + 1;
1713 const int kOriginalSize2 = kMaximalSize + 2;
1714
1715 delegate_.fake_image_downloader().Add(kIconURL1, IntVector{kOriginalSize1},
1716 SK_ColorBLUE);
1717 delegate_.fake_image_downloader().Add(kIconURL2, IntVector{kOriginalSize2},
1718 SK_ColorBLUE);
1719
1720 // Verify the best bitmap was selected (although smaller than |kIconURL2|)
1721 // and that it was scaled down to |kMaximalSize|.
1722 EXPECT_CALL(delegate_,
1723 OnFaviconUpdated(_, _, kIconURL1, _,
1724 ImageSizeIs(kMaximalSize, kMaximalSize)));
1725
1726 RunHandlerWithCandidates(
1727 FaviconDriverObserver::NON_TOUCH_LARGEST,
1728 {FaviconURL(kIconURL1, kFavicon,
1729 SizeVector{gfx::Size(kOriginalSize1, kOriginalSize1)}),
1730 FaviconURL(kIconURL2, kFavicon,
1731 SizeVector{gfx::Size(kOriginalSize2, kOriginalSize2)})});
1732
1733 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL1));
1734 }
1735
1736 // Test that if several icons are downloaded because the icons are smaller than
1737 // expected that OnFaviconUpdated() is called with the largest downloaded
1738 // bitmap.
TEST_F(FaviconHandlerTest,TestKeepDownloadedLargestFavicon)1739 TEST_F(FaviconHandlerTest, TestKeepDownloadedLargestFavicon) {
1740 EXPECT_CALL(delegate_,
1741 OnFaviconUpdated(_, _, kIconURL12x12, _, ImageSizeIs(12, 12)));
1742
1743 RunHandlerWithCandidates(
1744 FaviconDriverObserver::NON_TOUCH_LARGEST,
1745 {FaviconURL(kIconURL10x10, kFavicon, SizeVector{gfx::Size(16, 16)}),
1746 FaviconURL(kIconURL12x12, kFavicon, SizeVector{gfx::Size(15, 15)}),
1747 FaviconURL(kIconURL16x16, kFavicon, kEmptySizes)});
1748 }
1749
1750 // Test that if a page URL is followed by another page URL which is not
1751 // considered the same document, favicon candidates listed in the second page
1752 // get associated to that second page only.
TEST_F(FaviconHandlerTest,SetFaviconsForLastPageUrlOnly)1753 TEST_F(FaviconHandlerTest, SetFaviconsForLastPageUrlOnly) {
1754 const GURL kDifferentPageURL = GURL("http://www.google.com/other");
1755
1756 EXPECT_CALL(favicon_service_,
1757 SetFavicons(base::flat_set<GURL>{kDifferentPageURL},
1758 kIconURL12x12, _, _));
1759 EXPECT_CALL(delegate_,
1760 OnFaviconUpdated(kDifferentPageURL,
1761 FaviconDriverObserver::NON_TOUCH_16_DIP,
1762 kIconURL12x12, _, _));
1763
1764 std::unique_ptr<FaviconHandler> handler = std::make_unique<FaviconHandler>(
1765 &favicon_service_, &delegate_, FaviconDriverObserver::NON_TOUCH_16_DIP);
1766 handler->FetchFavicon(kPageURL, /*is_same_document=*/false);
1767 base::RunLoop().RunUntilIdle();
1768 // Load a new URL (different document) without feeding any candidates for the
1769 // first URL.
1770 handler->FetchFavicon(kDifferentPageURL, /*is_same_document=*/false);
1771 base::RunLoop().RunUntilIdle();
1772 handler->OnUpdateCandidates(
1773 kDifferentPageURL, {FaviconURL(kIconURL12x12, kFavicon, kEmptySizes)},
1774 /*manifest_url=*/GURL());
1775 base::RunLoop().RunUntilIdle();
1776 }
1777
1778 // Test that if a page URL is followed by another page URL which is considered
1779 // the same document (e.g. fragment navigation), favicon candidates listed in
1780 // the second page get associated to both page URLs.
TEST_F(FaviconHandlerTest,SetFaviconsForMultipleUrlsWithinDocument)1781 TEST_F(FaviconHandlerTest, SetFaviconsForMultipleUrlsWithinDocument) {
1782 const GURL kDifferentPageURL = GURL("http://www.google.com/other");
1783
1784 EXPECT_CALL(favicon_service_,
1785 SetFavicons(base::flat_set<GURL>{kPageURL, kDifferentPageURL},
1786 kIconURL12x12, _, _));
1787 EXPECT_CALL(delegate_,
1788 OnFaviconUpdated(kDifferentPageURL,
1789 FaviconDriverObserver::NON_TOUCH_16_DIP,
1790 kIconURL12x12, _, _));
1791
1792 std::unique_ptr<FaviconHandler> handler = std::make_unique<FaviconHandler>(
1793 &favicon_service_, &delegate_, FaviconDriverObserver::NON_TOUCH_16_DIP);
1794 handler->FetchFavicon(kPageURL, /*is_same_document=*/false);
1795 base::RunLoop().RunUntilIdle();
1796 // Load a new URL (same document) without feeding any candidates for the first
1797 // URL.
1798 handler->FetchFavicon(kDifferentPageURL, /*is_same_document=*/true);
1799 base::RunLoop().RunUntilIdle();
1800 handler->OnUpdateCandidates(
1801 kDifferentPageURL, {FaviconURL(kIconURL12x12, kFavicon, kEmptySizes)},
1802 /*manifest_url=*/GURL());
1803 base::RunLoop().RunUntilIdle();
1804 }
1805
1806 // Manifests are currently enabled by default. Leaving this fixture for
1807 // logical grouping and blame layer.
1808 class FaviconHandlerManifestsEnabledTest : public FaviconHandlerTest {
1809 protected:
1810 const GURL kManifestURL = GURL("http://www.google.com/manifest.json");
1811
1812 FaviconHandlerManifestsEnabledTest() = default;
1813
1814 // Exercises the handler for the simplest case where all types are kTouchIcon
1815 // and no sizes are provided, using a FaviconHandler of type TOUCH_LARGETS.
RunHandlerWithSimpleTouchIconCandidates(const std::vector<GURL> & urls,const GURL & manifest_url)1816 std::unique_ptr<FaviconHandler> RunHandlerWithSimpleTouchIconCandidates(
1817 const std::vector<GURL>& urls,
1818 const GURL& manifest_url) {
1819 std::vector<favicon::FaviconURL> candidates;
1820 for (const GURL& url : urls) {
1821 candidates.emplace_back(url, kTouchIcon, kEmptySizes);
1822 }
1823 return RunHandlerWithCandidates(FaviconDriverObserver::TOUCH_LARGEST,
1824 candidates, manifest_url);
1825 }
1826
1827 private:
1828 // Avoid accidental use of kFavicon type, since Web Manifests are handled by
1829 // the FaviconHandler of type TOUCH_LARGEST.
1830 using FaviconHandlerTest::RunHandlerWithSimpleFaviconCandidates;
1831
1832 DISALLOW_COPY_AND_ASSIGN(FaviconHandlerManifestsEnabledTest);
1833 };
1834
1835 // Test that favicon mappings are deleted when a manifest previously cached in
1836 // the DB is no longer referenced by the page and the page lists no regular
1837 // icons.
TEST_F(FaviconHandlerManifestsEnabledTest,RemovedWebManifestAndNoRegularIcons)1838 TEST_F(FaviconHandlerManifestsEnabledTest,
1839 RemovedWebManifestAndNoRegularIcons) {
1840 favicon_service_.fake()->Store(
1841 kPageURL, kManifestURL,
1842 CreateRawBitmapResult(kManifestURL, kWebManifestIcon));
1843
1844 EXPECT_CALL(
1845 favicon_service_,
1846 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kWebManifestIcon));
1847
1848 RunHandlerWithSimpleTouchIconCandidates(URLVector(), /*manifest_url=*/GURL());
1849 }
1850
1851 // Test that favicon mappings are updated (but not deleted) when a manifest
1852 // previously cached in the DB is no longer referenced by the page and the page
1853 // lists regular icons.
TEST_F(FaviconHandlerManifestsEnabledTest,RemovedWebManifestAndRegularIcons)1854 TEST_F(FaviconHandlerManifestsEnabledTest, RemovedWebManifestAndRegularIcons) {
1855 favicon_service_.fake()->Store(
1856 kPageURL, kManifestURL,
1857 CreateRawBitmapResult(kManifestURL, kWebManifestIcon));
1858
1859 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
1860 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, kTouchIcon, _));
1861
1862 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12},
1863 /*manifest_url=*/GURL());
1864 }
1865
1866 // Test that favicon mappings are updated (but not deleted) when a manifest
1867 // previously cached in the DB (but expired) is no longer referenced by the page
1868 // and the page lists regular icons.
TEST_F(FaviconHandlerManifestsEnabledTest,ExpiredAndRemovedWebManifestAndRegularIcons)1869 TEST_F(FaviconHandlerManifestsEnabledTest,
1870 ExpiredAndRemovedWebManifestAndRegularIcons) {
1871 favicon_service_.fake()->Store(
1872 kPageURL, kManifestURL,
1873 CreateRawBitmapResult(kManifestURL, kWebManifestIcon, /*expired=*/true));
1874
1875 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
1876 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, kTouchIcon, _));
1877
1878 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12},
1879 /*manifest_url=*/GURL());
1880 }
1881
1882 // Test that a favicon corresponding to a web manifest is reported when:
1883 // - There is data in the favicon database for the manifest URL.
1884 // AND
1885 // - FaviconService::OnFaviconDataForManifestFromFaviconService() runs before
1886 // FaviconHandler::OnUpdateCandidates() is called.
TEST_F(FaviconHandlerManifestsEnabledTest,GetFaviconFromManifestInHistoryIfCandidatesSlower)1887 TEST_F(FaviconHandlerManifestsEnabledTest,
1888 GetFaviconFromManifestInHistoryIfCandidatesSlower) {
1889 favicon_service_.fake()->Store(
1890 kPageURL, kManifestURL,
1891 CreateRawBitmapResult(kManifestURL, kWebManifestIcon));
1892
1893 EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(_)).Times(0);
1894
1895 EXPECT_CALL(favicon_service_,
1896 UpdateFaviconMappingsAndFetch(_, kManifestURL, kWebManifestIcon,
1897 /*desired_size_in_dip=*/0, _, _));
1898 EXPECT_CALL(delegate_,
1899 OnFaviconUpdated(_, FaviconDriverObserver::TOUCH_LARGEST,
1900 kManifestURL, _, _));
1901
1902 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
1903 EXPECT_THAT(favicon_service_.fake()->db_requests(),
1904 ElementsAre(kPageURL, kManifestURL));
1905 EXPECT_THAT(delegate_.downloads(), IsEmpty());
1906 }
1907
1908 // Test that a favicon corresponding to a web manifest is reported when:
1909 // - There is data in the favicon database for the manifest URL.
1910 // AND
1911 // - FaviconHandler::OnUpdateCandidates() is called before
1912 // FaviconService::OnFaviconDataForManifestFromFaviconService() runs.
TEST_F(FaviconHandlerManifestsEnabledTest,GetFaviconFromManifestInHistoryIfCandidatesFaster)1913 TEST_F(FaviconHandlerManifestsEnabledTest,
1914 GetFaviconFromManifestInHistoryIfCandidatesFaster) {
1915 favicon_service_.fake()->Store(
1916 kPageURL, kManifestURL,
1917 CreateRawBitmapResult(kManifestURL, kWebManifestIcon));
1918 // Defer the database lookup completion to control the exact timing.
1919 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kManifestURL);
1920
1921 EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(_)).Times(0);
1922
1923 EXPECT_CALL(favicon_service_,
1924 UpdateFaviconMappingsAndFetch(_, kManifestURL, kWebManifestIcon,
1925 /*desired_size_in_dip=*/0, _, _));
1926 EXPECT_CALL(delegate_,
1927 OnFaviconUpdated(_, FaviconDriverObserver::TOUCH_LARGEST,
1928 kManifestURL, _, _));
1929
1930 std::unique_ptr<FaviconHandler> handler =
1931 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
1932 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
1933
1934 // Complete the lookup.
1935 EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
1936 base::RunLoop().RunUntilIdle();
1937
1938 EXPECT_THAT(favicon_service_.fake()->db_requests(),
1939 ElementsAre(kPageURL, kManifestURL));
1940 EXPECT_THAT(delegate_.downloads(), IsEmpty());
1941 }
1942
1943 // Believed to fix crbug.com/544560.
1944 // Tests that there is not crash and SetFavicons() is called with the
1945 // appropriate icon URL in the following scenario:
1946 // - The database initially has a cached icon for the page (not expired).
1947 // - Two initial favicon candidates are received fast, before the history lookup
1948 // completes. There is no manifest URL initially.
1949 // - Before the history lookup completes, favicon candidates are updated via
1950 // javascript to include a manifest URL.
1951 // - The manifest lists at least one icon.
TEST_F(FaviconHandlerManifestsEnabledTest,AddManifestWithIconsViaJavascriptAfterFastCandidates)1952 TEST_F(FaviconHandlerManifestsEnabledTest,
1953 AddManifestWithIconsViaJavascriptAfterFastCandidates) {
1954 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
1955 EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, _, _));
1956
1957 // Initial database contains a cached by expired icon for |kPageURL|.
1958 favicon_service_.fake()->Store(
1959 kPageURL, kIconURL16x16,
1960 CreateRawBitmapResult(kIconURL16x16, kTouchIcon));
1961
1962 // Manifest with icons.
1963 const std::vector<favicon::FaviconURL> kManifestIcons = {
1964 FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
1965 };
1966 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
1967
1968 // Initial load does NOT contain a manifest. Regular candidates are received
1969 // before the history lookup for |kPageURL| is finished.
1970 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
1971 std::unique_ptr<FaviconHandler> handler =
1972 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12, kIconURL16x16},
1973 /*manifest_url=*/GURL());
1974 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
1975 // Update candidates, now containing a manifest URL.
1976 handler->OnUpdateCandidates(
1977 kPageURL, {FaviconURL(kIconURL16x16, kTouchIcon, kEmptySizes)},
1978 kManifestURL);
1979 base::RunLoop().RunUntilIdle();
1980 // Complete the history lookup for |kPageURL| now.
1981 ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
1982 base::RunLoop().RunUntilIdle();
1983
1984 EXPECT_THAT(favicon_service_.fake()->db_requests(),
1985 ElementsAre(kPageURL, kManifestURL));
1986 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL64x64));
1987 }
1988
1989 // Believed to fix crbug.com/544560.
1990 // Tests that there is not crash and SetFavicons() is called with the
1991 // appropriate icon URL in the following scenario:
1992 // - The database initially has a cached but expired icon for the page.
1993 // - Initial favicon candidates are received fast, before the history lookup
1994 // completes. There is no manifest URL initially.
1995 // - Before the history lookup completes, favicon candidates are updated via
1996 // javascript to include a manifest URL.
1997 // - The manifest lists at least one icon.
TEST_F(FaviconHandlerManifestsEnabledTest,AddManifestWithIconsViaJavascriptAfterFastCandidatesAndExpiredIcon)1998 TEST_F(FaviconHandlerManifestsEnabledTest,
1999 AddManifestWithIconsViaJavascriptAfterFastCandidatesAndExpiredIcon) {
2000 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
2001 EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, _, _));
2002
2003 // Initial database contains a cached by expired icon for |kPageURL|.
2004 favicon_service_.fake()->Store(
2005 kPageURL, kIconURL16x16,
2006 CreateRawBitmapResult(kIconURL16x16, kTouchIcon, /*expired=*/true));
2007
2008 // Manifest with icons.
2009 const std::vector<favicon::FaviconURL> kManifestIcons = {
2010 FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
2011 };
2012 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2013
2014 // Initial load does NOT contain a manifest. Regular candidates are received
2015 // before the history lookup for |kPageURL| is finished.
2016 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
2017 std::unique_ptr<FaviconHandler> handler =
2018 RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16},
2019 /*manifest_url=*/GURL());
2020 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
2021 // Update candidates, now containing a manifest URL.
2022 handler->OnUpdateCandidates(
2023 kPageURL, {FaviconURL(kIconURL16x16, kTouchIcon, kEmptySizes)},
2024 kManifestURL);
2025 base::RunLoop().RunUntilIdle();
2026 // Complete the history lookup for |kPageURL| now.
2027 ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
2028 base::RunLoop().RunUntilIdle();
2029
2030 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2031 ElementsAre(kPageURL, kManifestURL));
2032 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL64x64));
2033 }
2034
2035 // Believed to fix crbug.com/544560.
2036 // Same as the test above with the difference that the manifest contains no
2037 // icons.
TEST_F(FaviconHandlerManifestsEnabledTest,AddManifestWithoutIconsViaJavascriptAfterFastCandidatesAndExpiredIcon)2038 TEST_F(FaviconHandlerManifestsEnabledTest,
2039 AddManifestWithoutIconsViaJavascriptAfterFastCandidatesAndExpiredIcon) {
2040 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
2041 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL16x16, _, _));
2042
2043 // Initial database contains a cached by expired icon for |kPageURL|.
2044 favicon_service_.fake()->Store(
2045 kPageURL, kIconURL16x16,
2046 CreateRawBitmapResult(kIconURL16x16, kTouchIcon, /*expired=*/true));
2047
2048 // Manifest without icons.
2049 delegate_.fake_manifest_downloader().Add(kManifestURL,
2050 std::vector<favicon::FaviconURL>());
2051
2052 // Initial load does NOT contain a manifest. Regular candidates are received
2053 // before the history lookup for |kPageURL| is finished.
2054 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
2055 std::unique_ptr<FaviconHandler> handler =
2056 RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16},
2057 /*manifest_url=*/GURL());
2058 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
2059 // Update candidates, now containing a manifest URL.
2060 handler->OnUpdateCandidates(
2061 kPageURL, {FaviconURL(kIconURL16x16, kTouchIcon, kEmptySizes)},
2062 kManifestURL);
2063 base::RunLoop().RunUntilIdle();
2064 // Complete the history lookup for |kPageURL| now.
2065 ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
2066 base::RunLoop().RunUntilIdle();
2067
2068 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2069 ElementsAre(kPageURL, kManifestURL));
2070 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL16x16));
2071 }
2072
2073 // Test that a favicon corresponding to a web manifest is reported when there is
2074 // data in the database for neither the page URL nor the manifest URL.
TEST_F(FaviconHandlerManifestsEnabledTest,GetFaviconFromUnknownManifest)2075 TEST_F(FaviconHandlerManifestsEnabledTest, GetFaviconFromUnknownManifest) {
2076 const std::vector<favicon::FaviconURL> kManifestIcons = {
2077 FaviconURL(kIconURL16x16, kWebManifestIcon, kEmptySizes),
2078 };
2079
2080 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2081
2082 EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(_)).Times(0);
2083
2084 EXPECT_CALL(favicon_service_,
2085 SetFavicons(_, kManifestURL, kWebManifestIcon, _));
2086 EXPECT_CALL(delegate_,
2087 OnFaviconUpdated(_, FaviconDriverObserver::TOUCH_LARGEST,
2088 kManifestURL, _, _));
2089
2090 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2091 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2092 ElementsAre(kPageURL, kManifestURL));
2093 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL16x16));
2094 }
2095
2096 // Test that icons from a web manifest use a desired size of 192x192.
TEST_F(FaviconHandlerManifestsEnabledTest,Prefer192x192IconFromManifest)2097 TEST_F(FaviconHandlerManifestsEnabledTest, Prefer192x192IconFromManifest) {
2098 const GURL kIconURL144x144 = GURL("http://www.google.com/favicon144x144");
2099 const GURL kIconURL192x192 = GURL("http://www.google.com/favicon192x192");
2100
2101 delegate_.fake_image_downloader().Add(kIconURL144x144, IntVector{144});
2102 delegate_.fake_image_downloader().Add(kIconURL192x192, IntVector{192});
2103
2104 const std::vector<favicon::FaviconURL> kManifestIcons = {
2105 FaviconURL(kIconURL144x144, kWebManifestIcon,
2106 SizeVector(1U, gfx::Size(144, 144))),
2107 FaviconURL(kIconURL192x192, kWebManifestIcon,
2108 SizeVector(1U, gfx::Size(192, 192))),
2109 };
2110
2111 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2112
2113 RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL);
2114
2115 EXPECT_THAT(delegate_.downloads(),
2116 ElementsAre(kManifestURL, kIconURL192x192));
2117 }
2118
2119 // Test that a 192x192 favicon corresponding to a web manifest is reported with
2120 // the appropriate size when there is data in the database for neither the page
2121 // URL nor the manifest URL.
TEST_F(FaviconHandlerManifestsEnabledTest,GetNonResized192x192FaviconFromUnknownManifest)2122 TEST_F(FaviconHandlerManifestsEnabledTest,
2123 GetNonResized192x192FaviconFromUnknownManifest) {
2124 const GURL kIconURL192x192 = GURL("http://www.google.com/favicon192x192");
2125 const std::vector<favicon::FaviconURL> kManifestIcons = {
2126 FaviconURL(kIconURL192x192, kWebManifestIcon, kEmptySizes),
2127 };
2128 delegate_.fake_image_downloader().Add(kIconURL192x192, IntVector{192});
2129 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2130
2131 EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, kWebManifestIcon,
2132 ImageSizeIs(192, 192)));
2133
2134 RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL);
2135 }
2136
2137 // Test that the manifest and icon are redownloaded if the icon cached for the
2138 // page URL expired.
TEST_F(FaviconHandlerManifestsEnabledTest,GetFaviconFromExpiredManifest)2139 TEST_F(FaviconHandlerManifestsEnabledTest, GetFaviconFromExpiredManifest) {
2140 const std::vector<favicon::FaviconURL> kManifestIcons = {
2141 FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
2142 };
2143
2144 favicon_service_.fake()->Store(
2145 kPageURL, kManifestURL,
2146 CreateRawBitmapResult(kManifestURL, kWebManifestIcon,
2147 /*expired=*/true));
2148 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2149
2150 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _)).Times(2);
2151 EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, _, _));
2152
2153 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2154 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2155 ElementsAre(kPageURL, kManifestURL));
2156 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL64x64));
2157 }
2158
2159 // Test that the manifest and icon are redownloaded if the icon cached for the
2160 // manifest URL expired, which was observed during a visit to a different page
2161 // URL.
TEST_F(FaviconHandlerManifestsEnabledTest,GetFaviconFromExpiredManifestLinkedFromOtherPage)2162 TEST_F(FaviconHandlerManifestsEnabledTest,
2163 GetFaviconFromExpiredManifestLinkedFromOtherPage) {
2164 const GURL kSomePreviousPageURL("https://www.google.com/previous");
2165 const std::vector<favicon::FaviconURL> kManifestIcons = {
2166 FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
2167 };
2168
2169 favicon_service_.fake()->Store(
2170 kSomePreviousPageURL, kManifestURL,
2171 CreateRawBitmapResult(kManifestURL, kWebManifestIcon,
2172 /*expired=*/true));
2173 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2174
2175 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _)).Times(2);
2176 EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, _, _));
2177
2178 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2179 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2180 ElementsAre(kPageURL, kManifestURL));
2181 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL64x64));
2182 }
2183
2184 // Test that a favicon corresponding to a web manifest is reported when:
2185 // - There is data in the database for neither the page URL nor the manifest
2186 // URL.
2187 // - There is data in the database for the icon URL listed in the manifest.
TEST_F(FaviconHandlerManifestsEnabledTest,GetFaviconFromUnknownManifestButKnownIcon)2188 TEST_F(FaviconHandlerManifestsEnabledTest,
2189 GetFaviconFromUnknownManifestButKnownIcon) {
2190 const GURL kSomePreviousPageURL("https://www.google.com/previous");
2191 const std::vector<favicon::FaviconURL> kManifestIcons = {
2192 FaviconURL(kIconURL16x16, kWebManifestIcon, kEmptySizes),
2193 };
2194
2195 favicon_service_.fake()->Store(
2196 kSomePreviousPageURL, kIconURL16x16,
2197 CreateRawBitmapResult(kIconURL16x16, kTouchIcon));
2198 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2199
2200 EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, _, _));
2201 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _));
2202
2203 RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL);
2204 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2205 ElementsAre(kPageURL, kManifestURL));
2206 // This is because, in the current implementation, FaviconHandler only checks
2207 // whether there is an icon cached with the manifest URL as the "icon URL"
2208 // when a page has a non-empty Web Manifest.
2209 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL16x16));
2210 }
2211
2212 // Test a manifest that returns a 404 gets blacklisted via
2213 // UnableToDownloadFavicon() AND that the regular favicon is selected as
2214 // fallback.
TEST_F(FaviconHandlerManifestsEnabledTest,UnknownManifestReturning404)2215 TEST_F(FaviconHandlerManifestsEnabledTest, UnknownManifestReturning404) {
2216 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
2217
2218 EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(kManifestURL));
2219 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, _, _));
2220
2221 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2222 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2223 ElementsAre(kPageURL, kManifestURL, kIconURL12x12));
2224 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL12x12));
2225 }
2226
2227 // Test that a manifest that was previously blacklisted via
2228 // UnableToDownloadFavicon() is ignored and that the regular favicon is selected
2229 // as fallback.
TEST_F(FaviconHandlerManifestsEnabledTest,IgnoreManifestWithPrior404)2230 TEST_F(FaviconHandlerManifestsEnabledTest, IgnoreManifestWithPrior404) {
2231 ON_CALL(favicon_service_, WasUnableToDownloadFavicon(kManifestURL))
2232 .WillByDefault(Return(true));
2233
2234 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
2235
2236 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, _, _));
2237
2238 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2239 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2240 ElementsAre(kPageURL, kIconURL12x12));
2241 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL12x12));
2242 }
2243
2244 // Test that favicon mappings are deleted when a manifest previously cached in
2245 // the DB (but expired) returns a 404, when the page lists no regular icons.
TEST_F(FaviconHandlerManifestsEnabledTest,ExpiredManifestReturning404AndNoRegularIcons)2246 TEST_F(FaviconHandlerManifestsEnabledTest,
2247 ExpiredManifestReturning404AndNoRegularIcons) {
2248 favicon_service_.fake()->Store(
2249 kPageURL, kManifestURL,
2250 CreateRawBitmapResult(kManifestURL, kWebManifestIcon, /*expired=*/true));
2251
2252 EXPECT_CALL(
2253 favicon_service_,
2254 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kWebManifestIcon));
2255
2256 RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL);
2257 }
2258
2259 // Test that favicon mappings are updated (but not deleted) when a manifest
2260 // previously cached in the DB (but expired) returns a 404, when the page lists
2261 // regular icons that haven't been cached before.
TEST_F(FaviconHandlerManifestsEnabledTest,ExpiredManifestReturning404AndRegularIcons)2262 TEST_F(FaviconHandlerManifestsEnabledTest,
2263 ExpiredManifestReturning404AndRegularIcons) {
2264 favicon_service_.fake()->Store(
2265 kPageURL, kManifestURL,
2266 CreateRawBitmapResult(kManifestURL, kWebManifestIcon, /*expired=*/true));
2267
2268 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
2269
2270 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, kTouchIcon, _));
2271
2272 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2273 }
2274
2275 // Test that favicon mappings are deleted when a manifest previously cached in
2276 // the DB (but expired) contains no icons, when the page lists no regular icons.
TEST_F(FaviconHandlerManifestsEnabledTest,ExpiredManifestWithoutIconsAndNoRegularIcons)2277 TEST_F(FaviconHandlerManifestsEnabledTest,
2278 ExpiredManifestWithoutIconsAndNoRegularIcons) {
2279 delegate_.fake_manifest_downloader().Add(kManifestURL,
2280 std::vector<favicon::FaviconURL>());
2281 favicon_service_.fake()->Store(
2282 kPageURL, kManifestURL,
2283 CreateRawBitmapResult(kManifestURL, kWebManifestIcon, /*expired=*/true));
2284
2285 EXPECT_CALL(
2286 favicon_service_,
2287 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kWebManifestIcon));
2288
2289 RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL);
2290 }
2291
2292 // Test that favicon mappings are updated (but not deleted) when a manifest
2293 // previously cached in the DB (but expired) contains no icons, when the page
2294 // lists regular icons that haven't been cached before.
TEST_F(FaviconHandlerManifestsEnabledTest,ExpiredManifestWithoutIconsAndRegularIcons)2295 TEST_F(FaviconHandlerManifestsEnabledTest,
2296 ExpiredManifestWithoutIconsAndRegularIcons) {
2297 delegate_.fake_manifest_downloader().Add(kManifestURL,
2298 std::vector<favicon::FaviconURL>());
2299 favicon_service_.fake()->Store(
2300 kPageURL, kManifestURL,
2301 CreateRawBitmapResult(kManifestURL, kWebManifestIcon, /*expired=*/true));
2302
2303 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
2304
2305 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, kTouchIcon, _));
2306
2307 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2308 }
2309
2310 // Test that the regular favicon is selected when:
2311 // - The page links to a Web Manifest.
2312 // - The Web Manifest does not contain any icon URLs (it is not a 404).
2313 // - The page has an icon URL provided via a <link rel="icon"> tag.
2314 // - The database does not know about the page URL, manifest URL or icon URL.
TEST_F(FaviconHandlerManifestsEnabledTest,UnknownManifestWithoutIcons)2315 TEST_F(FaviconHandlerManifestsEnabledTest, UnknownManifestWithoutIcons) {
2316 delegate_.fake_manifest_downloader().Add(kManifestURL,
2317 std::vector<favicon::FaviconURL>());
2318
2319 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
2320
2321 // UnableToDownloadFavicon() is expected to prevent repeated downloads of the
2322 // same manifest (which is not otherwise cached, since it doesn't contain
2323 // icons).
2324 EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(kManifestURL));
2325 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL12x12, _, _));
2326
2327 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2328 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2329 ElementsAre(kPageURL, kManifestURL, kIconURL12x12));
2330 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL12x12));
2331 }
2332
2333 // Test that the regular favicon is selected when:
2334 // - The page links to a Web Manifest.
2335 // - The Web Manifest does not contain any icon URLs (it is not a 404).
2336 // - The page has an icon URL provided via a <link rel="icon"> tag.
2337 // - The database does not know about the page URL.
2338 // - The database does not know about the manifest URL.
2339 // - The database knows about the icon URL.
TEST_F(FaviconHandlerManifestsEnabledTest,UnknownManifestWithoutIconsAndKnownRegularIcons)2340 TEST_F(FaviconHandlerManifestsEnabledTest,
2341 UnknownManifestWithoutIconsAndKnownRegularIcons) {
2342 const GURL kSomePreviousPageURL("https://www.google.com/previous");
2343
2344 delegate_.fake_manifest_downloader().Add(kManifestURL,
2345 std::vector<favicon::FaviconURL>());
2346 favicon_service_.fake()->Store(
2347 kSomePreviousPageURL, kIconURL12x12,
2348 CreateRawBitmapResult(kIconURL12x12, kTouchIcon));
2349
2350 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
2351 EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
2352
2353 // UnableToDownloadFavicon() is expected to prevent repeated downloads of the
2354 // same manifest (which is not otherwise cached, since it doesn't contain
2355 // icons).
2356 EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(kManifestURL));
2357 EXPECT_CALL(favicon_service_,
2358 UpdateFaviconMappingsAndFetch(_, kManifestURL, _, _, _, _));
2359 EXPECT_CALL(favicon_service_,
2360 UpdateFaviconMappingsAndFetch(_, kIconURL12x12, _, _, _, _));
2361 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL12x12, _, _));
2362
2363 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2364 EXPECT_THAT(favicon_service_.fake()->db_requests(),
2365 ElementsAre(kPageURL, kManifestURL, kIconURL12x12));
2366 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL));
2367 }
2368
2369 // Test that the database remains unmodified when:
2370 // - The page links to a Web Manifest.
2371 // - The Web Manifest does not contain any icon URLs (it is not a 404).
2372 // - The page has an icon URL provided via a <link rel="icon"> tag.
2373 // - The database has a mapping between the page URL to the favicon URL.
TEST_F(FaviconHandlerManifestsEnabledTest,UnknownManifestWithoutIconsAndRegularIconInHistory)2374 TEST_F(FaviconHandlerManifestsEnabledTest,
2375 UnknownManifestWithoutIconsAndRegularIconInHistory) {
2376 delegate_.fake_manifest_downloader().Add(kManifestURL,
2377 std::vector<favicon::FaviconURL>());
2378 favicon_service_.fake()->Store(
2379 kPageURL, kIconURL16x16,
2380 CreateRawBitmapResult(kIconURL16x16, kTouchIcon));
2381
2382 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL16x16, _, _));
2383 EXPECT_CALL(favicon_service_,
2384 UpdateFaviconMappingsAndFetch(_, kManifestURL, _, _, _, _));
2385
2386 RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16}, kManifestURL);
2387
2388 ASSERT_THAT(favicon_service_.fake()->db_requests(),
2389 ElementsAre(kPageURL, kManifestURL));
2390 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL));
2391 }
2392
2393 // Test that Delegate::OnFaviconUpdated() is called if a page uses Javascript to
2394 // modify the page's <link rel="manifest"> tag to point to a different manifest.
TEST_F(FaviconHandlerManifestsEnabledTest,ManifestUpdateViaJavascript)2395 TEST_F(FaviconHandlerManifestsEnabledTest, ManifestUpdateViaJavascript) {
2396 const GURL kManifestURL1("http://www.google.com/manifest1.json");
2397 const GURL kManifestURL2("http://www.google.com/manifest2.json");
2398 const std::vector<favicon::FaviconURL> kManifestIcons1 = {
2399 FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
2400 };
2401 const std::vector<favicon::FaviconURL> kManifestIcons2 = {
2402 FaviconURL(kIconURL10x10, kWebManifestIcon, kEmptySizes),
2403 };
2404
2405 delegate_.fake_manifest_downloader().Add(kManifestURL1, kManifestIcons1);
2406 delegate_.fake_manifest_downloader().Add(kManifestURL2, kManifestIcons2);
2407
2408 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL1, _, _));
2409
2410 std::unique_ptr<FaviconHandler> handler =
2411 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL1);
2412 ASSERT_THAT(favicon_service_.fake()->db_requests(),
2413 ElementsAre(kPageURL, kManifestURL1));
2414 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL1, kIconURL64x64));
2415 ASSERT_TRUE(VerifyAndClearExpectations());
2416
2417 // Simulate the page changing it's manifest URL via Javascript.
2418 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL2, _, _));
2419 handler->OnUpdateCandidates(
2420 kPageURL, {FaviconURL(kIconURL12x12, kTouchIcon, kEmptySizes)},
2421 kManifestURL2);
2422 base::RunLoop().RunUntilIdle();
2423 ASSERT_THAT(favicon_service_.fake()->db_requests(),
2424 ElementsAre(kManifestURL2));
2425 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL2, kIconURL10x10));
2426 }
2427
2428 // Test that Delegate::OnFaviconUpdated() is called if a page uses Javascript to
2429 // remove the page's <link rel="manifest"> tag (i.e. no web manifest) WHILE a
2430 // lookup to the history database is ongoing for the manifest URL.
TEST_F(FaviconHandlerManifestsEnabledTest,RemoveManifestViaJavascriptWhileDatabaseLookup)2431 TEST_F(FaviconHandlerManifestsEnabledTest,
2432 RemoveManifestViaJavascriptWhileDatabaseLookup) {
2433 const std::vector<favicon::FaviconURL> kManifestIcons = {
2434 FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
2435 };
2436
2437 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2438 // Defer the database lookup completion to control the exact timing.
2439 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kManifestURL);
2440
2441 std::unique_ptr<FaviconHandler> handler =
2442 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12}, kManifestURL);
2443 ASSERT_THAT(favicon_service_.fake()->db_requests(),
2444 ElementsAre(kPageURL, kManifestURL));
2445 // Database lookup for |kManifestURL| is ongoing.
2446 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
2447
2448 // Simulate the page changing it's manifest URL to empty via Javascript.
2449 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL12x12, _, _));
2450 handler->OnUpdateCandidates(
2451 kPageURL, {FaviconURL(kIconURL12x12, kTouchIcon, kEmptySizes)}, GURL());
2452 // Complete the lookup.
2453 EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
2454 base::RunLoop().RunUntilIdle();
2455 ASSERT_THAT(favicon_service_.fake()->db_requests(),
2456 ElementsAre(kPageURL, kManifestURL, kIconURL12x12));
2457 EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL12x12));
2458 }
2459
2460 // Tests that favicon mappings are removed if a page initially lists no regular
2461 // favicons but does link to a web manifest, and later uses Javascript to remove
2462 // the manifest URL.
TEST_F(FaviconHandlerManifestsEnabledTest,RemoveManifestViaJavascriptDeletesMappings)2463 TEST_F(FaviconHandlerManifestsEnabledTest,
2464 RemoveManifestViaJavascriptDeletesMappings) {
2465 const std::vector<favicon::FaviconURL> kManifestIcons = {
2466 FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
2467 };
2468
2469 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2470
2471 EXPECT_CALL(favicon_service_, SetFavicons(base::flat_set<GURL>{kPageURL},
2472 kManifestURL, kWebManifestIcon, _));
2473
2474 std::unique_ptr<FaviconHandler> handler =
2475 RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL);
2476 ASSERT_THAT(favicon_service_.fake()->db_requests(),
2477 ElementsAre(kPageURL, kManifestURL));
2478 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL64x64));
2479 ASSERT_TRUE(VerifyAndClearExpectations());
2480
2481 // Simulate the page removing it's manifest URL via Javascript.
2482 EXPECT_CALL(
2483 favicon_service_,
2484 DeleteFaviconMappings(base::flat_set<GURL>{kPageURL}, kWebManifestIcon));
2485 handler->OnUpdateCandidates(kPageURL, std::vector<FaviconURL>(), GURL());
2486 base::RunLoop().RunUntilIdle();
2487 ASSERT_THAT(favicon_service_.fake()->db_requests(), IsEmpty());
2488 EXPECT_THAT(delegate_.downloads(), IsEmpty());
2489 }
2490
2491 // Test that Delegate::OnFaviconUpdated() is called a page without manifest uses
2492 // Javascript to add a <link rel="manifest"> tag (i.e. a new web manifest) WHILE
2493 // a lookup to the history database is ongoing for the icon URL.
TEST_F(FaviconHandlerManifestsEnabledTest,AddManifestViaJavascriptWhileDatabaseLookup)2494 TEST_F(FaviconHandlerManifestsEnabledTest,
2495 AddManifestViaJavascriptWhileDatabaseLookup) {
2496 const std::vector<favicon::FaviconURL> kManifestIcons = {
2497 FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
2498 };
2499
2500 delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
2501 // Defer the database lookup completion to control the exact timing.
2502 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kIconURL12x12);
2503
2504 std::unique_ptr<FaviconHandler> handler =
2505 RunHandlerWithSimpleTouchIconCandidates({kIconURL12x12},
2506 /*manifest_url=*/GURL());
2507 ASSERT_THAT(favicon_service_.fake()->db_requests(),
2508 ElementsAre(kPageURL, kIconURL12x12));
2509 // Database lookup for |kIconURL12x12| is ongoing.
2510 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
2511
2512 // Simulate the page changing it's manifest URL to |kManifestURL| via
2513 // Javascript.
2514 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _));
2515 handler->OnUpdateCandidates(
2516 kPageURL, {FaviconURL(kIconURL12x12, kTouchIcon, kEmptySizes)},
2517 kManifestURL);
2518 // Complete the lookup.
2519 EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
2520 base::RunLoop().RunUntilIdle();
2521 ASSERT_THAT(favicon_service_.fake()->db_requests(),
2522 ElementsAre(kPageURL, kIconURL12x12, kManifestURL));
2523 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL64x64));
2524 }
2525
2526 // Test that SetFavicons() is not called when:
2527 // - The page doesn't initially link to a Web Manifest.
2528 // - The page has an icon URL provided via a <link rel="icon"> tag.
2529 // - The database does not know about the page URL or icon URL.
2530 // - While the icon is being downloaded, the page uses Javascript to add a
2531 // <link rel="manifest"> tag.
2532 // - The database has bitmap data for the manifest URL.
TEST_F(FaviconHandlerManifestsEnabledTest,AddKnownManifestViaJavascriptWhileImageDownload)2533 TEST_F(FaviconHandlerManifestsEnabledTest,
2534 AddKnownManifestViaJavascriptWhileImageDownload) {
2535 const GURL kSomePreviousPageURL("https://www.google.com/previous");
2536
2537 favicon_service_.fake()->Store(
2538 kSomePreviousPageURL, kManifestURL,
2539 CreateRawBitmapResult(kManifestURL, kWebManifestIcon));
2540
2541 // Defer the image download completion to control the exact timing.
2542 delegate_.fake_image_downloader().SetRunCallbackManuallyForUrl(kIconURL16x16);
2543
2544 std::unique_ptr<FaviconHandler> handler =
2545 RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16},
2546 /*manifest_url=*/GURL());
2547
2548 ASSERT_TRUE(VerifyAndClearExpectations());
2549 ASSERT_TRUE(delegate_.fake_image_downloader().HasPendingManualCallback());
2550
2551 // Simulate the page changing it's manifest URL to |kManifestURL| via
2552 // Javascript. Should invalidate the ongoing image download.
2553 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
2554 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL, _, _));
2555
2556 handler->OnUpdateCandidates(
2557 kPageURL, {FaviconURL(kIconURL16x16, kTouchIcon, kEmptySizes)},
2558 kManifestURL);
2559
2560 // Finalizes download, which should be thrown away as the manifest URL was
2561 // provided.
2562 EXPECT_TRUE(delegate_.fake_image_downloader().RunCallbackManually());
2563 base::RunLoop().RunUntilIdle();
2564 }
2565
2566 // Test that SetFavicons() is called with the icon URL when:
2567 // - The page doesn't initially link to a Web Manifest.
2568 // - The page has an icon URL provided via a <link rel="icon"> tag.
2569 // - The database does not know about the page URL or icon URL.
2570 // - During the database lookup, the page uses Javascript to add a
2571 // <link rel="manifest"> tag.
2572 // - The database does not know about the manifest URL.
2573 // - The manifest contains no icons.
TEST_F(FaviconHandlerManifestsEnabledTest,AddManifestWithoutIconsViaJavascriptWhileDatabaseLookup)2574 TEST_F(FaviconHandlerManifestsEnabledTest,
2575 AddManifestWithoutIconsViaJavascriptWhileDatabaseLookup) {
2576 delegate_.fake_manifest_downloader().Add(kManifestURL,
2577 std::vector<favicon::FaviconURL>());
2578
2579 // Defer the database lookup completion to control the exact timing.
2580 favicon_service_.fake()->SetRunCallbackManuallyForUrl(kIconURL16x16);
2581
2582 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0);
2583 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0);
2584
2585 std::unique_ptr<FaviconHandler> handler =
2586 RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16},
2587 /*manifest_url=*/GURL());
2588
2589 ASSERT_TRUE(VerifyAndClearExpectations());
2590 ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
2591
2592 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL16x16, _, _));
2593 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL16x16, _, _));
2594
2595 handler->OnUpdateCandidates(
2596 kPageURL, {FaviconURL(kIconURL16x16, kTouchIcon, kEmptySizes)},
2597 kManifestURL);
2598
2599 // Finalizes lookup, which should be thrown away as the manifest URLs was
2600 // provided.
2601 EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
2602 base::RunLoop().RunUntilIdle();
2603
2604 // The manifest URL interrupted the original processing of kIconURL16x16, but
2605 // a second one should have been started.
2606 EXPECT_TRUE(favicon_service_.fake()->RunCallbackManually());
2607 base::RunLoop().RunUntilIdle();
2608 }
2609
2610 // Test that SetFavicons() is called when:
2611 // - The page links to one Web Manifest, which contains one icon.
2612 // - The database does not know about the page URL, icon URL or manifest URL.
2613 // - During image download, the page updates the manifest URL to point to
2614 // another manifest.
2615 // - The second manifest contains the same icons as the first.
TEST_F(FaviconHandlerManifestsEnabledTest,UpdateManifestWithSameIconURLsWhileDownloading)2616 TEST_F(FaviconHandlerManifestsEnabledTest,
2617 UpdateManifestWithSameIconURLsWhileDownloading) {
2618 const GURL kManifestURL1("http://www.google.com/manifest1.json");
2619 const GURL kManifestURL2("http://www.google.com/manifest2.json");
2620 const std::vector<favicon::FaviconURL> kManifestIcons = {
2621 FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
2622 };
2623
2624 delegate_.fake_manifest_downloader().Add(kManifestURL1, kManifestIcons);
2625 delegate_.fake_manifest_downloader().Add(kManifestURL2, kManifestIcons);
2626
2627 // Defer the download completion to control the exact timing.
2628 delegate_.fake_image_downloader().SetRunCallbackManuallyForUrl(kIconURL64x64);
2629
2630 std::unique_ptr<FaviconHandler> handler =
2631 RunHandlerWithSimpleTouchIconCandidates(URLVector(), kManifestURL1);
2632
2633 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL1, kIconURL64x64));
2634 ASSERT_TRUE(VerifyAndClearExpectations());
2635 ASSERT_TRUE(delegate_.fake_image_downloader().HasPendingManualCallback());
2636
2637 // Calling OnUpdateCandidates() with a different manifest URL should trigger
2638 // its download.
2639 handler->OnUpdateCandidates(kPageURL, std::vector<favicon::FaviconURL>(),
2640 kManifestURL2);
2641 base::RunLoop().RunUntilIdle();
2642 EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL2, kIconURL64x64));
2643
2644 // Complete the download.
2645 EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL2, _, _));
2646 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kManifestURL2, _, _));
2647 EXPECT_TRUE(delegate_.fake_image_downloader().RunCallbackManually());
2648 base::RunLoop().RunUntilIdle();
2649 }
2650
2651 } // namespace
2652 } // namespace favicon
2653