mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-01-30 19:14:56 +01:00
b3a8da9b25
This is the first pass in removing direct dependencies on the Alloy runtime from code that can potentially be shared between runtimes. CefBrowserHost and CefRequestContext APIs (including CefCookieManager, CefURLRequest, etc.) are not yet implemented for the Chrome runtime. Assert early if these API methods are called while the Chrome runtime is enabled.
512 lines
17 KiB
C++
512 lines
17 KiB
C++
// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
|
|
// reserved. Use of this source code is governed by a BSD-style license that can
|
|
// be found in the LICENSE file.
|
|
|
|
#include "libcef/renderer/render_urlrequest_impl.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "libcef/common/app_manager.h"
|
|
#include "libcef/common/request_impl.h"
|
|
#include "libcef/common/response_impl.h"
|
|
#include "libcef/common/task_runner_impl.h"
|
|
#include "libcef/renderer/blink_glue.h"
|
|
#include "libcef/renderer/frame_impl.h"
|
|
|
|
#include "base/logging.h"
|
|
#include "base/message_loop/message_loop.h"
|
|
#include "net/base/request_priority.h"
|
|
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
|
|
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
|
|
#include "third_party/blink/public/platform/web_security_origin.h"
|
|
#include "third_party/blink/public/platform/web_string.h"
|
|
#include "third_party/blink/public/platform/web_url.h"
|
|
#include "third_party/blink/public/platform/web_url_error.h"
|
|
#include "third_party/blink/public/platform/web_url_loader.h"
|
|
#include "third_party/blink/public/platform/web_url_loader_client.h"
|
|
#include "third_party/blink/public/platform/web_url_loader_factory.h"
|
|
#include "third_party/blink/public/platform/web_url_request.h"
|
|
#include "third_party/blink/public/platform/web_url_response.h"
|
|
|
|
using blink::WebString;
|
|
using blink::WebURL;
|
|
using blink::WebURLError;
|
|
using blink::WebURLLoader;
|
|
using blink::WebURLRequest;
|
|
using blink::WebURLResponse;
|
|
|
|
namespace {
|
|
|
|
class CefWebURLLoaderClient : public blink::WebURLLoaderClient {
|
|
public:
|
|
CefWebURLLoaderClient(CefRenderURLRequest::Context* context,
|
|
int request_flags);
|
|
~CefWebURLLoaderClient() override;
|
|
|
|
// blink::WebURLLoaderClient methods.
|
|
void DidSendData(uint64_t bytes_sent,
|
|
uint64_t total_bytes_to_be_sent) override;
|
|
void DidReceiveResponse(const WebURLResponse& response) override;
|
|
void DidReceiveData(const char* data, int dataLength) override;
|
|
void 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) override;
|
|
void DidFail(const WebURLError&,
|
|
int64_t total_encoded_data_length,
|
|
int64_t total_encoded_body_length,
|
|
int64_t total_decoded_body_length) override;
|
|
void DidStartLoadingResponseBody(
|
|
mojo::ScopedDataPipeConsumerHandle response_body) override;
|
|
bool 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) override;
|
|
|
|
protected:
|
|
// The context_ pointer will outlive this object.
|
|
CefRenderURLRequest::Context* context_;
|
|
int request_flags_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// CefRenderURLRequest::Context -----------------------------------------------
|
|
|
|
class CefRenderURLRequest::Context
|
|
: public base::RefCountedThreadSafe<CefRenderURLRequest::Context> {
|
|
public:
|
|
Context(CefRefPtr<CefRenderURLRequest> url_request,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request,
|
|
CefRefPtr<CefURLRequestClient> client)
|
|
: url_request_(url_request),
|
|
frame_(frame),
|
|
request_(request),
|
|
client_(client),
|
|
task_runner_(CefTaskRunnerImpl::GetCurrentTaskRunner()),
|
|
status_(UR_IO_PENDING),
|
|
error_code_(ERR_NONE),
|
|
body_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
|
|
response_was_cached_(false),
|
|
upload_data_size_(0),
|
|
got_upload_progress_complete_(false),
|
|
download_data_received_(0),
|
|
download_data_total_(-1) {
|
|
// Mark the request as read-only.
|
|
static_cast<CefRequestImpl*>(request_.get())->SetReadOnly(true);
|
|
}
|
|
|
|
inline bool CalledOnValidThread() {
|
|
return task_runner_->RunsTasksInCurrentSequence();
|
|
}
|
|
|
|
bool Start() {
|
|
DCHECK(CalledOnValidThread());
|
|
|
|
GURL url = GURL(request_->GetURL().ToString());
|
|
if (!url.is_valid())
|
|
return false;
|
|
|
|
url_client_.reset(new CefWebURLLoaderClient(this, request_->GetFlags()));
|
|
|
|
std::unique_ptr<network::ResourceRequest> resource_request =
|
|
std::make_unique<network::ResourceRequest>();
|
|
static_cast<CefRequestImpl*>(request_.get())
|
|
->Get(resource_request.get(), false);
|
|
resource_request->priority = net::MEDIUM;
|
|
|
|
// Behave the same as a subresource load.
|
|
resource_request->resource_type =
|
|
static_cast<int>(blink::mojom::ResourceType::kSubResource);
|
|
|
|
// Need load timing info for WebURLLoaderImpl::PopulateURLResponse to
|
|
// properly set cached status.
|
|
resource_request->enable_load_timing = true;
|
|
|
|
// Set the origin to match the request. The requirement for an origin is
|
|
// DCHECK'd in ResourceDispatcherHostImpl::ContinuePendingBeginRequest.
|
|
resource_request->request_initiator = url::Origin::Create(url);
|
|
|
|
if (request_->GetFlags() & UR_FLAG_ALLOW_STORED_CREDENTIALS) {
|
|
// Include SameSite cookies.
|
|
resource_request->force_ignore_site_for_cookies = true;
|
|
resource_request->site_for_cookies =
|
|
net::SiteForCookies::FromOrigin(*resource_request->request_initiator);
|
|
}
|
|
|
|
if (resource_request->request_body) {
|
|
const auto& elements = *resource_request->request_body->elements();
|
|
if (elements.size() > 0) {
|
|
const auto& element = elements[0];
|
|
if (element.type() == network::mojom::DataElementType::kBytes) {
|
|
upload_data_size_ = element.length() - element.offset();
|
|
}
|
|
}
|
|
}
|
|
|
|
blink::WebURLLoaderFactory* factory = nullptr;
|
|
if (frame_) {
|
|
// This factory supports all requests.
|
|
factory = static_cast<CefFrameImpl*>(frame_.get())->GetURLLoaderFactory();
|
|
}
|
|
if (!factory) {
|
|
// This factory only supports unintercepted http(s) and blob requests.
|
|
factory = CefAppManager::Get()->GetDefaultURLLoaderFactory();
|
|
}
|
|
|
|
loader_ = factory->CreateURLLoader(
|
|
blink::WebURLRequest(),
|
|
blink::scheduler::WebResourceLoadingTaskRunnerHandle::
|
|
CreateUnprioritized(task_runner_.get()));
|
|
loader_->LoadAsynchronously(
|
|
std::move(resource_request), nullptr /* extra_data */,
|
|
0 /* requestor_id */, false /* download_to_network_cache_only */,
|
|
false /* no_mime_sniffing */, url_client_.get());
|
|
return true;
|
|
}
|
|
|
|
void Cancel() {
|
|
DCHECK(CalledOnValidThread());
|
|
|
|
// The request may already be complete.
|
|
if (!loader_.get() || status_ != UR_IO_PENDING)
|
|
return;
|
|
|
|
status_ = UR_CANCELED;
|
|
error_code_ = ERR_ABORTED;
|
|
|
|
// Will result in a call to OnError().
|
|
loader_->Cancel();
|
|
}
|
|
|
|
void OnStopRedirect(const WebURL& redirect_url,
|
|
const WebURLResponse& response) {
|
|
DCHECK(CalledOnValidThread());
|
|
|
|
response_was_cached_ = blink_glue::ResponseWasCached(response);
|
|
response_ = CefResponse::Create();
|
|
CefResponseImpl* responseImpl =
|
|
static_cast<CefResponseImpl*>(response_.get());
|
|
|
|
// In case of StopOnRedirect we only set these fields. Everything else is
|
|
// left blank. This also replicates the behaviour of the browser urlrequest
|
|
// fetcher.
|
|
responseImpl->SetStatus(response.HttpStatusCode());
|
|
responseImpl->SetURL(redirect_url.GetString().Utf16());
|
|
responseImpl->SetReadOnly(true);
|
|
|
|
status_ = UR_CANCELED;
|
|
error_code_ = ERR_ABORTED;
|
|
|
|
OnComplete();
|
|
}
|
|
|
|
void OnResponse(const WebURLResponse& response) {
|
|
DCHECK(CalledOnValidThread());
|
|
|
|
response_was_cached_ = blink_glue::ResponseWasCached(response);
|
|
response_ = CefResponse::Create();
|
|
CefResponseImpl* responseImpl =
|
|
static_cast<CefResponseImpl*>(response_.get());
|
|
responseImpl->Set(response);
|
|
responseImpl->SetReadOnly(true);
|
|
|
|
download_data_total_ = response.ExpectedContentLength();
|
|
}
|
|
|
|
void OnError(const WebURLError& error) {
|
|
DCHECK(CalledOnValidThread());
|
|
|
|
if (status_ == UR_IO_PENDING) {
|
|
status_ = UR_FAILED;
|
|
error_code_ = static_cast<cef_errorcode_t>(error.reason());
|
|
}
|
|
|
|
OnComplete();
|
|
}
|
|
|
|
void OnComplete() {
|
|
DCHECK(CalledOnValidThread());
|
|
|
|
if (body_handle_.is_valid()) {
|
|
return;
|
|
}
|
|
|
|
if (status_ == UR_IO_PENDING) {
|
|
status_ = UR_SUCCESS;
|
|
NotifyUploadProgressIfNecessary();
|
|
}
|
|
|
|
if (loader_.get())
|
|
loader_.reset(nullptr);
|
|
|
|
DCHECK(url_request_.get());
|
|
client_->OnRequestComplete(url_request_.get());
|
|
|
|
// This may result in the Context object being deleted.
|
|
url_request_ = nullptr;
|
|
}
|
|
|
|
void OnBodyReadable(MojoResult, const mojo::HandleSignalsState&) {
|
|
const void* buffer = nullptr;
|
|
uint32_t read_bytes = 0;
|
|
MojoResult result = body_handle_->BeginReadData(&buffer, &read_bytes,
|
|
MOJO_READ_DATA_FLAG_NONE);
|
|
if (result == MOJO_RESULT_SHOULD_WAIT) {
|
|
body_watcher_.ArmOrNotify();
|
|
return;
|
|
}
|
|
|
|
if (result == MOJO_RESULT_FAILED_PRECONDITION) {
|
|
// Whole body has been read.
|
|
body_handle_.reset();
|
|
body_watcher_.Cancel();
|
|
OnComplete();
|
|
return;
|
|
}
|
|
|
|
if (result != MOJO_RESULT_OK) {
|
|
// Something went wrong.
|
|
body_handle_.reset();
|
|
body_watcher_.Cancel();
|
|
OnComplete();
|
|
return;
|
|
}
|
|
|
|
download_data_received_ += read_bytes;
|
|
|
|
client_->OnDownloadProgress(url_request_.get(), download_data_received_,
|
|
download_data_total_);
|
|
|
|
if (!(request_->GetFlags() & UR_FLAG_NO_DOWNLOAD_DATA)) {
|
|
client_->OnDownloadData(url_request_.get(), buffer, read_bytes);
|
|
}
|
|
|
|
body_handle_->EndReadData(read_bytes);
|
|
body_watcher_.ArmOrNotify();
|
|
}
|
|
|
|
void OnStartLoadingResponseBody(
|
|
mojo::ScopedDataPipeConsumerHandle response_body) {
|
|
DCHECK(CalledOnValidThread());
|
|
DCHECK(response_body);
|
|
DCHECK(!body_handle_);
|
|
body_handle_ = std::move(response_body);
|
|
|
|
body_watcher_.Watch(
|
|
body_handle_.get(),
|
|
MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
|
|
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
|
|
base::BindRepeating(&CefRenderURLRequest::Context::OnBodyReadable,
|
|
base::Unretained(this)));
|
|
body_watcher_.ArmOrNotify();
|
|
}
|
|
|
|
void OnDownloadProgress(int64_t current) {
|
|
DCHECK(CalledOnValidThread());
|
|
DCHECK(url_request_.get());
|
|
|
|
NotifyUploadProgressIfNecessary();
|
|
|
|
download_data_received_ += current;
|
|
client_->OnDownloadProgress(url_request_.get(), download_data_received_,
|
|
download_data_total_);
|
|
}
|
|
|
|
void OnDownloadData(const char* data, int dataLength) {
|
|
DCHECK(CalledOnValidThread());
|
|
DCHECK(url_request_.get());
|
|
client_->OnDownloadData(url_request_.get(), data, dataLength);
|
|
}
|
|
|
|
void OnUploadProgress(int64_t current, int64_t total) {
|
|
DCHECK(CalledOnValidThread());
|
|
DCHECK(url_request_.get());
|
|
if (current == total)
|
|
got_upload_progress_complete_ = true;
|
|
client_->OnUploadProgress(url_request_.get(), current, total);
|
|
}
|
|
|
|
CefRefPtr<CefRequest> request() const { return request_; }
|
|
CefRefPtr<CefURLRequestClient> client() const { return client_; }
|
|
CefURLRequest::Status status() const { return status_; }
|
|
CefURLRequest::ErrorCode error_code() const { return error_code_; }
|
|
CefRefPtr<CefResponse> response() const { return response_; }
|
|
bool response_was_cached() const { return response_was_cached_; }
|
|
|
|
private:
|
|
friend class base::RefCountedThreadSafe<CefRenderURLRequest::Context>;
|
|
|
|
virtual ~Context() {}
|
|
|
|
void NotifyUploadProgressIfNecessary() {
|
|
if (!got_upload_progress_complete_ && upload_data_size_ > 0) {
|
|
// Upload notifications are sent using a timer and may not occur if the
|
|
// request completes too quickly. We therefore send the notification here
|
|
// if necessary.
|
|
url_client_->DidSendData(upload_data_size_, upload_data_size_);
|
|
got_upload_progress_complete_ = true;
|
|
}
|
|
}
|
|
|
|
// Members only accessed on the initialization thread.
|
|
CefRefPtr<CefRenderURLRequest> url_request_;
|
|
CefRefPtr<CefFrame> frame_;
|
|
CefRefPtr<CefRequest> request_;
|
|
CefRefPtr<CefURLRequestClient> client_;
|
|
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
|
CefURLRequest::Status status_;
|
|
CefURLRequest::ErrorCode error_code_;
|
|
CefRefPtr<CefResponse> response_;
|
|
mojo::ScopedDataPipeConsumerHandle body_handle_;
|
|
mojo::SimpleWatcher body_watcher_;
|
|
bool response_was_cached_;
|
|
std::unique_ptr<blink::WebURLLoader> loader_;
|
|
std::unique_ptr<CefWebURLLoaderClient> url_client_;
|
|
int64_t upload_data_size_;
|
|
bool got_upload_progress_complete_;
|
|
int64_t download_data_received_;
|
|
int64_t download_data_total_;
|
|
};
|
|
|
|
// CefWebURLLoaderClient --------------------------------------------------
|
|
|
|
namespace {
|
|
|
|
CefWebURLLoaderClient::CefWebURLLoaderClient(
|
|
CefRenderURLRequest::Context* context,
|
|
int request_flags)
|
|
: context_(context), request_flags_(request_flags) {}
|
|
|
|
CefWebURLLoaderClient::~CefWebURLLoaderClient() {}
|
|
|
|
void CefWebURLLoaderClient::DidSendData(uint64_t bytes_sent,
|
|
uint64_t total_bytes_to_be_sent) {
|
|
if (request_flags_ & UR_FLAG_REPORT_UPLOAD_PROGRESS)
|
|
context_->OnUploadProgress(bytes_sent, total_bytes_to_be_sent);
|
|
}
|
|
|
|
void CefWebURLLoaderClient::DidReceiveResponse(const WebURLResponse& response) {
|
|
context_->OnResponse(response);
|
|
}
|
|
|
|
void CefWebURLLoaderClient::DidReceiveData(const char* data, int dataLength) {
|
|
context_->OnDownloadProgress(dataLength);
|
|
|
|
if (!(request_flags_ & UR_FLAG_NO_DOWNLOAD_DATA))
|
|
context_->OnDownloadData(data, dataLength);
|
|
}
|
|
|
|
void CefWebURLLoaderClient::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) {
|
|
context_->OnComplete();
|
|
}
|
|
|
|
void CefWebURLLoaderClient::DidFail(const WebURLError& error,
|
|
int64_t total_encoded_data_length,
|
|
int64_t total_encoded_body_length,
|
|
int64_t total_decoded_body_length) {
|
|
context_->OnError(error);
|
|
}
|
|
|
|
void CefWebURLLoaderClient::DidStartLoadingResponseBody(
|
|
mojo::ScopedDataPipeConsumerHandle response_body) {
|
|
context_->OnStartLoadingResponseBody(std::move(response_body));
|
|
}
|
|
|
|
bool CefWebURLLoaderClient::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) {
|
|
if (request_flags_ & UR_FLAG_STOP_ON_REDIRECT) {
|
|
context_->OnStopRedirect(new_url, passed_redirect_response);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// CefRenderURLRequest --------------------------------------------------------
|
|
|
|
CefRenderURLRequest::CefRenderURLRequest(
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request,
|
|
CefRefPtr<CefURLRequestClient> client) {
|
|
context_ = new Context(this, frame, request, client);
|
|
}
|
|
|
|
CefRenderURLRequest::~CefRenderURLRequest() {}
|
|
|
|
bool CefRenderURLRequest::Start() {
|
|
if (!VerifyContext())
|
|
return false;
|
|
return context_->Start();
|
|
}
|
|
|
|
CefRefPtr<CefRequest> CefRenderURLRequest::GetRequest() {
|
|
if (!VerifyContext())
|
|
return nullptr;
|
|
return context_->request();
|
|
}
|
|
|
|
CefRefPtr<CefURLRequestClient> CefRenderURLRequest::GetClient() {
|
|
if (!VerifyContext())
|
|
return nullptr;
|
|
return context_->client();
|
|
}
|
|
|
|
CefURLRequest::Status CefRenderURLRequest::GetRequestStatus() {
|
|
if (!VerifyContext())
|
|
return UR_UNKNOWN;
|
|
return context_->status();
|
|
}
|
|
|
|
CefURLRequest::ErrorCode CefRenderURLRequest::GetRequestError() {
|
|
if (!VerifyContext())
|
|
return ERR_NONE;
|
|
return context_->error_code();
|
|
}
|
|
|
|
CefRefPtr<CefResponse> CefRenderURLRequest::GetResponse() {
|
|
if (!VerifyContext())
|
|
return nullptr;
|
|
return context_->response();
|
|
}
|
|
|
|
bool CefRenderURLRequest::ResponseWasCached() {
|
|
if (!VerifyContext())
|
|
return false;
|
|
return context_->response_was_cached();
|
|
}
|
|
|
|
void CefRenderURLRequest::Cancel() {
|
|
if (!VerifyContext())
|
|
return;
|
|
return context_->Cancel();
|
|
}
|
|
|
|
bool CefRenderURLRequest::VerifyContext() {
|
|
DCHECK(context_.get());
|
|
if (!context_->CalledOnValidThread()) {
|
|
NOTREACHED() << "called on invalid thread";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|