1 // Copyright 2015 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/large_icon_service_impl.h"
6 
7 #include <memory>
8 #include <string>
9 
10 #include "base/bind.h"
11 #include "base/macros.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/ref_counted_memory.h"
14 #include "base/task/cancelable_task_tracker.h"
15 #include "base/test/metrics/histogram_tester.h"
16 #include "base/test/mock_callback.h"
17 #include "base/test/task_environment.h"
18 #include "base/threading/thread_task_runner_handle.h"
19 #include "build/build_config.h"
20 #include "components/favicon/core/favicon_client.h"
21 #include "components/favicon/core/test/mock_favicon_service.h"
22 #include "components/favicon_base/fallback_icon_style.h"
23 #include "components/favicon_base/favicon_types.h"
24 #include "components/image_fetcher/core/image_fetcher.h"
25 #include "components/image_fetcher/core/mock_image_fetcher.h"
26 #include "components/image_fetcher/core/request_metadata.h"
27 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
28 #include "testing/gmock/include/gmock/gmock.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "third_party/skia/include/core/SkBitmap.h"
31 #include "third_party/skia/include/core/SkColor.h"
32 #include "ui/base/layout.h"
33 #include "ui/gfx/codec/png_codec.h"
34 #include "ui/gfx/geometry/size.h"
35 #include "ui/gfx/image/image.h"
36 #include "ui/gfx/image/image_skia.h"
37 #include "url/gurl.h"
38 
39 namespace favicon {
40 namespace {
41 
42 using image_fetcher::MockImageFetcher;
43 using testing::_;
44 using testing::Eq;
45 using testing::HasSubstr;
46 using testing::IsEmpty;
47 using testing::IsNull;
48 using testing::NiceMock;
49 using testing::Not;
50 using testing::Property;
51 using testing::Return;
52 using testing::SaveArg;
53 
54 const char kDummyUrl[] = "http://www.example.com";
55 const char kDummyIconUrl[] = "http://www.example.com/touch_icon.png";
56 const SkColor kTestColor = SK_ColorRED;
57 
ACTION_P(PostFetchReply,p0)58 ACTION_P(PostFetchReply, p0) {
59   base::ThreadTaskRunnerHandle::Get()->PostTask(
60       FROM_HERE,
61       base::BindOnce(std::move(*arg2), p0, image_fetcher::RequestMetadata()));
62 }
63 
ACTION_P2(PostFetchReplyWithMetadata,p0,p1)64 ACTION_P2(PostFetchReplyWithMetadata, p0, p1) {
65   base::ThreadTaskRunnerHandle::Get()->PostTask(
66       FROM_HERE, base::BindOnce(std::move(*arg2), p0, p1));
67 }
68 
CreateTestSkBitmap(int w,int h,SkColor color)69 SkBitmap CreateTestSkBitmap(int w, int h, SkColor color) {
70   SkBitmap bitmap;
71   bitmap.allocN32Pixels(w, h);
72   bitmap.eraseColor(color);
73   return bitmap;
74 }
75 
CreateTestBitmapResult(int w,int h,SkColor color)76 favicon_base::FaviconRawBitmapResult CreateTestBitmapResult(int w,
77                                                             int h,
78                                                             SkColor color) {
79   favicon_base::FaviconRawBitmapResult result;
80   result.expired = false;
81 
82   // Create bitmap and fill with |color|.
83   scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
84   SkBitmap bitmap;
85   bitmap.allocN32Pixels(w, h);
86   bitmap.eraseColor(color);
87   gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &data->data());
88   result.bitmap_data = data;
89 
90   result.pixel_size = gfx::Size(w, h);
91   result.icon_url = GURL(kDummyIconUrl);
92   result.icon_type = favicon_base::IconType::kTouchIcon;
93   CHECK(result.is_valid());
94   return result;
95 }
96 
HasBackgroundColor(const favicon_base::FallbackIconStyle & fallback_icon_style,SkColor color)97 bool HasBackgroundColor(
98     const favicon_base::FallbackIconStyle& fallback_icon_style,
99     SkColor color) {
100   return !fallback_icon_style.is_default_background_color &&
101          fallback_icon_style.background_color == color;
102 }
103 
104 // TODO(jkrcal): Make the tests a bit crisper, see crbug.com/725822.
105 class LargeIconServiceTest : public testing::Test {
106  public:
LargeIconServiceTest()107   LargeIconServiceTest()
108       : scoped_set_supported_scale_factors_({ui::SCALE_FACTOR_200P}),
109         mock_image_fetcher_(new NiceMock<MockImageFetcher>()),
110         large_icon_service_(&mock_favicon_service_,
111                             base::WrapUnique(mock_image_fetcher_),
112                             /*desired_size_in_dip_for_server_requests=*/24,
113                             /*icon_type_for_server_requests=*/
114                             favicon_base::IconType::kTouchIcon,
115                             /*google_server_client_param=*/"test_chrome") {}
116 
~LargeIconServiceTest()117   ~LargeIconServiceTest() override {}
118 
119  protected:
120   base::test::TaskEnvironment task_environment_;
121   ui::test::ScopedSetSupportedScaleFactors scoped_set_supported_scale_factors_;
122   NiceMock<MockImageFetcher>* mock_image_fetcher_;
123   testing::NiceMock<MockFaviconService> mock_favicon_service_;
124   LargeIconServiceImpl large_icon_service_;
125   base::HistogramTester histogram_tester_;
126 
127  private:
128   DISALLOW_COPY_AND_ASSIGN(LargeIconServiceTest);
129 };
130 
TEST_F(LargeIconServiceTest,ShouldGetFromGoogleServer)131 TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServer) {
132   const GURL kExpectedServerUrl(
133       "https://t0.gstatic.com/faviconV2?client=test_chrome&nfrp=2"
134       "&check_seen=true&size=48&min_size=16&max_size=256"
135       "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/");
136 
137   EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0);
138   EXPECT_CALL(mock_favicon_service_,
139               CanSetOnDemandFavicons(GURL(kDummyUrl),
140                                      favicon_base::IconType::kTouchIcon, _))
141       .WillOnce([](auto, auto, base::OnceCallback<void(bool)> callback) {
142         return base::ThreadTaskRunnerHandle::Get()->PostTask(
143             FROM_HERE, base::BindOnce(std::move(callback), true));
144       });
145 
146   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
147   EXPECT_CALL(*mock_image_fetcher_,
148               FetchImageAndData_(kExpectedServerUrl, _, _, _))
149       .WillOnce(PostFetchReply(gfx::Image::CreateFrom1xBitmap(
150           CreateTestSkBitmap(64, 64, kTestColor))));
151   EXPECT_CALL(mock_favicon_service_,
152               SetOnDemandFavicons(GURL(kDummyUrl), kExpectedServerUrl,
153                                   favicon_base::IconType::kTouchIcon, _, _))
154       .WillOnce(
155           [](auto, auto, auto, auto, base::OnceCallback<void(bool)> callback) {
156             return base::ThreadTaskRunnerHandle::Get()->PostTask(
157                 FROM_HERE, base::BindOnce(std::move(callback), true));
158           });
159 
160   large_icon_service_
161       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
162           GURL(kDummyUrl),
163           /*may_page_url_be_private=*/true, /*should_trim_page_url_path=*/false,
164           TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
165 
166   EXPECT_CALL(callback,
167               Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS));
168   task_environment_.RunUntilIdle();
169   histogram_tester_.ExpectUniqueSample(
170       "Favicons.LargeIconService.DownloadedSize", 64, /*expected_count=*/1);
171 }
172 
TEST_F(LargeIconServiceTest,ShouldGetFromGoogleServerWithOriginalUrl)173 TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServerWithOriginalUrl) {
174   const GURL kExpectedServerUrl(
175       "https://t0.gstatic.com/faviconV2?client=test_chrome&nfrp=2"
176       "&check_seen=true&size=48&min_size=16&max_size=256"
177       "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/");
178   const GURL kExpectedOriginalUrl("http://www.example.com/favicon.png");
179 
180   EXPECT_CALL(mock_favicon_service_,
181               CanSetOnDemandFavicons(GURL(kDummyUrl),
182                                      favicon_base::IconType::kTouchIcon, _))
183       .WillOnce([](auto, auto, base::OnceCallback<void(bool)> callback) {
184         return base::ThreadTaskRunnerHandle::Get()->PostTask(
185             FROM_HERE, base::BindOnce(std::move(callback), true));
186       });
187 
188   image_fetcher::RequestMetadata expected_metadata;
189   expected_metadata.content_location_header = kExpectedOriginalUrl.spec();
190   EXPECT_CALL(*mock_image_fetcher_,
191               FetchImageAndData_(kExpectedServerUrl, _, _, _))
192       .WillOnce(PostFetchReplyWithMetadata(
193           gfx::Image::CreateFrom1xBitmap(
194               CreateTestSkBitmap(64, 64, kTestColor)),
195           expected_metadata));
196   EXPECT_CALL(mock_favicon_service_,
197               SetOnDemandFavicons(GURL(kDummyUrl), kExpectedOriginalUrl,
198                                   favicon_base::IconType::kTouchIcon, _, _))
199       .WillOnce(
200           [](auto, auto, auto, auto, base::OnceCallback<void(bool)> callback) {
201             return base::ThreadTaskRunnerHandle::Get()->PostTask(
202                 FROM_HERE, base::BindOnce(std::move(callback), true));
203           });
204 
205   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
206   large_icon_service_
207       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
208           GURL(kDummyUrl),
209           /*may_page_url_be_private=*/true, /*should_trim_page_url_path=*/false,
210           TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
211 
212   EXPECT_CALL(callback,
213               Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS));
214   task_environment_.RunUntilIdle();
215 }
216 
TEST_F(LargeIconServiceTest,ShouldTrimQueryParametersForGoogleServer)217 TEST_F(LargeIconServiceTest, ShouldTrimQueryParametersForGoogleServer) {
218   const GURL kDummyUrlWithQuery("http://www.example.com?foo=1");
219   const GURL kExpectedServerUrl(
220       "https://t0.gstatic.com/faviconV2?client=test_chrome&nfrp=2"
221       "&check_seen=true&size=48&min_size=16&max_size=256"
222       "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/");
223 
224   EXPECT_CALL(mock_favicon_service_,
225               CanSetOnDemandFavicons(GURL(kDummyUrlWithQuery),
226                                      favicon_base::IconType::kTouchIcon, _))
227       .WillOnce([](auto, auto, base::OnceCallback<void(bool)> callback) {
228         return base::ThreadTaskRunnerHandle::Get()->PostTask(
229             FROM_HERE, base::BindOnce(std::move(callback), true));
230       });
231 
232   EXPECT_CALL(*mock_image_fetcher_,
233               FetchImageAndData_(kExpectedServerUrl, _, _, _))
234       .WillOnce(PostFetchReply(gfx::Image::CreateFrom1xBitmap(
235           CreateTestSkBitmap(64, 64, kTestColor))));
236   // Verify that the non-trimmed page URL is used when writing to the database.
237   EXPECT_CALL(mock_favicon_service_,
238               SetOnDemandFavicons(_, kExpectedServerUrl, _, _, _));
239 
240   large_icon_service_
241       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
242           GURL(kDummyUrlWithQuery),
243           /*may_page_url_be_private=*/true, /*should_trim_page_url_path=*/false,
244           TRAFFIC_ANNOTATION_FOR_TESTS,
245           favicon_base::GoogleFaviconServerCallback());
246 
247   task_environment_.RunUntilIdle();
248 }
249 
TEST_F(LargeIconServiceTest,ShouldNotCheckOnPublicUrls)250 TEST_F(LargeIconServiceTest, ShouldNotCheckOnPublicUrls) {
251   EXPECT_CALL(mock_favicon_service_,
252               CanSetOnDemandFavicons(GURL(kDummyUrl),
253                                      favicon_base::IconType::kTouchIcon, _))
254       .WillOnce([](auto, auto, base::OnceCallback<void(bool)> callback) {
255         return base::ThreadTaskRunnerHandle::Get()->PostTask(
256             FROM_HERE, base::BindOnce(std::move(callback), true));
257       });
258 
259   // The request has no "check_seen=true"; full URL is tested elsewhere.
260   EXPECT_CALL(
261       *mock_image_fetcher_,
262       FetchImageAndData_(
263           Property(&GURL::query, Not(HasSubstr("check_seen=true"))), _, _, _))
264       .WillOnce(PostFetchReply(gfx::Image()));
265 
266   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
267 
268   large_icon_service_
269       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
270           GURL(kDummyUrl),
271           /*may_page_url_be_private=*/false,
272           /*should_trim_page_url_path=*/false, TRAFFIC_ANNOTATION_FOR_TESTS,
273           callback.Get());
274 
275   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
276                                 FAILURE_CONNECTION_ERROR));
277   task_environment_.RunUntilIdle();
278 }
279 
TEST_F(LargeIconServiceTest,ShouldNotQueryGoogleServerIfInvalidScheme)280 TEST_F(LargeIconServiceTest, ShouldNotQueryGoogleServerIfInvalidScheme) {
281   const GURL kDummyFtpUrl("ftp://www.example.com");
282 
283   EXPECT_CALL(*mock_image_fetcher_, FetchImageAndData_(_, _, _, _)).Times(0);
284 
285   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
286 
287   large_icon_service_
288       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
289           GURL(kDummyFtpUrl),
290           /*may_page_url_be_private=*/true, /*should_trim_page_url_path=*/false,
291           TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
292 
293   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
294                                 FAILURE_TARGET_URL_SKIPPED));
295   task_environment_.RunUntilIdle();
296   EXPECT_THAT(histogram_tester_.GetAllSamples(
297                   "Favicons.LargeIconService.DownloadedSize"),
298               IsEmpty());
299 }
300 
TEST_F(LargeIconServiceTest,ShouldNotQueryGoogleServerIfInvalidURL)301 TEST_F(LargeIconServiceTest, ShouldNotQueryGoogleServerIfInvalidURL) {
302   const GURL kDummyInvalidUrl("htt");
303 
304   EXPECT_CALL(*mock_image_fetcher_, FetchImageAndData_(_, _, _, _)).Times(0);
305 
306   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
307 
308   large_icon_service_
309       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
310           GURL(kDummyInvalidUrl),
311           /*may_page_url_be_private=*/true, /*should_trim_page_url_path=*/false,
312           TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
313 
314   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
315                                 FAILURE_TARGET_URL_INVALID));
316   task_environment_.RunUntilIdle();
317   EXPECT_THAT(histogram_tester_.GetAllSamples(
318                   "Favicons.LargeIconService.DownloadedSize"),
319               IsEmpty());
320 }
321 
TEST_F(LargeIconServiceTest,ShouldReportUnavailableIfFetchFromServerFails)322 TEST_F(LargeIconServiceTest, ShouldReportUnavailableIfFetchFromServerFails) {
323   const GURL kExpectedServerUrl(
324       "https://t0.gstatic.com/faviconV2?client=test_chrome&nfrp=2"
325       "&check_seen=true&size=48&min_size=16&max_size=256"
326       "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/");
327 
328   EXPECT_CALL(mock_favicon_service_,
329               CanSetOnDemandFavicons(GURL(kDummyUrl),
330                                      favicon_base::IconType::kTouchIcon, _))
331       .WillOnce([](auto, auto, base::OnceCallback<void(bool)> callback) {
332         return base::ThreadTaskRunnerHandle::Get()->PostTask(
333             FROM_HERE, base::BindOnce(std::move(callback), true));
334       });
335   EXPECT_CALL(mock_favicon_service_, SetOnDemandFavicons(_, _, _, _, _))
336       .Times(0);
337 
338   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
339   EXPECT_CALL(*mock_image_fetcher_,
340               FetchImageAndData_(kExpectedServerUrl, _, _, _))
341       .WillOnce(PostFetchReply(gfx::Image()));
342   EXPECT_CALL(mock_favicon_service_,
343               UnableToDownloadFavicon(kExpectedServerUrl));
344 
345   large_icon_service_
346       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
347           GURL(kDummyUrl),
348           /*may_page_url_be_private=*/true, /*should_trim_page_url_path=*/false,
349           TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
350 
351   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
352                                 FAILURE_CONNECTION_ERROR));
353   task_environment_.RunUntilIdle();
354   // Verify that download failure gets recorded.
355   histogram_tester_.ExpectUniqueSample(
356       "Favicons.LargeIconService.DownloadedSize", 0, /*expected_count=*/1);
357 }
358 
TEST_F(LargeIconServiceTest,ShouldNotGetFromGoogleServerIfUnavailable)359 TEST_F(LargeIconServiceTest, ShouldNotGetFromGoogleServerIfUnavailable) {
360   ON_CALL(mock_favicon_service_,
361           WasUnableToDownloadFavicon(
362               GURL("https://t0.gstatic.com/faviconV2?client=test_chrome&nfrp=2"
363                    "&check_seen=true&size=48&min_size=16&max_size=256"
364                    "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/")))
365       .WillByDefault(Return(true));
366 
367   EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0);
368   EXPECT_CALL(*mock_image_fetcher_, FetchImageAndData_(_, _, _, _)).Times(0);
369   EXPECT_CALL(mock_favicon_service_, SetOnDemandFavicons(_, _, _, _, _))
370       .Times(0);
371 
372   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
373   large_icon_service_
374       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
375           GURL(kDummyUrl),
376           /*may_page_url_be_private=*/true, /*should_trim_page_url_path=*/false,
377           TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
378 
379   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
380                                 FAILURE_HTTP_ERROR_CACHED));
381   task_environment_.RunUntilIdle();
382   EXPECT_THAT(histogram_tester_.GetAllSamples(
383                   "Favicons.LargeIconService.DownloadedSize"),
384               IsEmpty());
385 }
386 
TEST_F(LargeIconServiceTest,ShouldNotGetFromGoogleServerIfCannotSet)387 TEST_F(LargeIconServiceTest, ShouldNotGetFromGoogleServerIfCannotSet) {
388   EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0);
389   EXPECT_CALL(mock_favicon_service_,
390               CanSetOnDemandFavicons(GURL(kDummyUrl),
391                                      favicon_base::IconType::kTouchIcon, _))
392       .WillOnce([](auto, auto, base::OnceCallback<void(bool)> callback) {
393         return base::ThreadTaskRunnerHandle::Get()->PostTask(
394             FROM_HERE, base::BindOnce(std::move(callback), false));
395       });
396 
397   EXPECT_CALL(*mock_image_fetcher_, FetchImageAndData_(_, _, _, _)).Times(0);
398   EXPECT_CALL(mock_favicon_service_, SetOnDemandFavicons(_, _, _, _, _))
399       .Times(0);
400 
401   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
402   large_icon_service_
403       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
404           GURL(kDummyUrl),
405           /*may_page_url_be_private=*/true, /*should_trim_page_url_path=*/false,
406           TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
407 
408   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
409                                 FAILURE_ICON_EXISTS_IN_DB));
410   task_environment_.RunUntilIdle();
411   EXPECT_THAT(histogram_tester_.GetAllSamples(
412                   "Favicons.LargeIconService.DownloadedSize"),
413               IsEmpty());
414 }
415 
416 class LargeIconServiceGetterTest : public LargeIconServiceTest,
417                                    public ::testing::WithParamInterface<bool> {
418  public:
LargeIconServiceGetterTest()419   LargeIconServiceGetterTest() {}
~LargeIconServiceGetterTest()420   ~LargeIconServiceGetterTest() override {}
421 
GetLargeIconOrFallbackStyleAndWaitForCallback(const GURL & page_url,int min_source_size_in_pixel,int desired_size_in_pixel)422   void GetLargeIconOrFallbackStyleAndWaitForCallback(
423       const GURL& page_url,
424       int min_source_size_in_pixel,
425       int desired_size_in_pixel) {
426     // Switch over testing two analogous functions based on the bool param.
427     if (GetParam()) {
428       large_icon_service_.GetLargeIconRawBitmapOrFallbackStyleForPageUrl(
429           page_url, min_source_size_in_pixel, desired_size_in_pixel,
430           base::BindRepeating(
431               &LargeIconServiceGetterTest::RawBitmapResultCallback,
432               base::Unretained(this)),
433           &cancelable_task_tracker_);
434     } else {
435       large_icon_service_.GetLargeIconImageOrFallbackStyleForPageUrl(
436           page_url, min_source_size_in_pixel, desired_size_in_pixel,
437           base::BindRepeating(&LargeIconServiceGetterTest::ImageResultCallback,
438                               base::Unretained(this)),
439           &cancelable_task_tracker_);
440     }
441     task_environment_.RunUntilIdle();
442   }
443 
RawBitmapResultCallback(const favicon_base::LargeIconResult & result)444   void RawBitmapResultCallback(const favicon_base::LargeIconResult& result) {
445     if (result.bitmap.is_valid()) {
446       returned_bitmap_size_ =
447           std::make_unique<gfx::Size>(result.bitmap.pixel_size);
448     }
449     StoreFallbackStyle(result.fallback_icon_style.get());
450   }
451 
ImageResultCallback(const favicon_base::LargeIconImageResult & result)452   void ImageResultCallback(const favicon_base::LargeIconImageResult& result) {
453     if (!result.image.IsEmpty()) {
454       returned_bitmap_size_ =
455           std::make_unique<gfx::Size>(result.image.ToImageSkia()->size());
456       ASSERT_TRUE(result.icon_url.is_valid());
457     }
458     StoreFallbackStyle(result.fallback_icon_style.get());
459   }
460 
StoreFallbackStyle(const favicon_base::FallbackIconStyle * fallback_style)461   void StoreFallbackStyle(
462       const favicon_base::FallbackIconStyle* fallback_style) {
463     if (fallback_style) {
464       returned_fallback_style_ =
465           std::make_unique<favicon_base::FallbackIconStyle>(*fallback_style);
466     }
467   }
468 
469   // The parameter |mock_result| needs to be passed by value otherwise the
470   // lambda injected into the mock may capture a reference to a temporary,
471   // which would cause Undefined Behaviour.
InjectMockResult(const GURL & page_url,favicon_base::FaviconRawBitmapResult mock_result)472   void InjectMockResult(const GURL& page_url,
473                         favicon_base::FaviconRawBitmapResult mock_result) {
474     ON_CALL(mock_favicon_service_,
475             GetLargestRawFaviconForPageURL(page_url, _, _, _, _))
476         .WillByDefault([=](auto, auto, auto,
477                            favicon_base::FaviconRawBitmapCallback callback,
478                            base::CancelableTaskTracker* tracker) {
479           return tracker->PostTask(
480               base::ThreadTaskRunnerHandle::Get().get(), FROM_HERE,
481               base::BindOnce(std::move(callback), mock_result));
482         });
483   }
484 
485  protected:
486   base::CancelableTaskTracker cancelable_task_tracker_;
487 
488   std::unique_ptr<favicon_base::FallbackIconStyle> returned_fallback_style_;
489   std::unique_ptr<gfx::Size> returned_bitmap_size_;
490 
491  private:
492   DISALLOW_COPY_AND_ASSIGN(LargeIconServiceGetterTest);
493 };
494 
TEST_P(LargeIconServiceGetterTest,SameSize)495 TEST_P(LargeIconServiceGetterTest, SameSize) {
496   InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(24, 24, kTestColor));
497   GetLargeIconOrFallbackStyleAndWaitForCallback(
498       GURL(kDummyUrl),
499       24,   // |min_source_size_in_pixel|
500       24);  // |desired_size_in_pixel|
501   EXPECT_EQ(gfx::Size(24, 24), *returned_bitmap_size_);
502   EXPECT_EQ(nullptr, returned_fallback_style_);
503 }
504 
TEST_P(LargeIconServiceGetterTest,ScaleDown)505 TEST_P(LargeIconServiceGetterTest, ScaleDown) {
506   InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(32, 32, kTestColor));
507   GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 24);
508   EXPECT_EQ(gfx::Size(24, 24), *returned_bitmap_size_);
509   EXPECT_EQ(nullptr, returned_fallback_style_);
510 }
511 
TEST_P(LargeIconServiceGetterTest,ScaleUp)512 TEST_P(LargeIconServiceGetterTest, ScaleUp) {
513   InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(16, 16, kTestColor));
514   GetLargeIconOrFallbackStyleAndWaitForCallback(
515       GURL(kDummyUrl),
516       14,  // Lowered requirement so stored bitmap is admitted.
517       24);
518   EXPECT_EQ(gfx::Size(24, 24), *returned_bitmap_size_);
519   EXPECT_EQ(nullptr, returned_fallback_style_);
520 }
521 
522 // |desired_size_in_pixel| == 0 means retrieve original image without scaling.
TEST_P(LargeIconServiceGetterTest,NoScale)523 TEST_P(LargeIconServiceGetterTest, NoScale) {
524   InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(24, 24, kTestColor));
525   GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 16, 0);
526   EXPECT_EQ(gfx::Size(24, 24), *returned_bitmap_size_);
527   EXPECT_EQ(nullptr, returned_fallback_style_);
528 }
529 
TEST_P(LargeIconServiceGetterTest,FallbackSinceIconTooSmall)530 TEST_P(LargeIconServiceGetterTest, FallbackSinceIconTooSmall) {
531   InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(16, 16, kTestColor));
532   GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 24);
533   EXPECT_EQ(nullptr, returned_bitmap_size_);
534   EXPECT_TRUE(HasBackgroundColor(*returned_fallback_style_, kTestColor));
535   histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize",
536                                        16, /*expected_count=*/1);
537 }
538 
TEST_P(LargeIconServiceGetterTest,FallbackSinceIconNotSquare)539 TEST_P(LargeIconServiceGetterTest, FallbackSinceIconNotSquare) {
540   InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(24, 32, kTestColor));
541   GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 24);
542   EXPECT_EQ(nullptr, returned_bitmap_size_);
543   EXPECT_TRUE(HasBackgroundColor(*returned_fallback_style_, kTestColor));
544   histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize",
545                                        24, /*expected_count=*/1);
546 }
547 
TEST_P(LargeIconServiceGetterTest,FallbackSinceIconMissing)548 TEST_P(LargeIconServiceGetterTest, FallbackSinceIconMissing) {
549   InjectMockResult(GURL(kDummyUrl), favicon_base::FaviconRawBitmapResult());
550   GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 24);
551   EXPECT_EQ(nullptr, returned_bitmap_size_);
552   EXPECT_TRUE(returned_fallback_style_->is_default_background_color);
553   histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize",
554                                        0, /*expected_count=*/1);
555 }
556 
TEST_P(LargeIconServiceGetterTest,FallbackSinceIconMissingNoScale)557 TEST_P(LargeIconServiceGetterTest, FallbackSinceIconMissingNoScale) {
558   InjectMockResult(GURL(kDummyUrl), favicon_base::FaviconRawBitmapResult());
559   GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 0);
560   EXPECT_EQ(nullptr, returned_bitmap_size_);
561   EXPECT_TRUE(returned_fallback_style_->is_default_background_color);
562   histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize",
563                                        0, /*expected_count=*/1);
564 }
565 
566 // Oddball case where we demand a high resolution icon to scale down. Generates
567 // fallback even though an icon with the final size is available.
TEST_P(LargeIconServiceGetterTest,FallbackSinceTooPicky)568 TEST_P(LargeIconServiceGetterTest, FallbackSinceTooPicky) {
569   InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(24, 24, kTestColor));
570   GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 32, 24);
571   EXPECT_EQ(nullptr, returned_bitmap_size_);
572   EXPECT_TRUE(HasBackgroundColor(*returned_fallback_style_, kTestColor));
573   histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize",
574                                        24, /*expected_count=*/1);
575 }
576 
577 // Every test will appear with suffix /0 (param false) and /1 (param true), e.g.
578 //  LargeIconServiceGetterTest.FallbackSinceTooPicky/0: get image.
579 //  LargeIconServiceGetterTest.FallbackSinceTooPicky/1: get raw bitmap.
580 INSTANTIATE_TEST_SUITE_P(All,  // Empty instatiation name.
581                          LargeIconServiceGetterTest,
582                          ::testing::Values(false, true));
583 
584 }  // namespace
585 }  // namespace favicon
586