| // Copyright 2016 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/renderer/loader/url_loader_client_impl.h" | 
 |  | 
 | #include <vector> | 
 | #include "base/run_loop.h" | 
 | #include "base/test/task_environment.h" | 
 | #include "content/renderer/loader/navigation_response_override_parameters.h" | 
 | #include "content/renderer/loader/resource_dispatcher.h" | 
 | #include "content/renderer/loader/test_request_peer.h" | 
 | #include "mojo/public/cpp/bindings/pending_receiver.h" | 
 | #include "mojo/public/cpp/bindings/pending_remote.h" | 
 | #include "mojo/public/cpp/bindings/remote.h" | 
 | #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" | 
 | #include "net/url_request/redirect_info.h" | 
 | #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" | 
 | #include "services/network/public/mojom/url_loader_factory.mojom.h" | 
 | #include "services/network/public/mojom/url_response_head.mojom.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h" | 
 | #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr size_t kDataPipeCapacity = 4096; | 
 |  | 
 | std::string ReadOneChunk(mojo::ScopedDataPipeConsumerHandle* handle) { | 
 |   char buffer[kDataPipeCapacity]; | 
 |   uint32_t read_bytes = kDataPipeCapacity; | 
 |   MojoResult result = | 
 |       (*handle)->ReadData(buffer, &read_bytes, MOJO_READ_DATA_FLAG_NONE); | 
 |   if (result != MOJO_RESULT_OK) | 
 |     return ""; | 
 |   return std::string(buffer, read_bytes); | 
 | } | 
 |  | 
 | std::string GetRequestPeerContextBody(TestRequestPeer::Context* context) { | 
 |   if (context->body_handle) { | 
 |     context->data += ReadOneChunk(&context->body_handle); | 
 |   } | 
 |   return context->data; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | class URLLoaderClientImplTest : public ::testing::Test, | 
 |                                 public network::mojom::URLLoaderFactory { | 
 |  protected: | 
 |   URLLoaderClientImplTest() : dispatcher_(new ResourceDispatcher()) { | 
 |     auto request = std::make_unique<network::ResourceRequest>(); | 
 |     request_id_ = dispatcher_->StartAsync( | 
 |         std::move(request), 0 /* loader_option */, | 
 |         blink::scheduler::GetSingleThreadTaskRunnerForTesting(), | 
 |         TRAFFIC_ANNOTATION_FOR_TESTS, false, | 
 |         std::make_unique<TestRequestPeer>(dispatcher_.get(), | 
 |                                           &request_peer_context_), | 
 |         base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(this), | 
 |         std::vector<std::unique_ptr<blink::URLLoaderThrottle>>(), | 
 |         nullptr /* navigation_response_override_params */); | 
 |     request_peer_context_.request_id = request_id_; | 
 |  | 
 |     base::RunLoop().RunUntilIdle(); | 
 |     EXPECT_TRUE(url_loader_client_); | 
 |   } | 
 |  | 
 |   void TearDown() override { url_loader_client_.reset(); } | 
 |  | 
 |   void CreateLoaderAndStart( | 
 |       mojo::PendingReceiver<network::mojom::URLLoader> receiver, | 
 |       int32_t routing_id, | 
 |       int32_t request_id, | 
 |       uint32_t options, | 
 |       const network::ResourceRequest& url_request, | 
 |       mojo::PendingRemote<network::mojom::URLLoaderClient> client, | 
 |       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) | 
 |       override { | 
 |     url_loader_client_.Bind(std::move(client)); | 
 |   } | 
 |  | 
 |   void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) | 
 |       override { | 
 |     NOTREACHED(); | 
 |   } | 
 |  | 
 |   static MojoCreateDataPipeOptions DataPipeOptions() { | 
 |     MojoCreateDataPipeOptions options; | 
 |     options.struct_size = sizeof(MojoCreateDataPipeOptions); | 
 |     options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; | 
 |     options.element_num_bytes = 1; | 
 |     options.capacity_num_bytes = kDataPipeCapacity; | 
 |     return options; | 
 |   } | 
 |  | 
 |   base::test::SingleThreadTaskEnvironment task_environment_; | 
 |   std::unique_ptr<ResourceDispatcher> dispatcher_; | 
 |   TestRequestPeer::Context request_peer_context_; | 
 |   int request_id_ = 0; | 
 |   mojo::Remote<network::mojom::URLLoaderClient> url_loader_client_; | 
 | }; | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, OnReceiveResponse) { | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, ResponseBody) { | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |  | 
 |   mojo::DataPipe data_pipe(DataPipeOptions()); | 
 |   url_loader_client_->OnStartLoadingResponseBody( | 
 |       std::move(data_pipe.consumer_handle)); | 
 |   uint32_t size = 5; | 
 |   MojoResult result = data_pipe.producer_handle->WriteData( | 
 |       "hello", &size, MOJO_WRITE_DATA_FLAG_NONE); | 
 |   ASSERT_EQ(MOJO_RESULT_OK, result); | 
 |   EXPECT_EQ(5u, size); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_)); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, OnReceiveRedirect) { | 
 |   net::RedirectInfo redirect_info; | 
 |  | 
 |   url_loader_client_->OnReceiveRedirect(redirect_info, | 
 |                                         network::mojom::URLResponseHead::New()); | 
 |  | 
 |   EXPECT_EQ(0, request_peer_context_.seen_redirects); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ(1, request_peer_context_.seen_redirects); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, OnReceiveCachedMetadata) { | 
 |   std::vector<uint8_t> data; | 
 |   data.push_back('a'); | 
 |   mojo_base::BigBuffer metadata(data); | 
 |  | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   url_loader_client_->OnReceiveCachedMetadata(std::move(metadata)); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_EQ(0u, request_peer_context_.cached_metadata.size()); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   ASSERT_EQ(1u, request_peer_context_.cached_metadata.size()); | 
 |   EXPECT_EQ('a', request_peer_context_.cached_metadata.data()[0]); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, OnTransferSizeUpdated) { | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   url_loader_client_->OnTransferSizeUpdated(4); | 
 |   url_loader_client_->OnTransferSizeUpdated(4); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_EQ(8, request_peer_context_.total_encoded_data_length); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, OnCompleteWithResponseBody) { | 
 |   network::URLLoaderCompletionStatus status; | 
 |  | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   mojo::DataPipe data_pipe(DataPipeOptions()); | 
 |   url_loader_client_->OnStartLoadingResponseBody( | 
 |       std::move(data_pipe.consumer_handle)); | 
 |   uint32_t size = 5; | 
 |   MojoResult result = data_pipe.producer_handle->WriteData( | 
 |       "hello", &size, MOJO_WRITE_DATA_FLAG_NONE); | 
 |   ASSERT_EQ(MOJO_RESULT_OK, result); | 
 |   EXPECT_EQ(5u, size); | 
 |   data_pipe.producer_handle.reset(); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_)); | 
 |  | 
 |   url_loader_client_->OnComplete(status); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |  | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_TRUE(request_peer_context_.complete); | 
 | } | 
 |  | 
 | // Due to the lack of ordering guarantee, it is possible that the response body | 
 | // bytes arrives after the completion message. URLLoaderClientImpl should | 
 | // restore the order. | 
 | TEST_F(URLLoaderClientImplTest, OnCompleteShouldBeTheLastMessage) { | 
 |   network::URLLoaderCompletionStatus status; | 
 |  | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   mojo::DataPipe data_pipe(DataPipeOptions()); | 
 |   url_loader_client_->OnStartLoadingResponseBody( | 
 |       std::move(data_pipe.consumer_handle)); | 
 |   url_loader_client_->OnComplete(status); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_TRUE(request_peer_context_.complete); | 
 |  | 
 |   uint32_t size = 5; | 
 |   MojoResult result = data_pipe.producer_handle->WriteData( | 
 |       "hello", &size, MOJO_WRITE_DATA_FLAG_NONE); | 
 |   ASSERT_EQ(MOJO_RESULT_OK, result); | 
 |   EXPECT_EQ(5u, size); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_)); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, CancelOnReceiveResponse) { | 
 |   request_peer_context_.cancel_on_receive_response = true; | 
 |  | 
 |   network::URLLoaderCompletionStatus status; | 
 |  | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   mojo::DataPipe data_pipe(DataPipeOptions()); | 
 |   url_loader_client_->OnStartLoadingResponseBody( | 
 |       std::move(data_pipe.consumer_handle)); | 
 |   url_loader_client_->OnComplete(status); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_FALSE(request_peer_context_.cancelled); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_TRUE(request_peer_context_.cancelled); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, Defer) { | 
 |   network::URLLoaderCompletionStatus status; | 
 |  | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   mojo::DataPipe data_pipe; | 
 |   data_pipe.producer_handle.reset();  // Empty body. | 
 |   url_loader_client_->OnStartLoadingResponseBody( | 
 |       std::move(data_pipe.consumer_handle)); | 
 |   url_loader_client_->OnComplete(status); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, true); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, false); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_TRUE(request_peer_context_.complete); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, DeferWithResponseBody) { | 
 |   network::URLLoaderCompletionStatus status; | 
 |  | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   mojo::DataPipe data_pipe(DataPipeOptions()); | 
 |   uint32_t size = 5; | 
 |   MojoResult result = data_pipe.producer_handle->WriteData( | 
 |       "hello", &size, MOJO_WRITE_DATA_FLAG_NONE); | 
 |   ASSERT_EQ(MOJO_RESULT_OK, result); | 
 |   EXPECT_EQ(5u, size); | 
 |   data_pipe.producer_handle.reset(); | 
 |  | 
 |   url_loader_client_->OnStartLoadingResponseBody( | 
 |       std::move(data_pipe.consumer_handle)); | 
 |   url_loader_client_->OnComplete(status); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, true); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, false); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_TRUE(request_peer_context_.complete); | 
 |   EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_)); | 
 | } | 
 |  | 
 | // As "transfer size update" message is handled specially in the implementation, | 
 | // we have a separate test. | 
 | TEST_F(URLLoaderClientImplTest, DeferWithTransferSizeUpdated) { | 
 |   network::URLLoaderCompletionStatus status; | 
 |  | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   mojo::DataPipe data_pipe(DataPipeOptions()); | 
 |   uint32_t size = 5; | 
 |   MojoResult result = data_pipe.producer_handle->WriteData( | 
 |       "hello", &size, MOJO_WRITE_DATA_FLAG_NONE); | 
 |   ASSERT_EQ(MOJO_RESULT_OK, result); | 
 |   EXPECT_EQ(5u, size); | 
 |   data_pipe.producer_handle.reset(); | 
 |  | 
 |   url_loader_client_->OnStartLoadingResponseBody( | 
 |       std::move(data_pipe.consumer_handle)); | 
 |   url_loader_client_->OnTransferSizeUpdated(4); | 
 |   url_loader_client_->OnComplete(status); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, true); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, false); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_TRUE(request_peer_context_.complete); | 
 |   EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_EQ(4, request_peer_context_.total_encoded_data_length); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, SetDeferredDuringFlushingDeferredMessage) { | 
 |   request_peer_context_.defer_on_redirect = true; | 
 |  | 
 |   net::RedirectInfo redirect_info; | 
 |   network::URLLoaderCompletionStatus status; | 
 |  | 
 |   url_loader_client_->OnReceiveRedirect(redirect_info, | 
 |                                         network::mojom::URLResponseHead::New()); | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   mojo::DataPipe data_pipe(DataPipeOptions()); | 
 |   uint32_t size = 5; | 
 |   MojoResult result = data_pipe.producer_handle->WriteData( | 
 |       "hello", &size, MOJO_WRITE_DATA_FLAG_NONE); | 
 |   ASSERT_EQ(MOJO_RESULT_OK, result); | 
 |   EXPECT_EQ(5u, size); | 
 |   data_pipe.producer_handle.reset(); | 
 |  | 
 |   url_loader_client_->OnStartLoadingResponseBody( | 
 |       std::move(data_pipe.consumer_handle)); | 
 |   url_loader_client_->OnTransferSizeUpdated(4); | 
 |   url_loader_client_->OnComplete(status); | 
 |  | 
 |   EXPECT_EQ(0, request_peer_context_.seen_redirects); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, true); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ(0, request_peer_context_.seen_redirects); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, false); | 
 |   EXPECT_EQ(0, request_peer_context_.seen_redirects); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ(1, request_peer_context_.seen_redirects); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |   EXPECT_FALSE(request_peer_context_.cancelled); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, false); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ(1, request_peer_context_.seen_redirects); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_TRUE(request_peer_context_.complete); | 
 |   EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_)); | 
 |   EXPECT_EQ(4, request_peer_context_.total_encoded_data_length); | 
 |   EXPECT_FALSE(request_peer_context_.cancelled); | 
 | } | 
 |  | 
 | TEST_F(URLLoaderClientImplTest, | 
 |        SetDeferredDuringFlushingDeferredMessageOnTransferSizeUpdated) { | 
 |   request_peer_context_.defer_on_transfer_size_updated = true; | 
 |  | 
 |   network::URLLoaderCompletionStatus status; | 
 |  | 
 |   url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); | 
 |   mojo::DataPipe data_pipe; | 
 |   data_pipe.producer_handle.reset();  // Empty body. | 
 |   url_loader_client_->OnStartLoadingResponseBody( | 
 |       std::move(data_pipe.consumer_handle)); | 
 |  | 
 |   url_loader_client_->OnTransferSizeUpdated(4); | 
 |   url_loader_client_->OnComplete(status); | 
 |  | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, true); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, false); | 
 |   EXPECT_FALSE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ(0, request_peer_context_.total_encoded_data_length); | 
 |  | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_FALSE(request_peer_context_.complete); | 
 |   EXPECT_EQ(4, request_peer_context_.total_encoded_data_length); | 
 |   EXPECT_FALSE(request_peer_context_.cancelled); | 
 |  | 
 |   dispatcher_->SetDefersLoading(request_id_, false); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_TRUE(request_peer_context_.received_response); | 
 |   EXPECT_TRUE(request_peer_context_.complete); | 
 |   EXPECT_EQ(4, request_peer_context_.total_encoded_data_length); | 
 |   EXPECT_FALSE(request_peer_context_.cancelled); | 
 | } | 
 |  | 
 | }  // namespace content |