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