1 // Copyright 2020 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 "chrome/browser/ui/webui/sanitized_image_source.h"
6
7 #include "base/strings/strcat.h"
8 #include "base/test/mock_callback.h"
9 #include "chrome/common/webui_url_constants.h"
10 #include "chrome/test/base/testing_profile.h"
11 #include "components/image_fetcher/core/image_decoder.h"
12 #include "content/public/test/browser_task_environment.h"
13 #include "net/http/http_status_code.h"
14 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
15 #include "services/network/test/test_url_loader_factory.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "ui/gfx/geometry/size.h"
19 #include "ui/gfx/image/image.h"
20
21 namespace {
22
MakeImage(SkColor color)23 gfx::Image MakeImage(SkColor color) {
24 SkBitmap bitmap;
25 bitmap.allocN32Pixels(5, 5);
26 bitmap.eraseColor(color);
27 return gfx::Image::CreateFrom1xBitmap(bitmap);
28 }
29
30 } // namespace
31
32 MATCHER_P(MemoryEq, other, "Eq matcher for base::RefCountedMemory contents") {
33 return arg->Equals(other);
34 }
35
36 class MockImageDecoder : public image_fetcher::ImageDecoder {
37 public:
38 MOCK_METHOD3(DecodeImage,
39 void(const std::string&,
40 const gfx::Size&,
41 image_fetcher::ImageDecodedCallback));
42 };
43
44 class SanitizedImageSourceTest : public testing::Test {
45 public:
SetUp()46 void SetUp() override {
47 profile_ = std::make_unique<TestingProfile>();
48 auto image_decoder = std::make_unique<MockImageDecoder>();
49 mock_image_decoder_ = image_decoder.get();
50 sanitized_image_source_ = std::make_unique<SanitizedImageSource>(
51 profile_.get(),
52 base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
53 &test_url_loader_factory_),
54 std::move(image_decoder));
55 }
56
TearDown()57 void TearDown() override {
58 sanitized_image_source_.reset();
59 profile_.reset();
60 test_url_loader_factory_.ClearResponses();
61 }
62
63 protected:
64 content::BrowserTaskEnvironment task_environment_;
65 std::unique_ptr<TestingProfile> profile_;
66 network::TestURLLoaderFactory test_url_loader_factory_;
67 MockImageDecoder* mock_image_decoder_;
68 std::unique_ptr<SanitizedImageSource> sanitized_image_source_;
69 };
70
71 // Verifies that the image source can handle multiple requests in parallel.
TEST_F(SanitizedImageSourceTest,MultiRequest)72 TEST_F(SanitizedImageSourceTest, MultiRequest) {
73 std::vector<std::tuple<SkColor, std::string, std::string>> data(
74 {{SK_ColorRED, "https://foo.com/img.png", "abc"},
75 {SK_ColorBLUE, "https://bar.com/img.png", "def"},
76 {SK_ColorGREEN, "https://baz.com/img.png", "ghi"}});
77
78 // Set up expectations and mock data.
79 base::MockCallback<content::URLDataSource::GotDataCallback> callback;
80 for (const auto& datum : data) {
81 SkColor color;
82 std::string url;
83 std::string body;
84 std::tie(color, url, body) = datum;
85 test_url_loader_factory_.AddResponse(url, body);
86 EXPECT_CALL(*mock_image_decoder_,
87 DecodeImage(body, gfx::Size(), testing::_))
88 .Times(1)
89 .WillOnce([color](const std::string&, const gfx::Size&,
90 image_fetcher::ImageDecodedCallback callback) {
91 std::move(callback).Run(MakeImage(color));
92 });
93 EXPECT_CALL(callback, Run(MemoryEq(MakeImage(color).As1xPNGBytes())))
94 .Times(1);
95 }
96
97 // Issue requests.
98 for (const auto& datum : data) {
99 std::string url;
100 std::tie(std::ignore, url, std::ignore) = datum;
101 sanitized_image_source_->StartDataRequest(
102 GURL(base::StrCat({chrome::kChromeUIImageURL, "?", url})),
103 content::WebContents::Getter(), callback.Get());
104 }
105 task_environment_.RunUntilIdle();
106 }
107
108 // Verifies that the image source sends back an empty image in case the external
109 // image download fails.
TEST_F(SanitizedImageSourceTest,FailedLoad)110 TEST_F(SanitizedImageSourceTest, FailedLoad) {
111 constexpr char kImageUrl[] = "https://foo.com/img.png";
112
113 // Set up expectations and mock data.
114 test_url_loader_factory_.AddResponse(kImageUrl, "", net::HTTP_NOT_FOUND);
115 EXPECT_CALL(*mock_image_decoder_,
116 DecodeImage(testing::_, testing::_, testing::_))
117 .Times(0);
118 base::MockCallback<content::URLDataSource::GotDataCallback> callback;
119 EXPECT_CALL(callback,
120 Run(MemoryEq(base::MakeRefCounted<base::RefCountedString>())))
121 .Times(1);
122
123 // Issue request.
124 sanitized_image_source_->StartDataRequest(
125 GURL(base::StrCat({chrome::kChromeUIImageURL, "?", kImageUrl})),
126 content::WebContents::Getter(), callback.Get());
127 task_environment_.RunUntilIdle();
128 }
129
130 // Verifies that the image source ignores requests with a wrong URL.
TEST_F(SanitizedImageSourceTest,WrongUrl)131 TEST_F(SanitizedImageSourceTest, WrongUrl) {
132 // Set up expectations and mock data.
133 EXPECT_CALL(*mock_image_decoder_,
134 DecodeImage(testing::_, testing::_, testing::_))
135 .Times(0);
136 base::MockCallback<content::URLDataSource::GotDataCallback> callback;
137 EXPECT_CALL(callback,
138 Run(MemoryEq(base::MakeRefCounted<base::RefCountedString>())))
139 .Times(2);
140
141 // Issue request.
142 sanitized_image_source_->StartDataRequest(
143 GURL("chrome://abc?https://foo.com/img.png"),
144 content::WebContents::Getter(), callback.Get());
145 sanitized_image_source_->StartDataRequest(
146 GURL(base::StrCat({chrome::kChromeUIImageURL, "?abc"})),
147 content::WebContents::Getter(), callback.Get());
148 task_environment_.RunUntilIdle();
149
150 EXPECT_EQ(0, test_url_loader_factory_.NumPending());
151 }
152