• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that can
3 // be found in the LICENSE file.
4 
5 #include "libcef/renderer/render_urlrequest_impl.h"
6 
7 #include <stdint.h>
8 
9 #include "libcef/common/request_impl.h"
10 #include "libcef/common/response_impl.h"
11 #include "libcef/renderer/blink_glue.h"
12 #include "libcef/renderer/frame_impl.h"
13 #include "libcef/renderer/thread_util.h"
14 
15 #include "base/logging.h"
16 #include "net/base/request_priority.h"
17 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
18 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
19 #include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h"
20 #include "third_party/blink/public/platform/web_security_origin.h"
21 #include "third_party/blink/public/platform/web_string.h"
22 #include "third_party/blink/public/platform/web_url.h"
23 #include "third_party/blink/public/platform/web_url_error.h"
24 #include "third_party/blink/public/platform/web_url_loader.h"
25 #include "third_party/blink/public/platform/web_url_loader_client.h"
26 #include "third_party/blink/public/platform/web_url_request.h"
27 #include "third_party/blink/public/platform/web_url_request_extra_data.h"
28 #include "third_party/blink/public/platform/web_url_response.h"
29 
30 using blink::WebString;
31 using blink::WebURL;
32 using blink::WebURLError;
33 using blink::WebURLLoader;
34 using blink::WebURLRequest;
35 using blink::WebURLResponse;
36 
37 namespace {
38 
39 class CefWebURLLoaderClient : public blink::WebURLLoaderClient {
40  public:
41   CefWebURLLoaderClient(CefRenderURLRequest::Context* context,
42                         int request_flags);
43   ~CefWebURLLoaderClient() override;
44 
45   // blink::WebURLLoaderClient methods.
46   void DidSendData(uint64_t bytes_sent,
47                    uint64_t total_bytes_to_be_sent) override;
48   void DidReceiveResponse(const WebURLResponse& response) override;
49   void DidReceiveData(const char* data, int dataLength) override;
50   void DidFinishLoading(base::TimeTicks finish_time,
51                         int64_t total_encoded_data_length,
52                         int64_t total_encoded_body_length,
53                         int64_t total_decoded_body_length,
54                         bool should_report_corb_blocking) override;
55   void DidFail(const WebURLError&,
56                base::TimeTicks finish_time,
57                int64_t total_encoded_data_length,
58                int64_t total_encoded_body_length,
59                int64_t total_decoded_body_length) override;
60   void DidStartLoadingResponseBody(
61       mojo::ScopedDataPipeConsumerHandle response_body) override;
62   bool WillFollowRedirect(const WebURL& new_url,
63                           const net::SiteForCookies& new_site_for_cookies,
64                           const WebString& new_referrer,
65                           network::mojom::ReferrerPolicy new_referrer_policy,
66                           const WebString& new_method,
67                           const WebURLResponse& passed_redirect_response,
68                           bool& report_raw_headers,
69                           std::vector<std::string>* removed_headers,
70                           bool insecure_scheme_was_upgraded) override;
71 
72  protected:
73   // The context_ pointer will outlive this object.
74   CefRenderURLRequest::Context* context_;
75   int request_flags_;
76 };
77 
78 }  // namespace
79 
80 // CefRenderURLRequest::Context -----------------------------------------------
81 
82 class CefRenderURLRequest::Context
83     : public base::RefCountedThreadSafe<CefRenderURLRequest::Context> {
84  public:
Context(CefRefPtr<CefRenderURLRequest> url_request,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request,CefRefPtr<CefURLRequestClient> client)85   Context(CefRefPtr<CefRenderURLRequest> url_request,
86           CefRefPtr<CefFrame> frame,
87           CefRefPtr<CefRequest> request,
88           CefRefPtr<CefURLRequestClient> client)
89       : url_request_(url_request),
90         frame_(frame),
91         request_(request),
92         client_(client),
93         status_(UR_IO_PENDING),
94         error_code_(ERR_NONE),
95         body_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
96         response_was_cached_(false),
97         upload_data_size_(0),
98         got_upload_progress_complete_(false),
99         download_data_received_(0),
100         download_data_total_(-1) {
101     // Mark the request as read-only.
102     static_cast<CefRequestImpl*>(request_.get())->SetReadOnly(true);
103   }
104 
Start()105   bool Start() {
106     GURL url = GURL(request_->GetURL().ToString());
107     if (!url.is_valid())
108       return false;
109 
110     url_client_.reset(new CefWebURLLoaderClient(this, request_->GetFlags()));
111 
112     std::unique_ptr<network::ResourceRequest> resource_request =
113         std::make_unique<network::ResourceRequest>();
114     static_cast<CefRequestImpl*>(request_.get())
115         ->Get(resource_request.get(), false);
116     resource_request->priority = net::MEDIUM;
117 
118     // Behave the same as a subresource load.
119     resource_request->resource_type =
120         static_cast<int>(blink::mojom::ResourceType::kSubResource);
121 
122     // Need load timing info for WebURLLoaderImpl::PopulateURLResponse to
123     // properly set cached status.
124     resource_request->enable_load_timing = true;
125 
126     // Set the origin to match the request. The requirement for an origin is
127     // DCHECK'd in ResourceDispatcherHostImpl::ContinuePendingBeginRequest.
128     resource_request->request_initiator = url::Origin::Create(url);
129 
130     if (request_->GetFlags() & UR_FLAG_ALLOW_STORED_CREDENTIALS) {
131       // Include SameSite cookies.
132       resource_request->site_for_cookies =
133           net::SiteForCookies::FromOrigin(*resource_request->request_initiator);
134     }
135 
136     if (resource_request->request_body) {
137       const auto& elements = *resource_request->request_body->elements();
138       if (elements.size() > 0) {
139         const auto& element = elements[0];
140         if (element.type() == network::DataElement::Tag::kBytes) {
141           const auto& bytes_element = element.As<network::DataElementBytes>();
142           upload_data_size_ = bytes_element.bytes().size();
143         }
144       }
145     }
146 
147     auto frame_impl = static_cast<CefFrameImpl*>(frame_.get());
148     loader_ = frame_impl->CreateURLLoader();
149     loader_->LoadAsynchronously(
150         std::move(resource_request), /*extra_data=*/nullptr,
151         /*no_mime_sniffing=*/false,
152         frame_impl->CreateResourceLoadInfoNotifierWrapper(), url_client_.get());
153     return true;
154   }
155 
Cancel()156   void Cancel() {
157     // The request may already be complete.
158     if (!loader_.get() || status_ != UR_IO_PENDING)
159       return;
160 
161     status_ = UR_CANCELED;
162     error_code_ = ERR_ABORTED;
163 
164     // Will result in a call to OnError().
165     loader_->Cancel();
166   }
167 
OnStopRedirect(const WebURL & redirect_url,const WebURLResponse & response)168   void OnStopRedirect(const WebURL& redirect_url,
169                       const WebURLResponse& response) {
170     response_was_cached_ = blink_glue::ResponseWasCached(response);
171     response_ = CefResponse::Create();
172     CefResponseImpl* responseImpl =
173         static_cast<CefResponseImpl*>(response_.get());
174 
175     // In case of StopOnRedirect we only set these fields. Everything else is
176     // left blank. This also replicates the behaviour of the browser urlrequest
177     // fetcher.
178     responseImpl->SetStatus(response.HttpStatusCode());
179     responseImpl->SetURL(redirect_url.GetString().Utf16());
180     responseImpl->SetReadOnly(true);
181 
182     status_ = UR_CANCELED;
183     error_code_ = ERR_ABORTED;
184 
185     OnComplete();
186   }
187 
OnResponse(const WebURLResponse & response)188   void OnResponse(const WebURLResponse& response) {
189     response_was_cached_ = blink_glue::ResponseWasCached(response);
190     response_ = CefResponse::Create();
191     CefResponseImpl* responseImpl =
192         static_cast<CefResponseImpl*>(response_.get());
193     responseImpl->Set(response);
194     responseImpl->SetReadOnly(true);
195 
196     download_data_total_ = response.ExpectedContentLength();
197   }
198 
OnError(const WebURLError & error)199   void OnError(const WebURLError& error) {
200     if (status_ == UR_IO_PENDING) {
201       status_ = UR_FAILED;
202       error_code_ = static_cast<cef_errorcode_t>(error.reason());
203     }
204 
205     OnComplete();
206   }
207 
OnComplete()208   void OnComplete() {
209     if (body_handle_.is_valid()) {
210       return;
211     }
212 
213     if (status_ == UR_IO_PENDING) {
214       status_ = UR_SUCCESS;
215       NotifyUploadProgressIfNecessary();
216     }
217 
218     if (loader_.get())
219       loader_.reset(nullptr);
220 
221     DCHECK(url_request_.get());
222     client_->OnRequestComplete(url_request_.get());
223 
224     // This may result in the Context object being deleted.
225     url_request_ = nullptr;
226   }
227 
OnBodyReadable(MojoResult,const mojo::HandleSignalsState &)228   void OnBodyReadable(MojoResult, const mojo::HandleSignalsState&) {
229     const void* buffer = nullptr;
230     uint32_t read_bytes = 0;
231     MojoResult result = body_handle_->BeginReadData(&buffer, &read_bytes,
232                                                     MOJO_READ_DATA_FLAG_NONE);
233     if (result == MOJO_RESULT_SHOULD_WAIT) {
234       body_watcher_.ArmOrNotify();
235       return;
236     }
237 
238     if (result == MOJO_RESULT_FAILED_PRECONDITION) {
239       // Whole body has been read.
240       body_handle_.reset();
241       body_watcher_.Cancel();
242       OnComplete();
243       return;
244     }
245 
246     if (result != MOJO_RESULT_OK) {
247       // Something went wrong.
248       body_handle_.reset();
249       body_watcher_.Cancel();
250       OnComplete();
251       return;
252     }
253 
254     download_data_received_ += read_bytes;
255 
256     client_->OnDownloadProgress(url_request_.get(), download_data_received_,
257                                 download_data_total_);
258 
259     if (!(request_->GetFlags() & UR_FLAG_NO_DOWNLOAD_DATA)) {
260       client_->OnDownloadData(url_request_.get(), buffer, read_bytes);
261     }
262 
263     body_handle_->EndReadData(read_bytes);
264     body_watcher_.ArmOrNotify();
265   }
266 
OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle response_body)267   void OnStartLoadingResponseBody(
268       mojo::ScopedDataPipeConsumerHandle response_body) {
269     DCHECK(response_body);
270     DCHECK(!body_handle_);
271     body_handle_ = std::move(response_body);
272 
273     body_watcher_.Watch(
274         body_handle_.get(),
275         MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
276         MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
277         base::BindRepeating(&CefRenderURLRequest::Context::OnBodyReadable,
278                             base::Unretained(this)));
279     body_watcher_.ArmOrNotify();
280   }
281 
OnDownloadProgress(int64_t current)282   void OnDownloadProgress(int64_t current) {
283     DCHECK(url_request_.get());
284 
285     NotifyUploadProgressIfNecessary();
286 
287     download_data_received_ += current;
288     client_->OnDownloadProgress(url_request_.get(), download_data_received_,
289                                 download_data_total_);
290   }
291 
OnDownloadData(const char * data,int dataLength)292   void OnDownloadData(const char* data, int dataLength) {
293     DCHECK(url_request_.get());
294     client_->OnDownloadData(url_request_.get(), data, dataLength);
295   }
296 
OnUploadProgress(int64_t current,int64_t total)297   void OnUploadProgress(int64_t current, int64_t total) {
298     DCHECK(url_request_.get());
299     if (current == total)
300       got_upload_progress_complete_ = true;
301     client_->OnUploadProgress(url_request_.get(), current, total);
302   }
303 
request() const304   CefRefPtr<CefRequest> request() const { return request_; }
client() const305   CefRefPtr<CefURLRequestClient> client() const { return client_; }
status() const306   CefURLRequest::Status status() const { return status_; }
error_code() const307   CefURLRequest::ErrorCode error_code() const { return error_code_; }
response() const308   CefRefPtr<CefResponse> response() const { return response_; }
response_was_cached() const309   bool response_was_cached() const { return response_was_cached_; }
310 
311  private:
312   friend class base::RefCountedThreadSafe<CefRenderURLRequest::Context>;
313 
~Context()314   virtual ~Context() {}
315 
NotifyUploadProgressIfNecessary()316   void NotifyUploadProgressIfNecessary() {
317     if (!got_upload_progress_complete_ && upload_data_size_ > 0) {
318       // Upload notifications are sent using a timer and may not occur if the
319       // request completes too quickly. We therefore send the notification here
320       // if necessary.
321       url_client_->DidSendData(upload_data_size_, upload_data_size_);
322       got_upload_progress_complete_ = true;
323     }
324   }
325 
326   // Members only accessed on the initialization thread.
327   CefRefPtr<CefRenderURLRequest> url_request_;
328   CefRefPtr<CefFrame> frame_;
329   CefRefPtr<CefRequest> request_;
330   CefRefPtr<CefURLRequestClient> client_;
331   CefURLRequest::Status status_;
332   CefURLRequest::ErrorCode error_code_;
333   CefRefPtr<CefResponse> response_;
334   mojo::ScopedDataPipeConsumerHandle body_handle_;
335   mojo::SimpleWatcher body_watcher_;
336   bool response_was_cached_;
337   std::unique_ptr<blink::WebURLLoader> loader_;
338   std::unique_ptr<CefWebURLLoaderClient> url_client_;
339   int64_t upload_data_size_;
340   bool got_upload_progress_complete_;
341   int64_t download_data_received_;
342   int64_t download_data_total_;
343 };
344 
345 // CefWebURLLoaderClient --------------------------------------------------
346 
347 namespace {
348 
CefWebURLLoaderClient(CefRenderURLRequest::Context * context,int request_flags)349 CefWebURLLoaderClient::CefWebURLLoaderClient(
350     CefRenderURLRequest::Context* context,
351     int request_flags)
352     : context_(context), request_flags_(request_flags) {}
353 
~CefWebURLLoaderClient()354 CefWebURLLoaderClient::~CefWebURLLoaderClient() {}
355 
DidSendData(uint64_t bytes_sent,uint64_t total_bytes_to_be_sent)356 void CefWebURLLoaderClient::DidSendData(uint64_t bytes_sent,
357                                         uint64_t total_bytes_to_be_sent) {
358   if (request_flags_ & UR_FLAG_REPORT_UPLOAD_PROGRESS)
359     context_->OnUploadProgress(bytes_sent, total_bytes_to_be_sent);
360 }
361 
DidReceiveResponse(const WebURLResponse & response)362 void CefWebURLLoaderClient::DidReceiveResponse(const WebURLResponse& response) {
363   context_->OnResponse(response);
364 }
365 
DidReceiveData(const char * data,int dataLength)366 void CefWebURLLoaderClient::DidReceiveData(const char* data, int dataLength) {
367   context_->OnDownloadProgress(dataLength);
368 
369   if (!(request_flags_ & UR_FLAG_NO_DOWNLOAD_DATA))
370     context_->OnDownloadData(data, dataLength);
371 }
372 
DidFinishLoading(base::TimeTicks finish_time,int64_t total_encoded_data_length,int64_t total_encoded_body_length,int64_t total_decoded_body_length,bool should_report_corb_blocking)373 void CefWebURLLoaderClient::DidFinishLoading(base::TimeTicks finish_time,
374                                              int64_t total_encoded_data_length,
375                                              int64_t total_encoded_body_length,
376                                              int64_t total_decoded_body_length,
377                                              bool should_report_corb_blocking) {
378   context_->OnComplete();
379 }
380 
DidFail(const WebURLError & error,base::TimeTicks finish_time,int64_t total_encoded_data_length,int64_t total_encoded_body_length,int64_t total_decoded_body_length)381 void CefWebURLLoaderClient::DidFail(const WebURLError& error,
382                                     base::TimeTicks finish_time,
383                                     int64_t total_encoded_data_length,
384                                     int64_t total_encoded_body_length,
385                                     int64_t total_decoded_body_length) {
386   context_->OnError(error);
387 }
388 
DidStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle response_body)389 void CefWebURLLoaderClient::DidStartLoadingResponseBody(
390     mojo::ScopedDataPipeConsumerHandle response_body) {
391   context_->OnStartLoadingResponseBody(std::move(response_body));
392 }
393 
WillFollowRedirect(const WebURL & new_url,const net::SiteForCookies & new_site_for_cookies,const WebString & new_referrer,network::mojom::ReferrerPolicy new_referrer_policy,const WebString & new_method,const WebURLResponse & passed_redirect_response,bool & report_raw_headers,std::vector<std::string> * removed_headers,bool insecure_scheme_was_upgraded)394 bool CefWebURLLoaderClient::WillFollowRedirect(
395     const WebURL& new_url,
396     const net::SiteForCookies& new_site_for_cookies,
397     const WebString& new_referrer,
398     network::mojom::ReferrerPolicy new_referrer_policy,
399     const WebString& new_method,
400     const WebURLResponse& passed_redirect_response,
401     bool& report_raw_headers,
402     std::vector<std::string>* removed_headers,
403     bool insecure_scheme_was_upgraded) {
404   if (request_flags_ & UR_FLAG_STOP_ON_REDIRECT) {
405     context_->OnStopRedirect(new_url, passed_redirect_response);
406     return false;
407   }
408   return true;
409 }
410 
411 }  // namespace
412 
413 // CefRenderURLRequest --------------------------------------------------------
414 
CefRenderURLRequest(CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request,CefRefPtr<CefURLRequestClient> client)415 CefRenderURLRequest::CefRenderURLRequest(
416     CefRefPtr<CefFrame> frame,
417     CefRefPtr<CefRequest> request,
418     CefRefPtr<CefURLRequestClient> client) {
419   DCHECK(frame);
420   DCHECK(request);
421   DCHECK(client);
422   context_ = new Context(this, frame, request, client);
423 }
424 
~CefRenderURLRequest()425 CefRenderURLRequest::~CefRenderURLRequest() {}
426 
Start()427 bool CefRenderURLRequest::Start() {
428   if (!VerifyContext())
429     return false;
430   return context_->Start();
431 }
432 
GetRequest()433 CefRefPtr<CefRequest> CefRenderURLRequest::GetRequest() {
434   if (!VerifyContext())
435     return nullptr;
436   return context_->request();
437 }
438 
GetClient()439 CefRefPtr<CefURLRequestClient> CefRenderURLRequest::GetClient() {
440   if (!VerifyContext())
441     return nullptr;
442   return context_->client();
443 }
444 
GetRequestStatus()445 CefURLRequest::Status CefRenderURLRequest::GetRequestStatus() {
446   if (!VerifyContext())
447     return UR_UNKNOWN;
448   return context_->status();
449 }
450 
GetRequestError()451 CefURLRequest::ErrorCode CefRenderURLRequest::GetRequestError() {
452   if (!VerifyContext())
453     return ERR_NONE;
454   return context_->error_code();
455 }
456 
GetResponse()457 CefRefPtr<CefResponse> CefRenderURLRequest::GetResponse() {
458   if (!VerifyContext())
459     return nullptr;
460   return context_->response();
461 }
462 
ResponseWasCached()463 bool CefRenderURLRequest::ResponseWasCached() {
464   if (!VerifyContext())
465     return false;
466   return context_->response_was_cached();
467 }
468 
Cancel()469 void CefRenderURLRequest::Cancel() {
470   if (!VerifyContext())
471     return;
472   return context_->Cancel();
473 }
474 
VerifyContext()475 bool CefRenderURLRequest::VerifyContext() {
476   DCHECK(context_.get());
477   if (!CEF_CURRENTLY_ON_RT()) {
478     NOTREACHED() << "called on invalid thread";
479     return false;
480   }
481 
482   return true;
483 }
484