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