OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/favicon/core/favicon_handler.h" | 5 #include "components/favicon/core/favicon_handler.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <memory> | 9 #include <memory> |
10 #include <set> | 10 #include <set> |
| 11 #include <string> |
11 #include <vector> | 12 #include <vector> |
12 | 13 |
13 #include "base/macros.h" | 14 #include "base/macros.h" |
14 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
15 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
16 #include "base/run_loop.h" | 17 #include "base/run_loop.h" |
17 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
| 19 #include "base/test/histogram_tester.h" |
18 #include "components/favicon/core/favicon_driver.h" | 20 #include "components/favicon/core/favicon_driver.h" |
19 #include "components/favicon/core/test/mock_favicon_service.h" | 21 #include "components/favicon/core/test/mock_favicon_service.h" |
20 #include "testing/gmock/include/gmock/gmock.h" | 22 #include "testing/gmock/include/gmock/gmock.h" |
21 #include "testing/gtest/include/gtest/gtest.h" | 23 #include "testing/gtest/include/gtest/gtest.h" |
22 #include "third_party/skia/include/core/SkBitmap.h" | 24 #include "third_party/skia/include/core/SkBitmap.h" |
23 #include "ui/base/layout.h" | 25 #include "ui/base/layout.h" |
24 #include "ui/gfx/codec/png_codec.h" | 26 #include "ui/gfx/codec/png_codec.h" |
25 #include "ui/gfx/favicon_size.h" | 27 #include "ui/gfx/favicon_size.h" |
26 #include "ui/gfx/image/image.h" | 28 #include "ui/gfx/image/image.h" |
27 | 29 |
28 namespace favicon { | 30 namespace favicon { |
29 namespace { | 31 namespace { |
30 | 32 |
31 using favicon_base::FAVICON; | 33 using favicon_base::FAVICON; |
32 using favicon_base::FaviconRawBitmapResult; | 34 using favicon_base::FaviconRawBitmapResult; |
33 using favicon_base::TOUCH_ICON; | 35 using favicon_base::TOUCH_ICON; |
34 using favicon_base::TOUCH_PRECOMPOSED_ICON; | 36 using favicon_base::TOUCH_PRECOMPOSED_ICON; |
35 using testing::Assign; | 37 using testing::Assign; |
| 38 using testing::Contains; |
36 using testing::ElementsAre; | 39 using testing::ElementsAre; |
37 using testing::InSequence; | 40 using testing::InSequence; |
38 using testing::Invoke; | 41 using testing::Invoke; |
39 using testing::IsEmpty; | 42 using testing::IsEmpty; |
40 using testing::Return; | 43 using testing::Return; |
41 using testing::_; | 44 using testing::_; |
42 | 45 |
43 using IntVector = std::vector<int>; | 46 using IntVector = std::vector<int>; |
44 using URLVector = std::vector<GURL>; | 47 using URLVector = std::vector<GURL>; |
45 using BitmapVector = std::vector<SkBitmap>; | 48 using BitmapVector = std::vector<SkBitmap>; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 FaviconRawBitmapResult bitmap_result; | 86 FaviconRawBitmapResult bitmap_result; |
84 bitmap_result.expired = expired; | 87 bitmap_result.expired = expired; |
85 bitmap_result.bitmap_data = data; | 88 bitmap_result.bitmap_data = data; |
86 // Use a pixel size other than (0,0) as (0,0) has a special meaning. | 89 // Use a pixel size other than (0,0) as (0,0) has a special meaning. |
87 bitmap_result.pixel_size = gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize); | 90 bitmap_result.pixel_size = gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize); |
88 bitmap_result.icon_type = icon_type; | 91 bitmap_result.icon_type = icon_type; |
89 bitmap_result.icon_url = icon_url; | 92 bitmap_result.icon_url = icon_url; |
90 return {bitmap_result}; | 93 return {bitmap_result}; |
91 } | 94 } |
92 | 95 |
93 // Fake that implements the calls to FaviconHalder::Delegate's DownloadImage(), | 96 // Fake that implements the calls to FaviconHandler::Delegate's DownloadImage(), |
94 // delegated to this class through MockDelegate. | 97 // delegated to this class through MockDelegate. |
95 class FakeImageDownloader { | 98 class FakeImageDownloader { |
96 public: | 99 public: |
97 struct Response { | 100 struct Response { |
98 int http_status_code = 404; | 101 int http_status_code = 404; |
99 BitmapVector bitmaps; | 102 BitmapVector bitmaps; |
100 SizeVector original_bitmap_sizes; | 103 SizeVector original_bitmap_sizes; |
101 }; | 104 }; |
102 | 105 |
103 FakeImageDownloader() : next_download_id_(1) {} | 106 FakeImageDownloader() : next_download_id_(1) {} |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
314 protected: | 317 protected: |
315 const std::vector<gfx::Size> kEmptySizes; | 318 const std::vector<gfx::Size> kEmptySizes; |
316 | 319 |
317 // Some known icons for which download will succeed. | 320 // Some known icons for which download will succeed. |
318 const GURL kPageURL = GURL("http://www.google.com"); | 321 const GURL kPageURL = GURL("http://www.google.com"); |
319 const GURL kIconURL10x10 = GURL("http://www.google.com/favicon10x10"); | 322 const GURL kIconURL10x10 = GURL("http://www.google.com/favicon10x10"); |
320 const GURL kIconURL12x12 = GURL("http://www.google.com/favicon12x12"); | 323 const GURL kIconURL12x12 = GURL("http://www.google.com/favicon12x12"); |
321 const GURL kIconURL16x16 = GURL("http://www.google.com/favicon16x16"); | 324 const GURL kIconURL16x16 = GURL("http://www.google.com/favicon16x16"); |
322 const GURL kIconURL64x64 = GURL("http://www.google.com/favicon64x64"); | 325 const GURL kIconURL64x64 = GURL("http://www.google.com/favicon64x64"); |
323 | 326 |
324 FaviconHandlerTest() { | 327 FaviconHandlerTest() : download_histogram_(new base::HistogramTester()) { |
325 // Register various known icon URLs. | 328 // Register various known icon URLs. |
326 delegate_.fake_downloader().Add(kIconURL10x10, IntVector{10}); | 329 delegate_.fake_downloader().Add(kIconURL10x10, IntVector{10}); |
327 delegate_.fake_downloader().Add(kIconURL12x12, IntVector{12}); | 330 delegate_.fake_downloader().Add(kIconURL12x12, IntVector{12}); |
328 delegate_.fake_downloader().Add(kIconURL16x16, IntVector{16}); | 331 delegate_.fake_downloader().Add(kIconURL16x16, IntVector{16}); |
329 delegate_.fake_downloader().Add(kIconURL64x64, IntVector{64}); | 332 delegate_.fake_downloader().Add(kIconURL64x64, IntVector{64}); |
330 | 333 |
331 // The score computed by SelectFaviconFrames() is dependent on the supported | 334 // The score computed by SelectFaviconFrames() is dependent on the supported |
332 // scale factors of the platform. It is used for determining the goodness of | 335 // scale factors of the platform. It is used for determining the goodness of |
333 // a downloaded bitmap in FaviconHandler::OnDidDownloadFavicon(). | 336 // a downloaded bitmap in FaviconHandler::OnDidDownloadFavicon(). |
334 // Force the values of the scale factors so that the tests produce the same | 337 // Force the values of the scale factors so that the tests produce the same |
335 // results on all platforms. | 338 // results on all platforms. |
336 scoped_set_supported_scale_factors_.reset( | 339 scoped_set_supported_scale_factors_.reset( |
337 new ui::test::ScopedSetSupportedScaleFactors({ui::SCALE_FACTOR_100P})); | 340 new ui::test::ScopedSetSupportedScaleFactors({ui::SCALE_FACTOR_100P})); |
338 } | 341 } |
339 | 342 |
| 343 void TearDown() override { |
| 344 // Check that if any downloads occurred, the attempt count was recorded. |
| 345 if (!download_histogram_name_.empty() && delegate_.downloads().size() > 0) { |
| 346 download_histogram_->ExpectBucketCount( |
| 347 download_histogram_name_, |
| 348 /*sample=*/delegate_.downloads().size(), |
| 349 /*expected_count=*/1); |
| 350 } |
| 351 } |
| 352 |
340 bool VerifyAndClearExpectations() { | 353 bool VerifyAndClearExpectations() { |
341 base::RunLoop().RunUntilIdle(); | 354 base::RunLoop().RunUntilIdle(); |
342 favicon_service_.fake()->ClearDbRequests(); | 355 favicon_service_.fake()->ClearDbRequests(); |
343 delegate_.fake_downloader().ClearDownloads(); | 356 delegate_.fake_downloader().ClearDownloads(); |
| 357 // Drop download histogram without verification. |
| 358 download_histogram_ = base::MakeUnique<base::HistogramTester>(); |
344 return testing::Mock::VerifyAndClearExpectations(&favicon_service_) && | 359 return testing::Mock::VerifyAndClearExpectations(&favicon_service_) && |
345 testing::Mock::VerifyAndClearExpectations(&delegate_); | 360 testing::Mock::VerifyAndClearExpectations(&delegate_); |
346 } | 361 } |
347 | 362 |
348 // Creates a new handler and feeds in the page URL and the candidates. | 363 // Creates a new handler and feeds in the page URL and the candidates. |
349 // Returns the handler in case tests want to exercise further steps. | 364 // Returns the handler in case tests want to exercise further steps. |
350 std::unique_ptr<FaviconHandler> RunHandlerWithCandidates( | 365 std::unique_ptr<FaviconHandler> RunHandlerWithCandidates( |
351 FaviconDriverObserver::NotificationIconType handler_type, | 366 FaviconDriverObserver::NotificationIconType handler_type, |
352 const std::vector<favicon::FaviconURL>& candidates) { | 367 const std::vector<favicon::FaviconURL>& candidates) { |
| 368 download_histogram_name_ = |
| 369 handler_type == FaviconDriverObserver::TOUCH_LARGEST |
| 370 ? "Favicons.LargeIconDownloadAttempts" |
| 371 : "Favicons.DownloadAttempts"; |
353 auto handler = base::MakeUnique<FaviconHandler>(&favicon_service_, | 372 auto handler = base::MakeUnique<FaviconHandler>(&favicon_service_, |
354 &delegate_, handler_type); | 373 &delegate_, handler_type); |
355 handler->FetchFavicon(kPageURL); | 374 handler->FetchFavicon(kPageURL); |
356 // The first RunUntilIdle() causes the FaviconService lookups be faster than | 375 // The first RunUntilIdle() causes the FaviconService lookups be faster than |
357 // OnUpdateFaviconURL(), which is the most likely scenario. | 376 // OnUpdateFaviconURL(), which is the most likely scenario. |
358 base::RunLoop().RunUntilIdle(); | 377 base::RunLoop().RunUntilIdle(); |
359 handler->OnUpdateFaviconURL(kPageURL, candidates); | 378 handler->OnUpdateFaviconURL(kPageURL, candidates); |
360 base::RunLoop().RunUntilIdle(); | 379 base::RunLoop().RunUntilIdle(); |
361 return handler; | 380 return handler; |
362 } | 381 } |
363 | 382 |
364 // Same as above, but for the simplest case where all types are FAVICON and | 383 // Same as above, but for the simplest case where all types are FAVICON and |
365 // no sizes are provided, using a FaviconHandler of type NON_TOUCH_16_DIP. | 384 // no sizes are provided, using a FaviconHandler of type NON_TOUCH_16_DIP. |
366 std::unique_ptr<FaviconHandler> RunHandlerWithSimpleFaviconCandidates( | 385 std::unique_ptr<FaviconHandler> RunHandlerWithSimpleFaviconCandidates( |
367 const std::vector<GURL>& urls) { | 386 const std::vector<GURL>& urls) { |
368 std::vector<favicon::FaviconURL> candidates; | 387 std::vector<favicon::FaviconURL> candidates; |
369 for (const GURL& url : urls) { | 388 for (const GURL& url : urls) { |
370 candidates.emplace_back(url, FAVICON, kEmptySizes); | 389 candidates.emplace_back(url, FAVICON, kEmptySizes); |
371 } | 390 } |
372 return RunHandlerWithCandidates(FaviconDriverObserver::NON_TOUCH_16_DIP, | 391 return RunHandlerWithCandidates(FaviconDriverObserver::NON_TOUCH_16_DIP, |
373 candidates); | 392 candidates); |
374 } | 393 } |
375 | 394 |
| 395 std::string download_histogram_name_; |
376 base::MessageLoopForUI message_loop_; | 396 base::MessageLoopForUI message_loop_; |
377 std::unique_ptr<ui::test::ScopedSetSupportedScaleFactors> | 397 std::unique_ptr<ui::test::ScopedSetSupportedScaleFactors> |
378 scoped_set_supported_scale_factors_; | 398 scoped_set_supported_scale_factors_; |
| 399 std::unique_ptr<base::HistogramTester> download_histogram_; |
379 testing::NiceMock<MockFaviconServiceWithFake> favicon_service_; | 400 testing::NiceMock<MockFaviconServiceWithFake> favicon_service_; |
380 testing::NiceMock<MockDelegate> delegate_; | 401 testing::NiceMock<MockDelegate> delegate_; |
381 }; | 402 }; |
382 | 403 |
383 TEST_F(FaviconHandlerTest, GetFaviconFromHistory) { | 404 TEST_F(FaviconHandlerTest, GetFaviconFromHistory) { |
384 const GURL kIconURL("http://www.google.com/favicon"); | 405 const GURL kIconURL("http://www.google.com/favicon"); |
385 | 406 |
386 favicon_service_.fake()->Store(kPageURL, kIconURL, | 407 favicon_service_.fake()->Store(kPageURL, kIconURL, |
387 CreateRawBitmapResult(kIconURL)); | 408 CreateRawBitmapResult(kIconURL)); |
388 | 409 |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
600 // Defer the download completion such that RunUntilIdle() doesn't complete | 621 // Defer the download completion such that RunUntilIdle() doesn't complete |
601 // the download. | 622 // the download. |
602 delegate_.fake_downloader().SetRunCallbackManuallyForUrl(kIconURL1); | 623 delegate_.fake_downloader().SetRunCallbackManuallyForUrl(kIconURL1); |
603 | 624 |
604 delegate_.fake_downloader().Add(kIconURL1, IntVector{16}); | 625 delegate_.fake_downloader().Add(kIconURL1, IntVector{16}); |
605 delegate_.fake_downloader().Add(kIconURL3, IntVector{64}); | 626 delegate_.fake_downloader().Add(kIconURL3, IntVector{64}); |
606 | 627 |
607 std::unique_ptr<FaviconHandler> handler = | 628 std::unique_ptr<FaviconHandler> handler = |
608 RunHandlerWithSimpleFaviconCandidates({kIconURL1, kIconURL2}); | 629 RunHandlerWithSimpleFaviconCandidates({kIconURL1, kIconURL2}); |
609 | 630 |
| 631 download_histogram_->ExpectTotalCount("Favicons.DownloadAttempts", 0); |
610 ASSERT_TRUE(VerifyAndClearExpectations()); | 632 ASSERT_TRUE(VerifyAndClearExpectations()); |
611 ASSERT_TRUE(delegate_.fake_downloader().HasPendingManualCallback()); | 633 ASSERT_TRUE(delegate_.fake_downloader().HasPendingManualCallback()); |
612 | 634 |
613 // Favicon update should invalidate the ongoing download. | 635 // Favicon update should invalidate the ongoing download. |
614 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL3, _, _)); | 636 EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL3, _, _)); |
615 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL3, _, _)); | 637 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, kIconURL3, _, _)); |
616 | 638 |
617 handler->OnUpdateFaviconURL(kPageURL, | 639 handler->OnUpdateFaviconURL(kPageURL, |
618 {FaviconURL(kIconURL3, FAVICON, kEmptySizes)}); | 640 {FaviconURL(kIconURL3, FAVICON, kEmptySizes)}); |
619 | 641 |
(...skipping 19 matching lines...) Expand all Loading... |
639 // Defer the download completion such that RunUntilIdle() doesn't complete | 661 // Defer the download completion such that RunUntilIdle() doesn't complete |
640 // the download. | 662 // the download. |
641 delegate_.fake_downloader().SetRunCallbackManuallyForUrl(kSlowLoadingIconURL); | 663 delegate_.fake_downloader().SetRunCallbackManuallyForUrl(kSlowLoadingIconURL); |
642 delegate_.fake_downloader().Add(kSlowLoadingIconURL, IntVector{16}); | 664 delegate_.fake_downloader().Add(kSlowLoadingIconURL, IntVector{16}); |
643 | 665 |
644 std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates( | 666 std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates( |
645 FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls); | 667 FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls); |
646 | 668 |
647 ASSERT_THAT(favicon_service_.fake()->db_requests(), | 669 ASSERT_THAT(favicon_service_.fake()->db_requests(), |
648 ElementsAre(kPageURL, kIconURL64x64, kSlowLoadingIconURL)); | 670 ElementsAre(kPageURL, kIconURL64x64, kSlowLoadingIconURL)); |
| 671 download_histogram_->ExpectTotalCount("Favicons.DownloadAttempts", 0); |
649 ASSERT_TRUE(VerifyAndClearExpectations()); | 672 ASSERT_TRUE(VerifyAndClearExpectations()); |
650 ASSERT_TRUE(delegate_.fake_downloader().HasPendingManualCallback()); | 673 ASSERT_TRUE(delegate_.fake_downloader().HasPendingManualCallback()); |
651 | 674 |
652 // Calling OnUpdateFaviconURL() with the same icon URLs should have no effect, | 675 // Calling OnUpdateFaviconURL() with the same icon URLs should have no effect, |
653 // despite the ongoing download. | 676 // despite the ongoing download. |
654 handler->OnUpdateFaviconURL(kPageURL, favicon_urls); | 677 handler->OnUpdateFaviconURL(kPageURL, favicon_urls); |
655 base::RunLoop().RunUntilIdle(); | 678 base::RunLoop().RunUntilIdle(); |
656 | 679 |
657 // Complete the download. | 680 // Complete the download. |
658 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)); | 681 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)); |
659 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)); | 682 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)); |
660 EXPECT_TRUE(delegate_.fake_downloader().RunCallbackManually()); | 683 EXPECT_TRUE(delegate_.fake_downloader().RunCallbackManually()); |
661 base::RunLoop().RunUntilIdle(); | 684 base::RunLoop().RunUntilIdle(); |
662 EXPECT_THAT(delegate_.downloads(), IsEmpty()); | 685 EXPECT_THAT(delegate_.downloads(), IsEmpty()); |
663 } | 686 } |
664 | 687 |
665 // Test that calling OnUpdateFaviconUrl() with the same icon URLs as before is a | 688 // Test that calling OnUpdateFaviconUrl() with the same icon URLs as before is a |
666 // no-op. This is important because OnUpdateFaviconUrl() is called when the page | 689 // no-op. This is important because OnUpdateFaviconUrl() is called when the page |
667 // finishes loading. This can occur several times for pages with iframes. | 690 // finishes loading. This can occur several times for pages with iframes. |
668 TEST_F(FaviconHandlerTest, UpdateSameIconURLsAfterFinishedShouldBeNoop) { | 691 TEST_F(FaviconHandlerTest, UpdateSameIconURLsAfterFinishedShouldBeNoop) { |
669 const std::vector<FaviconURL> favicon_urls = { | 692 const std::vector<FaviconURL> favicon_urls = { |
670 FaviconURL(kIconURL10x10, FAVICON, kEmptySizes), | 693 FaviconURL(kIconURL10x10, FAVICON, kEmptySizes), |
671 FaviconURL(kIconURL16x16, FAVICON, kEmptySizes), | 694 FaviconURL(kIconURL16x16, FAVICON, kEmptySizes), |
672 }; | 695 }; |
673 | 696 |
674 std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates( | 697 std::unique_ptr<FaviconHandler> handler = RunHandlerWithCandidates( |
675 FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls); | 698 FaviconDriverObserver::NON_TOUCH_16_DIP, favicon_urls); |
676 | 699 |
| 700 download_histogram_->ExpectBucketCount( |
| 701 "Favicons.DownloadAttempts", |
| 702 /*sample=*/delegate_.downloads().size(), |
| 703 /*expected_count=*/1); |
677 ASSERT_TRUE(VerifyAndClearExpectations()); | 704 ASSERT_TRUE(VerifyAndClearExpectations()); |
678 | 705 |
679 // Calling OnUpdateFaviconURL() with identical data should be a no-op. | 706 // Calling OnUpdateFaviconURL() with identical data should be a no-op. |
680 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0); | 707 EXPECT_CALL(delegate_, OnFaviconUpdated(_, _, _, _, _)).Times(0); |
681 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0); | 708 EXPECT_CALL(favicon_service_, SetFavicons(_, _, _, _)).Times(0); |
682 | 709 |
683 handler->OnUpdateFaviconURL(kPageURL, favicon_urls); | 710 handler->OnUpdateFaviconURL(kPageURL, favicon_urls); |
684 base::RunLoop().RunUntilIdle(); | 711 base::RunLoop().RunUntilIdle(); |
685 EXPECT_THAT(favicon_service_.fake()->db_requests(), IsEmpty()); | 712 EXPECT_THAT(favicon_service_.fake()->db_requests(), IsEmpty()); |
686 EXPECT_THAT(delegate_.downloads(), IsEmpty()); | 713 EXPECT_THAT(delegate_.downloads(), IsEmpty()); |
(...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1017 | 1044 |
1018 RunHandlerWithCandidates( | 1045 RunHandlerWithCandidates( |
1019 FaviconDriverObserver::NON_TOUCH_LARGEST, | 1046 FaviconDriverObserver::NON_TOUCH_LARGEST, |
1020 {FaviconURL(kIconURL10x10, FAVICON, SizeVector{gfx::Size(16, 16)}), | 1047 {FaviconURL(kIconURL10x10, FAVICON, SizeVector{gfx::Size(16, 16)}), |
1021 FaviconURL(kIconURL12x12, FAVICON, SizeVector{gfx::Size(15, 15)}), | 1048 FaviconURL(kIconURL12x12, FAVICON, SizeVector{gfx::Size(15, 15)}), |
1022 FaviconURL(kIconURL16x16, FAVICON, kEmptySizes)}); | 1049 FaviconURL(kIconURL16x16, FAVICON, kEmptySizes)}); |
1023 } | 1050 } |
1024 | 1051 |
1025 } // namespace | 1052 } // namespace |
1026 } // namespace favicon | 1053 } // namespace favicon |
OLD | NEW |