plugin_response_writer_unittest.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Copyright 2021 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. #include "components/pdf/browser/plugin_response_writer.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <string>
  8. #include <utility>
  9. #include "base/callback.h"
  10. #include "base/callback_helpers.h"
  11. #include "base/memory/scoped_refptr.h"
  12. #include "base/run_loop.h"
  13. #include "base/test/mock_callback.h"
  14. #include "base/test/task_environment.h"
  15. #include "components/pdf/browser/mock_url_loader_client.h"
  16. #include "mojo/public/cpp/bindings/receiver.h"
  17. #include "mojo/public/cpp/system/data_pipe.h"
  18. #include "mojo/public/cpp/system/data_pipe_drainer.h"
  19. #include "net/base/net_errors.h"
  20. #include "net/http/http_response_headers.h"
  21. #include "services/network/public/cpp/url_loader_completion_status.h"
  22. #include "services/network/public/mojom/url_response_head.mojom.h"
  23. #include "testing/gmock/include/gmock/gmock.h"
  24. #include "testing/gtest/include/gtest/gtest.h"
  25. #include "url/gurl.h"
  26. namespace pdf {
  27. namespace {
  28. using ::testing::HasSubstr;
  29. using ::testing::NiceMock;
  30. using ::testing::Not;
  31. class BodyDrainer {
  32. public:
  33. explicit BodyDrainer(mojo::ScopedDataPipeConsumerHandle body)
  34. : drainer_(&drainer_client_, std::move(body)) {}
  35. const std::string& content() const { return drainer_client_.content; }
  36. void WaitComplete() {
  37. if (drainer_client_.is_complete)
  38. return;
  39. base::RunLoop run_loop;
  40. ASSERT_FALSE(drainer_client_.quit_closure);
  41. drainer_client_.quit_closure = run_loop.QuitClosure();
  42. run_loop.Run();
  43. EXPECT_TRUE(drainer_client_.is_complete);
  44. }
  45. private:
  46. struct DrainerClient : public mojo::DataPipeDrainer::Client {
  47. void OnDataAvailable(const void* data, size_t num_bytes) override {
  48. content.append(reinterpret_cast<const char*>(data), num_bytes);
  49. }
  50. void OnDataComplete() override {
  51. is_complete = true;
  52. if (quit_closure)
  53. std::move(quit_closure).Run();
  54. }
  55. std::string content;
  56. bool is_complete = false;
  57. base::OnceClosure quit_closure;
  58. };
  59. DrainerClient drainer_client_;
  60. mojo::DataPipeDrainer drainer_;
  61. };
  62. class PluginResponseWriterTest : public testing::Test {
  63. protected:
  64. PluginResponseWriterTest() {
  65. ON_CALL(mock_client_, OnReceiveResponse)
  66. .WillByDefault([this](network::mojom::URLResponseHeadPtr head,
  67. mojo::ScopedDataPipeConsumerHandle body) {
  68. body_drainer_ = std::make_unique<BodyDrainer>(std::move(body));
  69. });
  70. }
  71. std::unique_ptr<PluginResponseWriter> NewPluginResponseWriter(
  72. const PdfStreamDelegate::StreamInfo& stream_info) {
  73. return std::make_unique<PluginResponseWriter>(
  74. stream_info, client_receiver_.BindNewPipeAndPassRemote());
  75. }
  76. // Returns the generated response after sending it.
  77. std::string GenerateResponse(const PdfStreamDelegate::StreamInfo& stream) {
  78. auto response_writer = NewPluginResponseWriter(stream);
  79. base::RunLoop run_loop;
  80. EXPECT_CALL(mock_client_, OnComplete).WillOnce([&run_loop]() {
  81. run_loop.Quit();
  82. });
  83. response_writer->Start(base::DoNothing());
  84. run_loop.Run();
  85. // Waiting for `URLLoaderClient::OnComplete()` ensures `body_drainer_` is
  86. // set, but the data pipe may still have unread data.
  87. body_drainer_->WaitComplete();
  88. return body_drainer_->content();
  89. }
  90. base::test::TaskEnvironment task_environment_;
  91. NiceMock<MockURLLoaderClient> mock_client_;
  92. mojo::Receiver<network::mojom::URLLoaderClient> client_receiver_{
  93. &mock_client_};
  94. std::unique_ptr<BodyDrainer> body_drainer_;
  95. };
  96. } // namespace
  97. TEST_F(PluginResponseWriterTest, Start) {
  98. const std::string kFakeScript = "fake-script";
  99. PdfStreamDelegate::StreamInfo stream;
  100. stream.stream_url = GURL("chrome-extension://id/stream-url");
  101. stream.original_url = GURL("https://example.test/fake.pdf");
  102. stream.injected_script = &kFakeScript;
  103. stream.background_color = SK_ColorGREEN;
  104. stream.full_frame = true;
  105. stream.allow_javascript = true;
  106. auto response_writer = NewPluginResponseWriter(stream);
  107. base::RunLoop run_loop;
  108. {
  109. // Note that `URLLoaderClient` operations are received on a separate
  110. // sequence, and only are ordered with respect to each other.
  111. testing::InSequence in_sequence;
  112. EXPECT_CALL(mock_client_, OnReceiveResponse)
  113. .WillOnce([this](network::mojom::URLResponseHeadPtr head,
  114. mojo::ScopedDataPipeConsumerHandle body) {
  115. EXPECT_EQ(200, head->headers->response_code());
  116. EXPECT_EQ("text/html", head->mime_type);
  117. body_drainer_ = std::make_unique<BodyDrainer>(std::move(body));
  118. });
  119. EXPECT_CALL(mock_client_, OnComplete)
  120. .WillOnce(
  121. [&run_loop](const network::URLLoaderCompletionStatus& status) {
  122. EXPECT_EQ(net::OK, status.error_code);
  123. run_loop.Quit();
  124. });
  125. }
  126. // Note that `done_callback` is not ordered with respect to the
  127. // `URLLoaderClient` operations.
  128. base::MockCallback<base::OnceClosure> done_callback;
  129. EXPECT_CALL(done_callback, Run);
  130. response_writer->Start(done_callback.Get());
  131. run_loop.Run();
  132. // Waiting for `URLLoaderClient::OnComplete()` ensures `body_drainer_` is set,
  133. // but the data pipe may still have unread data.
  134. ASSERT_TRUE(body_drainer_);
  135. body_drainer_->WaitComplete();
  136. EXPECT_THAT(body_drainer_->content(),
  137. HasSubstr("src=\"chrome-extension://id/stream-url\""));
  138. EXPECT_THAT(body_drainer_->content(),
  139. HasSubstr("original-url=\"https://example.test/fake.pdf\""));
  140. EXPECT_THAT(body_drainer_->content(), HasSubstr(kFakeScript));
  141. EXPECT_THAT(body_drainer_->content(),
  142. HasSubstr("background-color=\"4278255360\""));
  143. EXPECT_THAT(body_drainer_->content(), HasSubstr("javascript=\"allow\""));
  144. EXPECT_THAT(body_drainer_->content(), HasSubstr("full-frame"));
  145. }
  146. TEST_F(PluginResponseWriterTest, StartWithUnescapedUrls) {
  147. PdfStreamDelegate::StreamInfo stream;
  148. stream.stream_url = GURL("chrome-extension://id/stream-url\"");
  149. stream.original_url = GURL("https://example.test/\"fake.pdf");
  150. std::string response = GenerateResponse(stream);
  151. EXPECT_THAT(response,
  152. HasSubstr("src=\"chrome-extension://id/stream-url%22\""));
  153. EXPECT_THAT(response,
  154. HasSubstr("original-url=\"https://example.test/%22fake.pdf\""));
  155. }
  156. TEST_F(PluginResponseWriterTest, StartForPrintPreview) {
  157. PdfStreamDelegate::StreamInfo stream;
  158. stream.stream_url = GURL("chrome-untrusted://print/1/0/print.pdf");
  159. stream.original_url = GURL("chrome-untrusted://print/1/0/print.pdf");
  160. std::string response = GenerateResponse(stream);
  161. EXPECT_THAT(response,
  162. HasSubstr("src=\"chrome-untrusted://print/1/0/print.pdf\""));
  163. EXPECT_THAT(
  164. response,
  165. HasSubstr("original-url=\"chrome-untrusted://print/1/0/print.pdf\""));
  166. }
  167. TEST_F(PluginResponseWriterTest, StartWithoutInjectedScript) {
  168. PdfStreamDelegate::StreamInfo stream;
  169. stream.injected_script = nullptr;
  170. std::string response = GenerateResponse(stream);
  171. EXPECT_THAT(response, Not(HasSubstr("fake-script")));
  172. }
  173. TEST_F(PluginResponseWriterTest, StartWithBackgroundColor) {
  174. PdfStreamDelegate::StreamInfo stream;
  175. stream.background_color = SK_ColorBLUE;
  176. std::string response = GenerateResponse(stream);
  177. EXPECT_THAT(response, HasSubstr("background-color=\"4278190335\""));
  178. }
  179. TEST_F(PluginResponseWriterTest, StartWithNonFullFrame) {
  180. PdfStreamDelegate::StreamInfo stream;
  181. stream.full_frame = false;
  182. std::string response = GenerateResponse(stream);
  183. EXPECT_THAT(response, Not(HasSubstr("full-frame")));
  184. }
  185. TEST_F(PluginResponseWriterTest, StartWithJavaScriptDisabled) {
  186. PdfStreamDelegate::StreamInfo stream;
  187. stream.allow_javascript = false;
  188. std::string response = GenerateResponse(stream);
  189. EXPECT_THAT(response, HasSubstr("javascript=\"block\""));
  190. }
  191. } // namespace pdf