1 // Copyright 2018 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 "content/browser/devtools/devtools_url_loader_interceptor.h"
6 #include "base/barrier_closure.h"
7 #include "base/base64.h"
8 #include "base/bind.h"
9 #include "base/no_destructor.h"
10 #include "base/strings/pattern.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "base/unguessable_token.h"
15 #include "content/browser/devtools/protocol/network.h"
16 #include "content/browser/devtools/protocol/network_handler.h"
17 #include "content/browser/loader/download_utils_impl.h"
18 #include "content/browser/renderer_host/frame_tree_node.h"
19 #include "content/public/browser/browser_task_traits.h"
20 #include "content/public/browser/storage_partition.h"
21 #include "content/public/common/content_client.h"
22 #include "mojo/public/cpp/bindings/receiver.h"
23 #include "mojo/public/cpp/bindings/receiver_set.h"
24 #include "mojo/public/cpp/system/data_pipe_drainer.h"
25 #include "net/base/load_flags.h"
26 #include "net/base/mime_sniffer.h"
27 #include "net/cookies/cookie_access_result.h"
28 #include "net/cookies/cookie_util.h"
29 #include "net/http/http_util.h"
30 #include "net/url_request/redirect_info.h"
31 #include "net/url_request/redirect_util.h"
32 #include "net/url_request/referrer_policy.h"
33 #include "services/network/public/cpp/cors/cors.h"
34 #include "services/network/public/cpp/resource_request_body.h"
35 #include "services/network/public/mojom/network_context.mojom.h"
36 #include "third_party/blink/public/platform/resource_request_blocked_reason.h"
37 
38 namespace content {
39 
40 InterceptedRequestInfo::InterceptedRequestInfo() = default;
41 
42 InterceptedRequestInfo::~InterceptedRequestInfo() = default;
43 
AuthChallengeResponse(ResponseType response_type)44 DevToolsURLLoaderInterceptor::AuthChallengeResponse::AuthChallengeResponse(
45     ResponseType response_type)
46     : response_type(response_type) {
47   DCHECK_NE(kProvideCredentials, response_type);
48 }
49 
AuthChallengeResponse(const base::string16 & username,const base::string16 & password)50 DevToolsURLLoaderInterceptor::AuthChallengeResponse::AuthChallengeResponse(
51     const base::string16& username,
52     const base::string16& password)
53     : response_type(kProvideCredentials), credentials(username, password) {}
54 
FilterEntry(const base::UnguessableToken & target_id,std::vector<Pattern> patterns,RequestInterceptedCallback callback)55 DevToolsURLLoaderInterceptor::FilterEntry::FilterEntry(
56     const base::UnguessableToken& target_id,
57     std::vector<Pattern> patterns,
58     RequestInterceptedCallback callback)
59     : target_id(target_id),
60       patterns(std::move(patterns)),
61       callback(std::move(callback)) {}
62 
63 DevToolsURLLoaderInterceptor::FilterEntry::FilterEntry(FilterEntry&&) = default;
64 DevToolsURLLoaderInterceptor::FilterEntry::~FilterEntry() = default;
65 
66 DevToolsURLLoaderInterceptor::Modifications::Modifications() = default;
67 
Modifications(net::Error error_reason)68 DevToolsURLLoaderInterceptor::Modifications::Modifications(
69     net::Error error_reason)
70     : error_reason(error_reason) {}
71 
Modifications(scoped_refptr<net::HttpResponseHeaders> response_headers,scoped_refptr<base::RefCountedMemory> response_body)72 DevToolsURLLoaderInterceptor::Modifications::Modifications(
73     scoped_refptr<net::HttpResponseHeaders> response_headers,
74     scoped_refptr<base::RefCountedMemory> response_body)
75     : response_headers(std::move(response_headers)),
76       response_body(std::move(response_body)) {}
77 
Modifications(std::unique_ptr<AuthChallengeResponse> auth_challenge_response)78 DevToolsURLLoaderInterceptor::Modifications::Modifications(
79     std::unique_ptr<AuthChallengeResponse> auth_challenge_response)
80     : auth_challenge_response(std::move(auth_challenge_response)) {}
81 
Modifications(protocol::Maybe<std::string> modified_url,protocol::Maybe<std::string> modified_method,protocol::Maybe<protocol::Binary> modified_post_data,std::unique_ptr<HeadersVector> modified_headers)82 DevToolsURLLoaderInterceptor::Modifications::Modifications(
83     protocol::Maybe<std::string> modified_url,
84     protocol::Maybe<std::string> modified_method,
85     protocol::Maybe<protocol::Binary> modified_post_data,
86     std::unique_ptr<HeadersVector> modified_headers)
87     : modified_url(std::move(modified_url)),
88       modified_method(std::move(modified_method)),
89       modified_post_data(std::move(modified_post_data)),
90       modified_headers(std::move(modified_headers)) {}
91 
Modifications(base::Optional<net::Error> error_reason,scoped_refptr<net::HttpResponseHeaders> response_headers,scoped_refptr<base::RefCountedMemory> response_body,size_t body_offset,protocol::Maybe<std::string> modified_url,protocol::Maybe<std::string> modified_method,protocol::Maybe<protocol::Binary> modified_post_data,std::unique_ptr<HeadersVector> modified_headers,std::unique_ptr<AuthChallengeResponse> auth_challenge_response)92 DevToolsURLLoaderInterceptor::Modifications::Modifications(
93     base::Optional<net::Error> error_reason,
94     scoped_refptr<net::HttpResponseHeaders> response_headers,
95     scoped_refptr<base::RefCountedMemory> response_body,
96     size_t body_offset,
97     protocol::Maybe<std::string> modified_url,
98     protocol::Maybe<std::string> modified_method,
99     protocol::Maybe<protocol::Binary> modified_post_data,
100     std::unique_ptr<HeadersVector> modified_headers,
101     std::unique_ptr<AuthChallengeResponse> auth_challenge_response)
102     : error_reason(std::move(error_reason)),
103       response_headers(std::move(response_headers)),
104       response_body(std::move(response_body)),
105       body_offset(body_offset),
106       modified_url(std::move(modified_url)),
107       modified_method(std::move(modified_method)),
108       modified_post_data(std::move(modified_post_data)),
109       modified_headers(std::move(modified_headers)),
110       auth_challenge_response(std::move(auth_challenge_response)) {}
111 
112 DevToolsURLLoaderInterceptor::Modifications::~Modifications() = default;
113 
114 DevToolsURLLoaderInterceptor::Pattern::~Pattern() = default;
115 
116 DevToolsURLLoaderInterceptor::Pattern::Pattern(const Pattern& other) = default;
117 
Pattern(const std::string & url_pattern,base::flat_set<blink::mojom::ResourceType> resource_types,InterceptionStage interception_stage)118 DevToolsURLLoaderInterceptor::Pattern::Pattern(
119     const std::string& url_pattern,
120     base::flat_set<blink::mojom::ResourceType> resource_types,
121     InterceptionStage interception_stage)
122     : url_pattern(url_pattern),
123       resource_types(std::move(resource_types)),
124       interception_stage(interception_stage) {}
125 
Matches(const std::string & url,blink::mojom::ResourceType resource_type) const126 bool DevToolsURLLoaderInterceptor::Pattern::Matches(
127     const std::string& url,
128     blink::mojom::ResourceType resource_type) const {
129   if (!resource_types.empty() &&
130       resource_types.find(resource_type) == resource_types.end()) {
131     return false;
132   }
133   return base::MatchPattern(url, url_pattern);
134 }
135 
136 struct CreateLoaderParameters {
CreateLoaderParameterscontent::CreateLoaderParameters137   CreateLoaderParameters(
138       int32_t routing_id,
139       int32_t request_id,
140       uint32_t options,
141       network::ResourceRequest request,
142       net::MutableNetworkTrafficAnnotationTag traffic_annotation)
143       : routing_id(routing_id),
144         request_id(request_id),
145         options(options),
146         request(request),
147         traffic_annotation(traffic_annotation) {}
148 
149   const int32_t routing_id;
150   const int32_t request_id;
151   const uint32_t options;
152   network::ResourceRequest request;
153   const net::MutableNetworkTrafficAnnotationTag traffic_annotation;
154 };
155 
156 namespace {
157 
158 using RequestInterceptedCallback =
159     DevToolsURLLoaderInterceptor::RequestInterceptedCallback;
160 using ContinueInterceptedRequestCallback =
161     DevToolsURLLoaderInterceptor::ContinueInterceptedRequestCallback;
162 using GetResponseBodyCallback =
163     DevToolsURLLoaderInterceptor::GetResponseBodyForInterceptionCallback;
164 using TakeResponseBodyPipeCallback =
165     DevToolsURLLoaderInterceptor::TakeResponseBodyPipeCallback;
166 using Modifications = DevToolsURLLoaderInterceptor::Modifications;
167 using InterceptionStage = DevToolsURLLoaderInterceptor::InterceptionStage;
168 using protocol::Response;
169 using GlobalRequestId = std::tuple<int32_t, int32_t, int32_t>;
170 using network::mojom::CredentialsMode;
171 using network::mojom::FetchResponseType;
172 
173 class BodyReader : public mojo::DataPipeDrainer::Client {
174  public:
BodyReader(base::OnceClosure download_complete_callback)175   explicit BodyReader(base::OnceClosure download_complete_callback)
176       : download_complete_callback_(std::move(download_complete_callback)),
177         body_(base::MakeRefCounted<base::RefCountedString>()) {}
178 
179   void StartReading(mojo::ScopedDataPipeConsumerHandle body);
180 
AddCallback(std::unique_ptr<GetResponseBodyCallback> callback)181   void AddCallback(std::unique_ptr<GetResponseBodyCallback> callback) {
182     if (data_complete_) {
183       DCHECK(callbacks_.empty());
184       callback->sendSuccess(encoded_body_, true);
185       return;
186     }
187     callbacks_.push_back(std::move(callback));
188   }
189 
data_complete() const190   bool data_complete() const { return data_complete_; }
191 
body() const192   scoped_refptr<base::RefCountedMemory> body() const {
193     DCHECK(data_complete_);
194     return body_;
195   }
196 
CancelWithError(std::string error)197   void CancelWithError(std::string error) {
198     for (auto& cb : callbacks_)
199       cb->sendFailure(Response::ServerError(error));
200     callbacks_.clear();
201   }
202 
203  private:
OnDataAvailable(const void * data,size_t num_bytes)204   void OnDataAvailable(const void* data, size_t num_bytes) override {
205     DCHECK(!data_complete_);
206     body_->data().append(
207         std::string(static_cast<const char*>(data), num_bytes));
208   }
209 
210   void OnDataComplete() override;
211 
212   std::unique_ptr<mojo::DataPipeDrainer> body_pipe_drainer_;
213   std::vector<std::unique_ptr<GetResponseBodyCallback>> callbacks_;
214   base::OnceClosure download_complete_callback_;
215   scoped_refptr<base::RefCountedString> body_;
216   std::string encoded_body_;
217   bool data_complete_ = false;
218 };
219 
StartReading(mojo::ScopedDataPipeConsumerHandle body)220 void BodyReader::StartReading(mojo::ScopedDataPipeConsumerHandle body) {
221   DCHECK(!callbacks_.empty());
222   DCHECK(!body_pipe_drainer_);
223   DCHECK(!data_complete_);
224 
225   body_pipe_drainer_.reset(new mojo::DataPipeDrainer(this, std::move(body)));
226 }
227 
OnDataComplete()228 void BodyReader::OnDataComplete() {
229   DCHECK(!data_complete_);
230   data_complete_ = true;
231   body_pipe_drainer_.reset();
232   // TODO(caseq): only encode if necessary.
233   base::Base64Encode(body_->data(), &encoded_body_);
234   for (auto& cb : callbacks_)
235     cb->sendSuccess(encoded_body_, true);
236   callbacks_.clear();
237   std::move(download_complete_callback_).Run();
238 }
239 
240 struct ResponseMetadata {
241   ResponseMetadata() = default;
ResponseMetadatacontent::__anon044a4f3b0111::ResponseMetadata242   explicit ResponseMetadata(network::mojom::URLResponseHeadPtr head)
243       : head(std::move(head)) {}
244 
245   network::mojom::URLResponseHeadPtr head =
246       network::mojom::URLResponseHead::New();
247   std::unique_ptr<net::RedirectInfo> redirect_info;
248   mojo_base::BigBuffer cached_metadata;
249   size_t encoded_length = 0;
250   size_t transfer_size = 0;
251   network::URLLoaderCompletionStatus status;
252 };
253 
254 }  // namespace
255 
256 class InterceptionJob : public network::mojom::URLLoaderClient,
257                         public network::mojom::URLLoader {
258  public:
FindByRequestId(const GlobalRequestId & global_req_id)259   static InterceptionJob* FindByRequestId(
260       const GlobalRequestId& global_req_id) {
261     const auto& map = GetInterceptionJobMap();
262     auto it = map.find(global_req_id);
263     return it == map.end() ? nullptr : it->second;
264   }
265 
266   InterceptionJob(
267       DevToolsURLLoaderInterceptor* interceptor,
268       const std::string& id,
269       const base::UnguessableToken& frame_token,
270       int32_t process_id,
271       const base::Optional<std::string>& renderer_request_id,
272       std::unique_ptr<CreateLoaderParameters> create_loader_params,
273       bool is_download,
274       mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
275       mojo::PendingRemote<network::mojom::URLLoaderClient> client,
276       mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,
277       mojo::PendingRemote<network::mojom::CookieManager> cookie_manager);
278 
279   void GetResponseBody(std::unique_ptr<GetResponseBodyCallback> callback);
280   void TakeResponseBodyPipe(TakeResponseBodyPipeCallback callback);
281   void ContinueInterceptedRequest(
282       std::unique_ptr<Modifications> modifications,
283       std::unique_ptr<ContinueInterceptedRequestCallback> callback);
284   void Detach();
285 
286   void OnAuthRequest(
287       const net::AuthChallengeInfo& auth_info,
288       DevToolsURLLoaderInterceptor::HandleAuthRequestCallback callback);
289 
290  private:
GetInterceptionJobMap()291   static std::map<GlobalRequestId, InterceptionJob*>& GetInterceptionJobMap() {
292     static base::NoDestructor<std::map<GlobalRequestId, InterceptionJob*>> inst;
293     return *inst;
294   }
295 
~InterceptionJob()296   ~InterceptionJob() override {
297     if (registered_in_global_request_map_) {
298       size_t erased = GetInterceptionJobMap().erase(global_req_id_);
299       DCHECK_EQ(1lu, erased);
300     }
301   }
302 
303   Response InnerContinueRequest(std::unique_ptr<Modifications> modifications);
304   void ProcessAuthResponse(
305       const DevToolsURLLoaderInterceptor::AuthChallengeResponse&
306           auth_challenge_response);
307   Response ProcessResponseOverride(
308       scoped_refptr<net::HttpResponseHeaders> headers,
309       scoped_refptr<base::RefCountedMemory> body,
310       size_t response_body_offset);
311   void ProcessRedirectByClient(const GURL& redirect_url);
312   void ProcessSetCookies(const net::HttpResponseHeaders& response_headers,
313                          base::OnceClosure callback);
314   void SendResponse(scoped_refptr<base::RefCountedMemory> body, size_t offset);
315   void ApplyModificationsToRequest(
316       std::unique_ptr<Modifications> modifications);
317 
318   void StartRequest();
319   void CancelRequest();
320   void CompleteRequest(const network::URLLoaderCompletionStatus& status);
321   void Shutdown();
322 
323   std::unique_ptr<InterceptedRequestInfo> BuildRequestInfo(
324       const network::mojom::URLResponseHeadPtr& head);
325   void NotifyClient(std::unique_ptr<InterceptedRequestInfo> request_info);
326   void FetchCookies(
327       network::mojom::CookieManager::GetCookieListCallback callback);
328   void NotifyClientWithCookies(
329       std::unique_ptr<InterceptedRequestInfo> request_info,
330       const net::CookieAccessResultList& cookies_with_access_result,
331       const net::CookieAccessResultList& excluded_cookies);
332 
333   void ResponseBodyComplete();
334 
ShouldBypassForResponse() const335   bool ShouldBypassForResponse() const {
336     if (state_ == State::kResponseTaken)
337       return false;
338     DCHECK_EQ(!!response_metadata_, !!body_reader_);
339     DCHECK_EQ(state_, State::kResponseReceived);
340     return !response_metadata_;
341   }
342 
343   // network::mojom::URLLoader methods
344   void FollowRedirect(
345       const std::vector<std::string>& removed_headers,
346       const net::HttpRequestHeaders& modified_headers,
347       const net::HttpRequestHeaders& modified_cors_exempt_headers,
348       const base::Optional<GURL>& new_url) override;
349   void SetPriority(net::RequestPriority priority,
350                    int32_t intra_priority_value) override;
351   void PauseReadingBodyFromNet() override;
352   void ResumeReadingBodyFromNet() override;
353 
354   // network::mojom::URLLoaderClient methods
355   void OnReceiveResponse(network::mojom::URLResponseHeadPtr head) override;
356   void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
357                          network::mojom::URLResponseHeadPtr head) override;
358   void OnUploadProgress(int64_t current_position,
359                         int64_t total_size,
360                         OnUploadProgressCallback callback) override;
361   void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override;
362   void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
363   void OnStartLoadingResponseBody(
364       mojo::ScopedDataPipeConsumerHandle body) override;
365   void OnComplete(const network::URLLoaderCompletionStatus& status) override;
366 
367   bool CanGetResponseBody(std::string* error_reason);
368   bool StartJobAndMaybeNotify();
369 
370   void UpdateCORSFlag();
371   network::mojom::FetchResponseType CalculateResponseTainting();
372   network::ResourceRequest GetResourceRequestForCookies();
373 
374   const std::string id_prefix_;
375   const GlobalRequestId global_req_id_;
376   const base::UnguessableToken frame_token_;
377   const bool report_upload_;
378 
379   DevToolsURLLoaderInterceptor* interceptor_;
380   InterceptionStage stage_;
381 
382   std::unique_ptr<CreateLoaderParameters> create_loader_params_;
383   const bool is_download_;
384 
385   mojo::Receiver<network::mojom::URLLoaderClient> client_receiver_{this};
386   mojo::Receiver<network::mojom::URLLoader> loader_receiver_{this};
387 
388   mojo::Remote<network::mojom::URLLoaderClient> client_;
389   mojo::Remote<network::mojom::URLLoader> loader_;
390   mojo::Remote<network::mojom::URLLoaderFactory> target_factory_;
391   mojo::Remote<network::mojom::CookieManager> cookie_manager_;
392 
393   enum State {
394     kNotStarted,
395     kRequestSent,
396     kRedirectReceived,
397     kFollowRedirect,
398     kAuthRequired,
399     kResponseReceived,
400     kResponseTaken,
401   };
402 
403   State state_;
404   base::TimeTicks start_ticks_;
405   base::Time start_time_;
406 
407   bool waiting_for_resolution_;
408   int redirect_count_;
409   bool tainted_origin_ = false;
410   bool fetch_cors_flag_ = false;
411   std::string current_id_;
412 
413   std::unique_ptr<BodyReader> body_reader_;
414   std::unique_ptr<ResponseMetadata> response_metadata_;
415   bool registered_in_global_request_map_;
416 
417   base::Optional<std::pair<net::RequestPriority, int32_t>> priority_;
418   DevToolsURLLoaderInterceptor::HandleAuthRequestCallback
419       pending_auth_callback_;
420   TakeResponseBodyPipeCallback pending_response_body_pipe_callback_;
421 
422   const base::Optional<std::string> renderer_request_id_;
423 
424   DISALLOW_COPY_AND_ASSIGN(InterceptionJob);
425 };
426 
CreateJob(const base::UnguessableToken & frame_token,int32_t process_id,bool is_download,const base::Optional<std::string> & renderer_request_id,std::unique_ptr<CreateLoaderParameters> create_params,mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,mojo::PendingRemote<network::mojom::URLLoaderClient> client,mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,mojo::PendingRemote<network::mojom::CookieManager> cookie_manager)427 void DevToolsURLLoaderInterceptor::CreateJob(
428     const base::UnguessableToken& frame_token,
429     int32_t process_id,
430     bool is_download,
431     const base::Optional<std::string>& renderer_request_id,
432     std::unique_ptr<CreateLoaderParameters> create_params,
433     mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
434     mojo::PendingRemote<network::mojom::URLLoaderClient> client,
435     mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,
436     mojo::PendingRemote<network::mojom::CookieManager> cookie_manager) {
437   DCHECK(!frame_token.is_empty());
438 
439   static int last_id = 0;
440 
441   std::string id = base::StringPrintf("interception-job-%d", ++last_id);
442   // This class will manage its own life time to match the loader client.
443   new InterceptionJob(
444       this, std::move(id), frame_token, process_id, renderer_request_id,
445       std::move(create_params), is_download, std::move(loader_receiver),
446       std::move(client), std::move(target_factory), std::move(cookie_manager));
447 }
448 
GetInterceptionStage(const GURL & url,blink::mojom::ResourceType resource_type) const449 InterceptionStage DevToolsURLLoaderInterceptor::GetInterceptionStage(
450     const GURL& url,
451     blink::mojom::ResourceType resource_type) const {
452   InterceptionStage stage = InterceptionStage::DONT_INTERCEPT;
453   std::string unused;
454   std::string url_str = protocol::NetworkHandler::ExtractFragment(url, &unused);
455   for (const auto& pattern : patterns_) {
456     if (pattern.Matches(url_str, resource_type))
457       stage |= pattern.interception_stage;
458   }
459   return stage;
460 }
461 
462 class DevToolsURLLoaderFactoryProxy : public network::mojom::URLLoaderFactory {
463  public:
464   DevToolsURLLoaderFactoryProxy(
465       const base::UnguessableToken& frame_token,
466       int32_t process_id,
467       bool is_download,
468       mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,
469       mojo::PendingRemote<network::mojom::URLLoaderFactory>
470           target_factory_remote,
471       mojo::PendingRemote<network::mojom::CookieManager> cookie_manager,
472       base::WeakPtr<DevToolsURLLoaderInterceptor> interceptor);
473   ~DevToolsURLLoaderFactoryProxy() override;
474 
475  private:
476   // network::mojom::URLLoaderFactory implementation
477   void CreateLoaderAndStart(
478       mojo::PendingReceiver<network::mojom::URLLoader> loader,
479       int32_t routing_id,
480       int32_t request_id,
481       uint32_t options,
482       const network::ResourceRequest& request,
483       mojo::PendingRemote<network::mojom::URLLoaderClient> client,
484       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
485       override;
486   void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
487       override;
488 
489   void OnProxyBindingError();
490   void OnTargetFactoryError();
491 
492   const base::UnguessableToken frame_token_;
493   const int32_t process_id_;
494   const bool is_download_;
495 
496   mojo::Remote<network::mojom::URLLoaderFactory> target_factory_;
497   mojo::Remote<network::mojom::CookieManager> cookie_manager_;
498   base::WeakPtr<DevToolsURLLoaderInterceptor> interceptor_;
499   mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_;
500 
501   SEQUENCE_CHECKER(sequence_checker_);
502 };
503 
504 // This class owns itself and will delete self when any mojo
505 // connection is broken.
DevToolsURLLoaderFactoryProxy(const base::UnguessableToken & frame_token,int32_t process_id,bool is_download,mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_remote,mojo::PendingRemote<network::mojom::CookieManager> cookie_manager,base::WeakPtr<DevToolsURLLoaderInterceptor> interceptor)506 DevToolsURLLoaderFactoryProxy::DevToolsURLLoaderFactoryProxy(
507     const base::UnguessableToken& frame_token,
508     int32_t process_id,
509     bool is_download,
510     mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,
511     mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_remote,
512     mojo::PendingRemote<network::mojom::CookieManager> cookie_manager,
513     base::WeakPtr<DevToolsURLLoaderInterceptor> interceptor)
514     : frame_token_(frame_token),
515       process_id_(process_id),
516       is_download_(is_download),
517       target_factory_(std::move(target_factory_remote)),
518       interceptor_(std::move(interceptor)) {
519   target_factory_.set_disconnect_handler(
520       base::BindOnce(&DevToolsURLLoaderFactoryProxy::OnTargetFactoryError,
521                      base::Unretained(this)));
522 
523   receivers_.Add(this, std::move(loader_receiver));
524   receivers_.set_disconnect_handler(
525       base::BindRepeating(&DevToolsURLLoaderFactoryProxy::OnProxyBindingError,
526                           base::Unretained(this)));
527 
528   cookie_manager_.Bind(std::move(cookie_manager));
529   cookie_manager_.set_disconnect_handler(
530       base::BindOnce(&DevToolsURLLoaderFactoryProxy::OnTargetFactoryError,
531                      base::Unretained(this)));
532 }
533 
534 DevToolsURLLoaderFactoryProxy::~DevToolsURLLoaderFactoryProxy() = default;
535 
CreateLoaderAndStart(mojo::PendingReceiver<network::mojom::URLLoader> loader,int32_t routing_id,int32_t request_id,uint32_t options,const network::ResourceRequest & request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,const net::MutableNetworkTrafficAnnotationTag & traffic_annotation)536 void DevToolsURLLoaderFactoryProxy::CreateLoaderAndStart(
537     mojo::PendingReceiver<network::mojom::URLLoader> loader,
538     int32_t routing_id,
539     int32_t request_id,
540     uint32_t options,
541     const network::ResourceRequest& request,
542     mojo::PendingRemote<network::mojom::URLLoaderClient> client,
543     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
544   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
545 
546   DevToolsURLLoaderInterceptor* interceptor = interceptor_.get();
547   if (!interceptor_ || request.url.SchemeIs(url::kDataScheme)) {
548     target_factory_->CreateLoaderAndStart(
549         std::move(loader), routing_id, request_id, options, request,
550         std::move(client), traffic_annotation);
551     return;
552   }
553   auto creation_params = std::make_unique<CreateLoaderParameters>(
554       routing_id, request_id, options, request, traffic_annotation);
555   mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_clone;
556   target_factory_->Clone(factory_clone.InitWithNewPipeAndPassReceiver());
557   mojo::PendingRemote<network::mojom::CookieManager> cookie_manager_clone;
558   cookie_manager_->CloneInterface(
559       cookie_manager_clone.InitWithNewPipeAndPassReceiver());
560   interceptor->CreateJob(
561       frame_token_, process_id_, is_download_, request.devtools_request_id,
562       std::move(creation_params), std::move(loader), std::move(client),
563       std::move(factory_clone), std::move(cookie_manager_clone));
564 }
565 
Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)566 void DevToolsURLLoaderFactoryProxy::Clone(
567     mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
568   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
569   receivers_.Add(this, std::move(receiver));
570 }
571 
OnTargetFactoryError()572 void DevToolsURLLoaderFactoryProxy::OnTargetFactoryError() {
573   delete this;
574 }
575 
OnProxyBindingError()576 void DevToolsURLLoaderFactoryProxy::OnProxyBindingError() {
577   if (receivers_.empty())
578     delete this;
579 }
580 
581 // static
HandleAuthRequest(int32_t process_id,int32_t routing_id,int32_t request_id,const net::AuthChallengeInfo & auth_info,HandleAuthRequestCallback callback)582 void DevToolsURLLoaderInterceptor::HandleAuthRequest(
583     int32_t process_id,
584     int32_t routing_id,
585     int32_t request_id,
586     const net::AuthChallengeInfo& auth_info,
587     HandleAuthRequestCallback callback) {
588   GlobalRequestId req_id = std::make_tuple(process_id, routing_id, request_id);
589   if (auto* job = InterceptionJob::FindByRequestId(req_id))
590     job->OnAuthRequest(auth_info, std::move(callback));
591   else
592     std::move(callback).Run(true, base::nullopt);
593 }
594 
DevToolsURLLoaderInterceptor(RequestInterceptedCallback callback)595 DevToolsURLLoaderInterceptor::DevToolsURLLoaderInterceptor(
596     RequestInterceptedCallback callback)
597     : request_intercepted_callback_(std::move(callback)), weak_factory_(this) {}
598 
~DevToolsURLLoaderInterceptor()599 DevToolsURLLoaderInterceptor::~DevToolsURLLoaderInterceptor() {
600   for (auto const& entry : jobs_)
601     entry.second->Detach();
602 }
603 
SetPatterns(std::vector<DevToolsURLLoaderInterceptor::Pattern> patterns,bool handle_auth)604 void DevToolsURLLoaderInterceptor::SetPatterns(
605     std::vector<DevToolsURLLoaderInterceptor::Pattern> patterns,
606     bool handle_auth) {
607   patterns_ = std::move(patterns);
608   handle_auth_ = handle_auth;
609   DCHECK(patterns_.size() || !handle_auth);
610 }
611 
GetResponseBody(const std::string & interception_id,std::unique_ptr<GetResponseBodyCallback> callback)612 void DevToolsURLLoaderInterceptor::GetResponseBody(
613     const std::string& interception_id,
614     std::unique_ptr<GetResponseBodyCallback> callback) {
615   if (InterceptionJob* job = FindJob(interception_id, &callback))
616     job->GetResponseBody(std::move(callback));
617 }
618 
TakeResponseBodyPipe(const std::string & interception_id,DevToolsURLLoaderInterceptor::TakeResponseBodyPipeCallback callback)619 void DevToolsURLLoaderInterceptor::TakeResponseBodyPipe(
620     const std::string& interception_id,
621     DevToolsURLLoaderInterceptor::TakeResponseBodyPipeCallback callback) {
622   auto it = jobs_.find(interception_id);
623   if (it == jobs_.end()) {
624     std::move(callback).Run(
625         protocol::Response::InvalidParams("Invalid InterceptionId."),
626         mojo::ScopedDataPipeConsumerHandle(), base::EmptyString());
627     return;
628   }
629   it->second->TakeResponseBodyPipe(std::move(callback));
630 }
631 
ContinueInterceptedRequest(const std::string & interception_id,std::unique_ptr<Modifications> modifications,std::unique_ptr<ContinueInterceptedRequestCallback> callback)632 void DevToolsURLLoaderInterceptor::ContinueInterceptedRequest(
633     const std::string& interception_id,
634     std::unique_ptr<Modifications> modifications,
635     std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
636   if (InterceptionJob* job = FindJob(interception_id, &callback)) {
637     job->ContinueInterceptedRequest(std::move(modifications),
638                                     std::move(callback));
639   }
640 }
641 
CreateProxyForInterception(RenderProcessHost * rph,const base::UnguessableToken & frame_token,bool is_navigation,bool is_download,network::mojom::URLLoaderFactoryOverride * intercepting_factory)642 bool DevToolsURLLoaderInterceptor::CreateProxyForInterception(
643     RenderProcessHost* rph,
644     const base::UnguessableToken& frame_token,
645     bool is_navigation,
646     bool is_download,
647     network::mojom::URLLoaderFactoryOverride* intercepting_factory) {
648   if (patterns_.empty())
649     return false;
650 
651   // If we're the first interceptor to install an override, make a
652   // remote/receiver pair, then handle this similarly to appending
653   // a proxy to existing override.
654   if (!intercepting_factory->overriding_factory) {
655     DCHECK(!intercepting_factory->overridden_factory_receiver);
656     intercepting_factory->overridden_factory_receiver =
657         intercepting_factory->overriding_factory
658             .InitWithNewPipeAndPassReceiver();
659   }
660   mojo::PendingRemote<network::mojom::URLLoaderFactory> target_remote;
661   auto overridden_factory_receiver =
662       target_remote.InitWithNewPipeAndPassReceiver();
663   mojo::PendingRemote<network::mojom::CookieManager> cookie_manager;
664   int process_id = is_navigation ? 0 : rph->GetID();
665   rph->GetStoragePartition()->GetNetworkContext()->GetCookieManager(
666       cookie_manager.InitWithNewPipeAndPassReceiver());
667   new DevToolsURLLoaderFactoryProxy(
668       frame_token, process_id, is_download,
669       std::move(intercepting_factory->overridden_factory_receiver),
670       std::move(target_remote), std::move(cookie_manager),
671       weak_factory_.GetWeakPtr());
672   intercepting_factory->overridden_factory_receiver =
673       std::move(overridden_factory_receiver);
674   return true;
675 }
676 
InterceptionJob(DevToolsURLLoaderInterceptor * interceptor,const std::string & id,const base::UnguessableToken & frame_token,int process_id,const base::Optional<std::string> & renderer_request_id,std::unique_ptr<CreateLoaderParameters> create_loader_params,bool is_download,mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,mojo::PendingRemote<network::mojom::URLLoaderClient> client,mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,mojo::PendingRemote<network::mojom::CookieManager> cookie_manager)677 InterceptionJob::InterceptionJob(
678     DevToolsURLLoaderInterceptor* interceptor,
679     const std::string& id,
680     const base::UnguessableToken& frame_token,
681     int process_id,
682     const base::Optional<std::string>& renderer_request_id,
683     std::unique_ptr<CreateLoaderParameters> create_loader_params,
684     bool is_download,
685     mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
686     mojo::PendingRemote<network::mojom::URLLoaderClient> client,
687     mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,
688     mojo::PendingRemote<network::mojom::CookieManager> cookie_manager)
689     : id_prefix_(id),
690       global_req_id_(
691           std::make_tuple(process_id,
692                           create_loader_params->request.render_frame_id,
693                           create_loader_params->request_id)),
694       frame_token_(frame_token),
695       report_upload_(!!create_loader_params->request.request_body),
696       interceptor_(interceptor),
697       create_loader_params_(std::move(create_loader_params)),
698       is_download_(is_download),
699       client_(std::move(client)),
700       target_factory_(std::move(target_factory)),
701       cookie_manager_(std::move(cookie_manager)),
702       state_(kNotStarted),
703       waiting_for_resolution_(false),
704       redirect_count_(0),
705       renderer_request_id_(renderer_request_id) {
706   loader_receiver_.Bind(std::move(loader_receiver));
707   loader_receiver_.set_disconnect_handler(
708       base::BindOnce(&InterceptionJob::Shutdown, base::Unretained(this)));
709 
710   auto& job_map = GetInterceptionJobMap();
711   // TODO(caseq): for now, all auth requests will go to the top-level job.
712   // Figure out if we need anything smarter here.
713   registered_in_global_request_map_ =
714       job_map.emplace(global_req_id_, this).second;
715 
716   if (StartJobAndMaybeNotify())
717     return;
718 
719   StartRequest();
720 }
721 
StartJobAndMaybeNotify()722 bool InterceptionJob::StartJobAndMaybeNotify() {
723   UpdateCORSFlag();
724   start_ticks_ = base::TimeTicks::Now();
725   start_time_ = base::Time::Now();
726 
727   current_id_ = id_prefix_ + base::StringPrintf(".%d", redirect_count_);
728   interceptor_->AddJob(current_id_, this);
729 
730   const network::ResourceRequest& request = create_loader_params_->request;
731   stage_ = interceptor_->GetInterceptionStage(
732       request.url,
733       static_cast<blink::mojom::ResourceType>(request.resource_type));
734 
735   if (!(stage_ & InterceptionStage::REQUEST))
736     return false;
737 
738   if (state_ == State::kRedirectReceived)
739     state_ = State::kFollowRedirect;
740   else
741     DCHECK_EQ(State::kNotStarted, state_);
742   NotifyClient(BuildRequestInfo(nullptr));
743   return true;
744 }
745 
746 // FIXME(caseq): The logic in the three methods below is borrowed from
747 // CorsURLLoader as a matter of a quick and mergeable fix for crbug.com/1022173.
748 // This logic should be unified with CorsURLLoader.
CalculateResponseTainting()749 network::mojom::FetchResponseType InterceptionJob::CalculateResponseTainting() {
750   if (fetch_cors_flag_)
751     return FetchResponseType::kCors;
752   if (create_loader_params_->request.mode ==
753           network::mojom::RequestMode::kNoCors &&
754       tainted_origin_) {
755     return FetchResponseType::kOpaque;
756   }
757   return FetchResponseType::kBasic;
758 }
759 
GetResourceRequestForCookies()760 network::ResourceRequest InterceptionJob::GetResourceRequestForCookies() {
761   FetchResponseType response_tainting =
762       fetch_cors_flag_ ? FetchResponseType::kCors : FetchResponseType::kBasic;
763 
764   network::ResourceRequest result = create_loader_params_->request;
765   result.credentials_mode =
766       network::cors::CalculateCredentialsFlag(
767           create_loader_params_->request.credentials_mode, response_tainting)
768           ? CredentialsMode::kInclude
769           : CredentialsMode::kOmit;
770   return result;
771 }
772 
UpdateCORSFlag()773 void InterceptionJob::UpdateCORSFlag() {
774   if (fetch_cors_flag_)
775     return;
776 
777   const network::ResourceRequest& request = create_loader_params_->request;
778   fetch_cors_flag_ = network::cors::ShouldCheckCors(
779       request.url, request.request_initiator, request.mode);
780 }
781 
CanGetResponseBody(std::string * error_reason)782 bool InterceptionJob::CanGetResponseBody(std::string* error_reason) {
783   if (!(stage_ & InterceptionStage::RESPONSE)) {
784     *error_reason =
785         "Can only get response body on HeadersReceived pattern matched "
786         "requests.";
787     return false;
788   }
789   if (state_ != State::kResponseReceived || !waiting_for_resolution_) {
790     *error_reason =
791         "Can only get response body on requests captured after headers "
792         "received.";
793     return false;
794   }
795   return true;
796 }
797 
GetResponseBody(std::unique_ptr<GetResponseBodyCallback> callback)798 void InterceptionJob::GetResponseBody(
799     std::unique_ptr<GetResponseBodyCallback> callback) {
800   std::string error_reason;
801   if (!CanGetResponseBody(&error_reason)) {
802     callback->sendFailure(Response::ServerError(std::move(error_reason)));
803     return;
804   }
805   if (!body_reader_) {
806     body_reader_ = std::make_unique<BodyReader>(base::BindOnce(
807         &InterceptionJob::ResponseBodyComplete, base::Unretained(this)));
808     client_receiver_.Resume();
809     loader_->ResumeReadingBodyFromNet();
810   }
811   body_reader_->AddCallback(std::move(callback));
812 }
813 
TakeResponseBodyPipe(TakeResponseBodyPipeCallback callback)814 void InterceptionJob::TakeResponseBodyPipe(
815     TakeResponseBodyPipeCallback callback) {
816   std::string error_reason;
817   if (!CanGetResponseBody(&error_reason)) {
818     std::move(callback).Run(Response::ServerError(std::move(error_reason)),
819                             mojo::ScopedDataPipeConsumerHandle(),
820                             base::EmptyString());
821     return;
822   }
823   DCHECK_EQ(state_, State::kResponseReceived);
824   DCHECK(!!response_metadata_);
825   state_ = State::kResponseTaken;
826   pending_response_body_pipe_callback_ = std::move(callback);
827   client_receiver_.Resume();
828   loader_->ResumeReadingBodyFromNet();
829 }
830 
ContinueInterceptedRequest(std::unique_ptr<Modifications> modifications,std::unique_ptr<ContinueInterceptedRequestCallback> callback)831 void InterceptionJob::ContinueInterceptedRequest(
832     std::unique_ptr<Modifications> modifications,
833     std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
834   Response response = InnerContinueRequest(std::move(modifications));
835   // |this| may be destroyed at this point.
836   if (response.IsSuccess())
837     callback->sendSuccess();
838   else
839     callback->sendFailure(std::move(response));
840 }
841 
Detach()842 void InterceptionJob::Detach() {
843   stage_ = InterceptionStage::DONT_INTERCEPT;
844   interceptor_ = nullptr;
845   if (!waiting_for_resolution_)
846     return;
847   if (state_ == State::kAuthRequired) {
848     state_ = State::kRequestSent;
849     waiting_for_resolution_ = false;
850     std::move(pending_auth_callback_).Run(true, base::nullopt);
851     return;
852   }
853   InnerContinueRequest(std::make_unique<Modifications>());
854 }
855 
InnerContinueRequest(std::unique_ptr<Modifications> modifications)856 Response InterceptionJob::InnerContinueRequest(
857     std::unique_ptr<Modifications> modifications) {
858   if (!waiting_for_resolution_) {
859     return Response::ServerError(
860         "Invalid state for continueInterceptedRequest");
861   }
862   waiting_for_resolution_ = false;
863 
864   if (state_ == State::kAuthRequired) {
865     if (!modifications->auth_challenge_response)
866       return Response::InvalidParams("authChallengeResponse required.");
867     ProcessAuthResponse(*modifications->auth_challenge_response);
868     return Response::Success();
869   }
870   if (modifications->auth_challenge_response)
871     return Response::InvalidParams("authChallengeResponse not expected.");
872 
873   if (modifications->error_reason) {
874     network::URLLoaderCompletionStatus status(
875         modifications->error_reason.value());
876     status.completion_time = base::TimeTicks::Now();
877     if (modifications->error_reason == net::ERR_BLOCKED_BY_CLIENT) {
878       // So we know that these modifications originated from devtools
879       // (also known as inspector), and can therefore annotate the
880       // request. We only do this for one specific error code thus
881       // far, to minimize risk of breaking other usages.
882       status.extended_error_code =
883           static_cast<int>(blink::ResourceRequestBlockedReason::kInspector);
884     }
885     CompleteRequest(status);
886     return Response::Success();
887   }
888 
889   if (modifications->response_headers || modifications->response_body) {
890     return ProcessResponseOverride(std::move(modifications->response_headers),
891                                    std::move(modifications->response_body),
892                                    modifications->body_offset);
893   }
894 
895   if (state_ == State::kFollowRedirect) {
896     if (modifications->modified_url.isJust()) {
897       CancelRequest();
898       // Fall through to the generic logic of re-starting the request
899       // at the bottom of the method.
900     } else {
901       // TODO(caseq): report error if other modifications are present.
902       state_ = State::kRequestSent;
903       loader_->FollowRedirect({}, {}, {}, base::nullopt);
904       return Response::Success();
905     }
906   }
907   if (state_ == State::kRedirectReceived) {
908     // TODO(caseq): report error if other modifications are present.
909     if (modifications->modified_url.isJust()) {
910       std::string location = modifications->modified_url.fromJust();
911       CancelRequest();
912       response_metadata_->head->headers->SetHeader("location", location);
913       GURL redirect_url = create_loader_params_->request.url.Resolve(location);
914       if (!redirect_url.is_valid())
915         return Response::ServerError("Invalid modified URL");
916       ProcessRedirectByClient(redirect_url);
917       return Response::Success();
918     }
919     client_->OnReceiveRedirect(*response_metadata_->redirect_info,
920                                std::move(response_metadata_->head));
921     return Response::Success();
922   }
923 
924   if (body_reader_) {
925     if (body_reader_->data_complete())
926       SendResponse(body_reader_->body(), 0);
927 
928     // There are read callbacks pending, so let the reader do its job and come
929     // back when it's done.
930     return Response::Success();
931   }
932 
933   if (response_metadata_) {
934     if (state_ == State::kResponseTaken) {
935       return Response::InvalidParams(
936           "Unable to continue request as is after body is taken");
937     }
938     // TODO(caseq): report error if other modifications are present.
939     if (response_metadata_->status.error_code) {
940       CompleteRequest(response_metadata_->status);
941       return Response::Success();
942     }
943     DCHECK_EQ(State::kResponseReceived, state_);
944     DCHECK(!body_reader_);
945     client_->OnReceiveResponse(std::move(response_metadata_->head));
946     response_metadata_.reset();
947     loader_->ResumeReadingBodyFromNet();
948     client_receiver_.Resume();
949     return Response::Success();
950   }
951 
952   DCHECK_EQ(State::kNotStarted, state_);
953   ApplyModificationsToRequest(std::move(modifications));
954   StartRequest();
955   return Response::Success();
956 }
957 
ApplyModificationsToRequest(std::unique_ptr<Modifications> modifications)958 void InterceptionJob::ApplyModificationsToRequest(
959     std::unique_ptr<Modifications> modifications) {
960   network::ResourceRequest* request = &create_loader_params_->request;
961 
962   // Note this redirect is not visible to the page by design. If they want a
963   // visible redirect they can mock a response with a 302.
964   if (modifications->modified_url.isJust())
965     request->url = GURL(modifications->modified_url.fromJust());
966 
967   if (modifications->modified_method.isJust())
968     request->method = modifications->modified_method.fromJust();
969 
970   if (modifications->modified_post_data.isJust()) {
971     const auto& post_data = modifications->modified_post_data.fromJust();
972     request->request_body = network::ResourceRequestBody::CreateFromBytes(
973         reinterpret_cast<const char*>(post_data.data()), post_data.size());
974   }
975 
976   if (modifications->modified_headers) {
977     request->headers.Clear();
978     for (const auto& entry : *modifications->modified_headers) {
979       if (base::EqualsCaseInsensitiveASCII(entry.first,
980                                            net::HttpRequestHeaders::kReferer)) {
981         request->referrer = GURL(entry.second);
982         request->referrer_policy = net::ReferrerPolicy::NEVER_CLEAR;
983       } else {
984         request->headers.SetHeader(entry.first, entry.second);
985       }
986     }
987   }
988 }
989 
ProcessAuthResponse(const DevToolsURLLoaderInterceptor::AuthChallengeResponse & response)990 void InterceptionJob::ProcessAuthResponse(
991     const DevToolsURLLoaderInterceptor::AuthChallengeResponse& response) {
992   DCHECK_EQ(kAuthRequired, state_);
993   switch (response.response_type) {
994     case DevToolsURLLoaderInterceptor::AuthChallengeResponse::kDefault:
995       std::move(pending_auth_callback_).Run(true, base::nullopt);
996       break;
997     case DevToolsURLLoaderInterceptor::AuthChallengeResponse::kCancelAuth:
998       std::move(pending_auth_callback_).Run(false, base::nullopt);
999       break;
1000     case DevToolsURLLoaderInterceptor::AuthChallengeResponse::
1001         kProvideCredentials:
1002       std::move(pending_auth_callback_).Run(false, response.credentials);
1003       break;
1004   }
1005   state_ = kRequestSent;
1006 }
1007 
ProcessResponseOverride(scoped_refptr<net::HttpResponseHeaders> headers,scoped_refptr<base::RefCountedMemory> body,size_t response_body_offset)1008 Response InterceptionJob::ProcessResponseOverride(
1009     scoped_refptr<net::HttpResponseHeaders> headers,
1010     scoped_refptr<base::RefCountedMemory> body,
1011     size_t response_body_offset) {
1012   CancelRequest();
1013 
1014   DCHECK_LE(response_body_offset, body ? body->size() : 0);
1015   size_t body_size = body ? body->size() - response_body_offset : 0;
1016   response_metadata_ = std::make_unique<ResponseMetadata>();
1017   network::mojom::URLResponseHeadPtr& head = response_metadata_->head;
1018 
1019   head->request_time = start_time_;
1020   head->response_time = base::Time::Now();
1021 
1022   // TODO(caseq): we're only doing this because some clients expect load timing
1023   // to be present with mocked responses. Consider removing this.
1024   const base::TimeTicks now_ticks = base::TimeTicks::Now();
1025   head->load_timing.request_start_time = start_time_;
1026   head->load_timing.request_start = start_ticks_;
1027   head->load_timing.receive_headers_end = now_ticks;
1028 
1029   static const char kDummyHeaders[] = "HTTP/1.1 200 OK\0\0";
1030   head->headers = std::move(headers);
1031   if (!head->headers) {
1032     head->headers =
1033         base::MakeRefCounted<net::HttpResponseHeaders>(kDummyHeaders);
1034   }
1035   head->headers->GetMimeTypeAndCharset(&head->mime_type, &head->charset);
1036   if (head->mime_type.empty() && body_size) {
1037     size_t bytes_to_sniff =
1038         std::min(body_size, static_cast<size_t>(net::kMaxBytesToSniff));
1039     net::SniffMimeType(
1040         base::StringPiece(body->front_as<const char>() + response_body_offset,
1041                           bytes_to_sniff),
1042         create_loader_params_->request.url, "",
1043         net::ForceSniffFileUrlsForHtml::kDisabled, &head->mime_type);
1044     head->did_mime_sniff = true;
1045   }
1046   // TODO(caseq): we're cheating here a bit, raw_headers() have \0's
1047   // where real headers would have \r\n, but the sizes here
1048   // probably don't have to be exact.
1049   size_t headers_size = head->headers->raw_headers().size();
1050   head->content_length = body_size;
1051   head->encoded_data_length = headers_size;
1052   head->encoded_body_length = 0;
1053   head->request_start = start_ticks_;
1054   head->response_start = now_ticks;
1055 
1056   response_metadata_->transfer_size = body_size;
1057 
1058   response_metadata_->status.completion_time = base::TimeTicks::Now();
1059   response_metadata_->status.encoded_data_length = headers_size + body_size;
1060   response_metadata_->status.encoded_body_length = body_size;
1061   response_metadata_->status.decoded_body_length = body_size;
1062 
1063   base::OnceClosure continue_after_cookies_set;
1064   std::string location;
1065   if (head->headers->IsRedirect(&location)) {
1066     GURL redirect_url = create_loader_params_->request.url.Resolve(location);
1067     if (redirect_url.is_valid()) {
1068       continue_after_cookies_set =
1069           base::BindOnce(&InterceptionJob::ProcessRedirectByClient,
1070                          base::Unretained(this), std::move(redirect_url));
1071     }
1072   }
1073   if (!continue_after_cookies_set) {
1074     continue_after_cookies_set =
1075         base::BindOnce(&InterceptionJob::SendResponse, base::Unretained(this),
1076                        std::move(body), response_body_offset);
1077   }
1078   ProcessSetCookies(*head->headers, std::move(continue_after_cookies_set));
1079 
1080   return Response::Success();
1081 }
1082 
ProcessSetCookies(const net::HttpResponseHeaders & headers,base::OnceClosure callback)1083 void InterceptionJob::ProcessSetCookies(const net::HttpResponseHeaders& headers,
1084                                         base::OnceClosure callback) {
1085   if (!GetResourceRequestForCookies().SavesCookies()) {
1086     std::move(callback).Run();
1087     return;
1088   }
1089 
1090   std::vector<std::unique_ptr<net::CanonicalCookie>> cookies;
1091   base::Time response_date;
1092   base::Optional<base::Time> server_time = base::nullopt;
1093   if (headers.GetDateValue(&response_date))
1094     server_time = base::make_optional(response_date);
1095   base::Time now = base::Time::Now();
1096 
1097   const base::StringPiece name("Set-Cookie");
1098   std::string cookie_line;
1099   size_t iter = 0;
1100   while (headers.EnumerateHeader(&iter, name, &cookie_line)) {
1101     std::unique_ptr<net::CanonicalCookie> cookie = net::CanonicalCookie::Create(
1102         create_loader_params_->request.url, cookie_line, now, server_time);
1103     if (cookie)
1104       cookies.emplace_back(std::move(cookie));
1105   }
1106 
1107   net::CookieOptions options;
1108   options.set_include_httponly();
1109   bool should_treat_as_first_party =
1110       GetContentClient()
1111           ->browser()
1112           ->ShouldIgnoreSameSiteCookieRestrictionsWhenTopLevel(
1113               create_loader_params_->request.site_for_cookies.scheme(),
1114               create_loader_params_->request.url.SchemeIsCryptographic());
1115   options.set_same_site_cookie_context(
1116       net::cookie_util::ComputeSameSiteContextForResponse(
1117           create_loader_params_->request.url,
1118           create_loader_params_->request.site_for_cookies,
1119           create_loader_params_->request.request_initiator,
1120           (create_loader_params_->request.force_ignore_site_for_cookies ||
1121            should_treat_as_first_party)));
1122 
1123   // |this| might be deleted here if |cookies| is empty!
1124   auto on_cookie_set = base::BindRepeating(
1125       [](base::RepeatingClosure closure, net::CookieAccessResult) {
1126         closure.Run();
1127       },
1128       base::BarrierClosure(cookies.size(), std::move(callback)));
1129   for (auto& cookie : cookies) {
1130     cookie_manager_->SetCanonicalCookie(
1131         *cookie, create_loader_params_->request.url, options, on_cookie_set);
1132   }
1133 }
1134 
ProcessRedirectByClient(const GURL & redirect_url)1135 void InterceptionJob::ProcessRedirectByClient(const GURL& redirect_url) {
1136   DCHECK(redirect_url.is_valid());
1137 
1138   const net::HttpResponseHeaders& headers = *response_metadata_->head->headers;
1139   const network::ResourceRequest& request = create_loader_params_->request;
1140 
1141   auto first_party_url_policy =
1142       request.update_first_party_url_on_redirect
1143           ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT
1144           : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL;
1145 
1146   response_metadata_->redirect_info = std::make_unique<net::RedirectInfo>(
1147       net::RedirectInfo::ComputeRedirectInfo(
1148           request.method, request.url, request.site_for_cookies,
1149           first_party_url_policy, request.referrer_policy,
1150           request.referrer.spec(), headers.response_code(), redirect_url,
1151           net::RedirectUtil::GetReferrerPolicyHeader(&headers),
1152           false /* insecure_scheme_was_upgraded */, true /* copy_fragment */));
1153 
1154   client_->OnReceiveRedirect(*response_metadata_->redirect_info,
1155                              std::move(response_metadata_->head));
1156 }
1157 
SendResponse(scoped_refptr<base::RefCountedMemory> body,size_t offset)1158 void InterceptionJob::SendResponse(scoped_refptr<base::RefCountedMemory> body,
1159                                    size_t offset) {
1160   client_->OnReceiveResponse(std::move(response_metadata_->head));
1161   if (response_metadata_->cached_metadata.size() != 0)
1162     client_->OnReceiveCachedMetadata(
1163         std::move(response_metadata_->cached_metadata));
1164 
1165   if (body) {
1166     DCHECK_LE(offset, body->size());
1167     size_t body_size = body->size() - offset;
1168     // We shouldn't be able to transfer a string that big over the protocol,
1169     // but just in case...
1170     DCHECK_LE(body_size, UINT32_MAX)
1171         << "Response bodies larger than " << UINT32_MAX << " are not supported";
1172     mojo::DataPipe pipe(body_size);
1173     uint32_t num_bytes = body_size;
1174     MojoResult res = pipe.producer_handle->WriteData(
1175         body->front() + offset, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
1176     DCHECK_EQ(0u, res);
1177     DCHECK_EQ(num_bytes, body_size);
1178     client_->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
1179   }
1180   if (response_metadata_->transfer_size)
1181     client_->OnTransferSizeUpdated(response_metadata_->transfer_size);
1182   CompleteRequest(response_metadata_->status);
1183 }
1184 
ResponseBodyComplete()1185 void InterceptionJob::ResponseBodyComplete() {
1186   if (waiting_for_resolution_)
1187     return;
1188   // We're here only if client has already told us to proceed with unmodified
1189   // response.
1190   SendResponse(body_reader_->body(), 0);
1191 }
1192 
StartRequest()1193 void InterceptionJob::StartRequest() {
1194   DCHECK_EQ(State::kNotStarted, state_);
1195   DCHECK(!response_metadata_);
1196 
1197   state_ = State::kRequestSent;
1198 
1199   target_factory_->CreateLoaderAndStart(
1200       loader_.BindNewPipeAndPassReceiver(), create_loader_params_->routing_id,
1201       create_loader_params_->request_id, create_loader_params_->options,
1202       create_loader_params_->request,
1203       client_receiver_.BindNewPipeAndPassRemote(),
1204       create_loader_params_->traffic_annotation);
1205   client_receiver_.set_disconnect_handler(
1206       base::BindOnce(&InterceptionJob::Shutdown, base::Unretained(this)));
1207 
1208   if (priority_)
1209     loader_->SetPriority(priority_->first, priority_->second);
1210 }
1211 
CancelRequest()1212 void InterceptionJob::CancelRequest() {
1213   if (state_ == State::kNotStarted)
1214     return;
1215   client_receiver_.reset();
1216   loader_.reset();
1217   if (body_reader_) {
1218     body_reader_->CancelWithError(
1219         "Another command has cancelled the fetch request");
1220     body_reader_.reset();
1221   }
1222   state_ = State::kNotStarted;
1223 }
1224 
BuildRequestInfo(const network::mojom::URLResponseHeadPtr & head)1225 std::unique_ptr<InterceptedRequestInfo> InterceptionJob::BuildRequestInfo(
1226     const network::mojom::URLResponseHeadPtr& head) {
1227   auto result = std::make_unique<InterceptedRequestInfo>();
1228   result->interception_id = current_id_;
1229   if (renderer_request_id_.has_value())
1230     result->renderer_request_id = renderer_request_id_.value();
1231   result->frame_id = frame_token_;
1232   blink::mojom::ResourceType resource_type =
1233       static_cast<blink::mojom::ResourceType>(
1234           create_loader_params_->request.resource_type);
1235   result->resource_type = resource_type;
1236   result->is_navigation =
1237       resource_type == blink::mojom::ResourceType::kMainFrame ||
1238       resource_type == blink::mojom::ResourceType::kSubFrame;
1239 
1240   if (head && head->headers)
1241     result->response_headers = head->headers;
1242   return result;
1243 }
1244 
FetchCookies(network::mojom::CookieManager::GetCookieListCallback callback)1245 void InterceptionJob::FetchCookies(
1246     network::mojom::CookieManager::GetCookieListCallback callback) {
1247   if (!GetResourceRequestForCookies().SendsCookies()) {
1248     std::move(callback).Run({}, {});
1249     return;
1250   }
1251   net::CookieOptions options;
1252   options.set_include_httponly();
1253   options.set_do_not_update_access_time();
1254 
1255   const network::ResourceRequest& request = create_loader_params_->request;
1256 
1257   bool should_treat_as_first_party =
1258       GetContentClient()
1259           ->browser()
1260           ->ShouldIgnoreSameSiteCookieRestrictionsWhenTopLevel(
1261               request.site_for_cookies.scheme(),
1262               request.url.SchemeIsCryptographic());
1263   options.set_same_site_cookie_context(
1264       net::cookie_util::ComputeSameSiteContextForRequest(
1265           request.method, request.url, request.site_for_cookies,
1266           request.request_initiator,
1267           (request.force_ignore_site_for_cookies ||
1268            should_treat_as_first_party)));
1269 
1270   cookie_manager_->GetCookieList(request.url, options, std::move(callback));
1271 }
1272 
NotifyClient(std::unique_ptr<InterceptedRequestInfo> request_info)1273 void InterceptionJob::NotifyClient(
1274     std::unique_ptr<InterceptedRequestInfo> request_info) {
1275   DCHECK(!waiting_for_resolution_);
1276   FetchCookies(base::BindOnce(&InterceptionJob::NotifyClientWithCookies,
1277                               base::Unretained(this), std::move(request_info)));
1278 }
1279 
NotifyClientWithCookies(std::unique_ptr<InterceptedRequestInfo> request_info,const net::CookieAccessResultList & cookies_with_access_result,const net::CookieAccessResultList & excluded_cookies)1280 void InterceptionJob::NotifyClientWithCookies(
1281     std::unique_ptr<InterceptedRequestInfo> request_info,
1282     const net::CookieAccessResultList& cookies_with_access_result,
1283     const net::CookieAccessResultList& excluded_cookies) {
1284   if (!interceptor_)
1285     return;
1286   std::string cookie_line;
1287   if (!cookies_with_access_result.empty()) {
1288     cookie_line =
1289         net::CanonicalCookie::BuildCookieLine(cookies_with_access_result);
1290   }
1291   request_info->network_request =
1292       protocol::NetworkHandler::CreateRequestFromResourceRequest(
1293           create_loader_params_->request, cookie_line);
1294 
1295   waiting_for_resolution_ = true;
1296   interceptor_->request_intercepted_callback_.Run(std::move(request_info));
1297 }
1298 
CompleteRequest(const network::URLLoaderCompletionStatus & status)1299 void InterceptionJob::CompleteRequest(
1300     const network::URLLoaderCompletionStatus& status) {
1301   client_->OnComplete(status);
1302   Shutdown();
1303 }
1304 
Shutdown()1305 void InterceptionJob::Shutdown() {
1306   if (interceptor_)
1307     interceptor_->RemoveJob(current_id_);
1308   delete this;
1309 }
1310 
1311 // URLLoader methods
FollowRedirect(const std::vector<std::string> & removed_headers,const net::HttpRequestHeaders & modified_headers,const net::HttpRequestHeaders & modified_cors_exempt_headers,const base::Optional<GURL> & new_url)1312 void InterceptionJob::FollowRedirect(
1313     const std::vector<std::string>& removed_headers,
1314     const net::HttpRequestHeaders& modified_headers,
1315     const net::HttpRequestHeaders& modified_cors_exempt_headers,
1316     const base::Optional<GURL>& new_url) {
1317   DCHECK(!new_url.has_value()) << "Redirect with modified url was not "
1318                                   "supported yet. crbug.com/845683";
1319   DCHECK(!waiting_for_resolution_);
1320 
1321   network::ResourceRequest* request = &create_loader_params_->request;
1322   const net::RedirectInfo& info = *response_metadata_->redirect_info;
1323   const auto current_origin = url::Origin::Create(request->url);
1324   if (request->request_initiator &&
1325       (!url::Origin::Create(info.new_url).IsSameOriginWith(current_origin) &&
1326        !request->request_initiator->IsSameOriginWith(current_origin))) {
1327     tainted_origin_ = true;
1328   }
1329 
1330   bool clear_body = false;
1331   net::RedirectUtil::UpdateHttpRequest(request->url, request->method, info,
1332                                        removed_headers, modified_headers,
1333                                        &request->headers, &clear_body);
1334   request->cors_exempt_headers.MergeFrom(modified_cors_exempt_headers);
1335   for (const std::string& name : removed_headers)
1336     request->cors_exempt_headers.RemoveHeader(name);
1337 
1338   if (clear_body)
1339     request->request_body = nullptr;
1340   request->method = info.new_method;
1341   request->url = info.new_url;
1342   request->site_for_cookies = info.new_site_for_cookies;
1343   request->referrer_policy = info.new_referrer_policy;
1344   request->referrer = GURL(info.new_referrer);
1345   response_metadata_.reset();
1346 
1347   UpdateCORSFlag();
1348 
1349   if (interceptor_) {
1350     // Pretend that each redirect hop is a new request -- this is for
1351     // compatibilty with URLRequestJob-based interception implementation.
1352     interceptor_->RemoveJob(current_id_);
1353     redirect_count_++;
1354     if (StartJobAndMaybeNotify())
1355       return;
1356   }
1357   if (state_ == State::kRedirectReceived) {
1358     state_ = State::kRequestSent;
1359     loader_->FollowRedirect(removed_headers, modified_headers,
1360                             modified_cors_exempt_headers,
1361                             base::nullopt /* new_url */);
1362     return;
1363   }
1364 
1365   DCHECK_EQ(State::kNotStarted, state_);
1366   StartRequest();
1367 }
1368 
SetPriority(net::RequestPriority priority,int32_t intra_priority_value)1369 void InterceptionJob::SetPriority(net::RequestPriority priority,
1370                                   int32_t intra_priority_value) {
1371   priority_ = std::make_pair(priority, intra_priority_value);
1372 
1373   if (loader_)
1374     loader_->SetPriority(priority, intra_priority_value);
1375 }
1376 
PauseReadingBodyFromNet()1377 void InterceptionJob::PauseReadingBodyFromNet() {
1378   if (!body_reader_ && loader_ && state_ != State::kResponseTaken)
1379     loader_->PauseReadingBodyFromNet();
1380 }
1381 
ResumeReadingBodyFromNet()1382 void InterceptionJob::ResumeReadingBodyFromNet() {
1383   if (!body_reader_ && loader_ && state_ != State::kResponseTaken)
1384     loader_->ResumeReadingBodyFromNet();
1385 }
1386 
1387 // URLLoaderClient methods
OnReceiveResponse(network::mojom::URLResponseHeadPtr head)1388 void InterceptionJob::OnReceiveResponse(
1389     network::mojom::URLResponseHeadPtr head) {
1390   state_ = State::kResponseReceived;
1391   DCHECK(!response_metadata_);
1392   if (!(stage_ & InterceptionStage::RESPONSE)) {
1393     client_->OnReceiveResponse(std::move(head));
1394     return;
1395   }
1396   loader_->PauseReadingBodyFromNet();
1397   client_receiver_.Pause();
1398 
1399   auto request_info = BuildRequestInfo(head);
1400   const network::ResourceRequest& request = create_loader_params_->request;
1401   request_info->is_download =
1402       request_info->is_navigation &&
1403       (is_download_ || download_utils::IsDownload(
1404                            request.url, head->headers.get(), head->mime_type));
1405 
1406   response_metadata_ = std::make_unique<ResponseMetadata>(std::move(head));
1407 
1408   NotifyClient(std::move(request_info));
1409 }
1410 
OnReceiveRedirect(const net::RedirectInfo & redirect_info,network::mojom::URLResponseHeadPtr head)1411 void InterceptionJob::OnReceiveRedirect(
1412     const net::RedirectInfo& redirect_info,
1413     network::mojom::URLResponseHeadPtr head) {
1414   DCHECK_EQ(State::kRequestSent, state_);
1415   state_ = State::kRedirectReceived;
1416   response_metadata_ = std::make_unique<ResponseMetadata>(head.Clone());
1417   response_metadata_->redirect_info =
1418       std::make_unique<net::RedirectInfo>(redirect_info);
1419 
1420   if (!(stage_ & InterceptionStage::RESPONSE)) {
1421     client_->OnReceiveRedirect(redirect_info, std::move(head));
1422     return;
1423   }
1424 
1425   std::unique_ptr<InterceptedRequestInfo> request_info = BuildRequestInfo(head);
1426   request_info->redirect_url = redirect_info.new_url.spec();
1427   NotifyClient(std::move(request_info));
1428 }
1429 
OnUploadProgress(int64_t current_position,int64_t total_size,OnUploadProgressCallback callback)1430 void InterceptionJob::OnUploadProgress(int64_t current_position,
1431                                        int64_t total_size,
1432                                        OnUploadProgressCallback callback) {
1433   if (!report_upload_)
1434     return;
1435   client_->OnUploadProgress(current_position, total_size, std::move(callback));
1436 }
1437 
OnReceiveCachedMetadata(mojo_base::BigBuffer data)1438 void InterceptionJob::OnReceiveCachedMetadata(mojo_base::BigBuffer data) {
1439   if (ShouldBypassForResponse())
1440     client_->OnReceiveCachedMetadata(std::move(data));
1441   else
1442     response_metadata_->cached_metadata = std::move(data);
1443 }
1444 
OnTransferSizeUpdated(int32_t transfer_size_diff)1445 void InterceptionJob::OnTransferSizeUpdated(int32_t transfer_size_diff) {
1446   if (ShouldBypassForResponse())
1447     client_->OnTransferSizeUpdated(transfer_size_diff);
1448   else
1449     response_metadata_->transfer_size += transfer_size_diff;
1450 }
1451 
OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body)1452 void InterceptionJob::OnStartLoadingResponseBody(
1453     mojo::ScopedDataPipeConsumerHandle body) {
1454   if (pending_response_body_pipe_callback_) {
1455     DCHECK_EQ(State::kResponseTaken, state_);
1456     DCHECK(!body_reader_);
1457     std::move(pending_response_body_pipe_callback_)
1458         .Run(Response::Success(), std::move(body),
1459              response_metadata_->head->mime_type);
1460     return;
1461   }
1462   DCHECK_EQ(State::kResponseReceived, state_);
1463   if (ShouldBypassForResponse())
1464     client_->OnStartLoadingResponseBody(std::move(body));
1465   else
1466     body_reader_->StartReading(std::move(body));
1467 }
1468 
OnComplete(const network::URLLoaderCompletionStatus & status)1469 void InterceptionJob::OnComplete(
1470     const network::URLLoaderCompletionStatus& status) {
1471   // No need to listen to the channel any more, so just reset it, so if the pipe
1472   // is closed by the other end, |shutdown| isn't run.
1473   client_receiver_.reset();
1474   loader_.reset();
1475 
1476   if (!response_metadata_) {
1477     // If we haven't seen response and get an error completion,
1478     // treat it as a response and intercept (provided response are
1479     // being intercepted).
1480     if (!(stage_ & InterceptionStage::RESPONSE) || !status.error_code) {
1481       CompleteRequest(status);
1482       return;
1483     }
1484     response_metadata_ = std::make_unique<ResponseMetadata>();
1485     response_metadata_->status = status;
1486     auto request_info = BuildRequestInfo(nullptr);
1487     request_info->response_error_code = status.error_code;
1488     NotifyClient(std::move(request_info));
1489     return;
1490   }
1491   // Since we're not forwarding OnComplete right now, make sure
1492   // we're in the proper state. The completion is due upon client response.
1493   DCHECK(state_ == State::kResponseReceived || state_ == State::kResponseTaken);
1494   DCHECK(waiting_for_resolution_);
1495 
1496   response_metadata_->status = status;
1497 }
1498 
OnAuthRequest(const net::AuthChallengeInfo & auth_info,DevToolsURLLoaderInterceptor::HandleAuthRequestCallback callback)1499 void InterceptionJob::OnAuthRequest(
1500     const net::AuthChallengeInfo& auth_info,
1501     DevToolsURLLoaderInterceptor::HandleAuthRequestCallback callback) {
1502   DCHECK_EQ(kRequestSent, state_);
1503   DCHECK(pending_auth_callback_.is_null());
1504   DCHECK(!waiting_for_resolution_);
1505 
1506   if (!(stage_ & InterceptionStage::REQUEST) || !interceptor_ ||
1507       !interceptor_->handle_auth_) {
1508     std::move(callback).Run(true, base::nullopt);
1509     return;
1510   }
1511   state_ = State::kAuthRequired;
1512   auto request_info = BuildRequestInfo(nullptr);
1513   request_info->auth_challenge =
1514       std::make_unique<net::AuthChallengeInfo>(auth_info);
1515   pending_auth_callback_ = std::move(callback);
1516   NotifyClient(std::move(request_info));
1517 }
1518 
DevToolsURLLoaderFactoryAdapter(mojo::PendingRemote<network::mojom::URLLoaderFactory> factory)1519 DevToolsURLLoaderFactoryAdapter::DevToolsURLLoaderFactoryAdapter(
1520     mojo::PendingRemote<network::mojom::URLLoaderFactory> factory)
1521     : factory_(std::move(factory)) {}
1522 
1523 DevToolsURLLoaderFactoryAdapter::~DevToolsURLLoaderFactoryAdapter() = default;
1524 
CreateLoaderAndStart(mojo::PendingReceiver<network::mojom::URLLoader> loader,int32_t routing_id,int32_t request_id,uint32_t options,const network::ResourceRequest & request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,const net::MutableNetworkTrafficAnnotationTag & traffic_annotation)1525 void DevToolsURLLoaderFactoryAdapter::CreateLoaderAndStart(
1526     mojo::PendingReceiver<network::mojom::URLLoader> loader,
1527     int32_t routing_id,
1528     int32_t request_id,
1529     uint32_t options,
1530     const network::ResourceRequest& request,
1531     mojo::PendingRemote<network::mojom::URLLoaderClient> client,
1532     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
1533   factory_->CreateLoaderAndStart(std::move(loader), routing_id, request_id,
1534                                  options, request, std::move(client),
1535                                  traffic_annotation);
1536 }
1537 
Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)1538 void DevToolsURLLoaderFactoryAdapter::Clone(
1539     mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
1540   factory_->Clone(std::move(receiver));
1541 }
1542 
1543 }  // namespace content
1544