cef/libcef/renderer/render_urlrequest_impl.cc

376 lines
11 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 "libcef/common/request_impl.h"
#include "libcef/common/response_impl.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebKitPlatformSupport.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURL.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLError.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLLoader.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLLoaderClient.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLRequest.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLResponse.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
using WebKit::WebString;
using WebKit::WebURL;
using WebKit::WebURLError;
using WebKit::WebURLLoader;
using WebKit::WebURLRequest;
using WebKit::WebURLResponse;
namespace {
class CefWebURLLoaderClient : public WebKit::WebURLLoaderClient {
public:
CefWebURLLoaderClient(CefRenderURLRequest::Context* context,
int request_flags);
virtual ~CefWebURLLoaderClient();
// WebKit::WebURLLoaderClient methods.
virtual void willSendRequest(
WebURLLoader* loader,
WebURLRequest& newRequest,
const WebURLResponse& redirectResponse) OVERRIDE;
virtual void didSendData(
WebURLLoader* loader,
unsigned long long bytesSent,
unsigned long long totalBytesToBeSent) OVERRIDE;
virtual void didReceiveResponse(
WebURLLoader* loader,
const WebURLResponse& response) OVERRIDE;
virtual void didDownloadData(WebURLLoader* loader,
int dataLength) OVERRIDE;
virtual void didReceiveData(WebURLLoader* loader,
const char* data,
int dataLength,
int encodedDataLength) OVERRIDE;
virtual void didReceiveCachedMetadata(WebURLLoader* loader,
const char* data,
int dataLength) OVERRIDE;
virtual void didFinishLoading(WebURLLoader* loader,
double finishTime) OVERRIDE;
virtual void didFail(WebURLLoader* loader,
const WebURLError& error) 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<CefRequest> request,
CefRefPtr<CefURLRequestClient> client)
: url_request_(url_request),
request_(request),
client_(client),
message_loop_proxy_(MessageLoop::current()->message_loop_proxy()),
status_(UR_IO_PENDING),
error_code_(ERR_NONE),
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);
}
virtual ~Context() {
}
inline bool CalledOnValidThread() {
return message_loop_proxy_->BelongsToCurrentThread();
}
bool Start() {
DCHECK(CalledOnValidThread());
GURL url = GURL(request_->GetURL().ToString());
if (!url.is_valid())
return false;
loader_.reset(WebKit::webKitPlatformSupport()->createURLLoader());
url_client_.reset(new CefWebURLLoaderClient(this, request_->GetFlags()));
WebURLRequest urlRequest;
static_cast<CefRequestImpl*>(request_.get())->Get(urlRequest);
if (urlRequest.reportUploadProgress()) {
// Attempt to determine the upload data size.
CefRefPtr<CefPostData> post_data = request_->GetPostData();
if (post_data.get()) {
CefPostData::ElementVector elements;
post_data->GetElements(elements);
if (elements.size() == 1 && elements[0]->GetType() == PDE_TYPE_BYTES) {
CefPostDataElementImpl* impl =
static_cast<CefPostDataElementImpl*>(elements[0].get());
upload_data_size_ = impl->GetBytesCount();
}
}
}
loader_->loadAsynchronously(urlRequest, 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 OnResponse(const WebURLResponse& response) {
DCHECK(CalledOnValidThread());
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<CefURLRequest::ErrorCode>(error.reason);
}
OnComplete();
}
void OnComplete() {
DCHECK(CalledOnValidThread());
if (status_ == UR_IO_PENDING) {
status_ = UR_SUCCESS;
NotifyUploadProgressIfNecessary();
}
if (loader_.get())
loader_.reset(NULL);
DCHECK(url_request_.get());
client_->OnRequestComplete(url_request_.get());
// This may result in the Context object being deleted.
url_request_ = NULL;
}
void OnDownloadProgress(int64 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 current, int64 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() { return request_; }
CefRefPtr<CefURLRequestClient> client() { return client_; }
CefURLRequest::Status status() { return status_; }
CefURLRequest::ErrorCode error_code() { return error_code_; }
CefRefPtr<CefResponse> response() { return response_; }
private:
void NotifyUploadProgressIfNecessary() {
if (!got_upload_progress_complete_ && upload_data_size_ > 0) {
// URLFetcher sends upload notifications using a timer and will not send
// a notification if the request completes too quickly. We therefore
// send the notification here if necessary.
client_->OnUploadProgress(url_request_.get(), upload_data_size_,
upload_data_size_);
got_upload_progress_complete_ = true;
}
}
// Members only accessed on the initialization thread.
CefRefPtr<CefRenderURLRequest> url_request_;
CefRefPtr<CefRequest> request_;
CefRefPtr<CefURLRequestClient> client_;
scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
CefURLRequest::Status status_;
CefURLRequest::ErrorCode error_code_;
CefRefPtr<CefResponse> response_;
scoped_ptr<WebKit::WebURLLoader> loader_;
scoped_ptr<CefWebURLLoaderClient> url_client_;
int64 upload_data_size_;
bool got_upload_progress_complete_;
int64 download_data_received_;
int64 download_data_total_;
};
// CefWebURLLoaderClient --------------------------------------------------
namespace {
CefWebURLLoaderClient::CefWebURLLoaderClient(
CefRenderURLRequest::Context* context,
int request_flags)
: context_(context),
request_flags_(request_flags) {
}
CefWebURLLoaderClient::~CefWebURLLoaderClient() {
}
void CefWebURLLoaderClient::willSendRequest(
WebURLLoader* loader,
WebURLRequest& newRequest,
const WebURLResponse& redirectResponse) {
}
void CefWebURLLoaderClient::didSendData(
WebURLLoader* loader,
unsigned long long bytesSent,
unsigned long long totalBytesToBeSent) {
if (request_flags_ & UR_FLAG_REPORT_UPLOAD_PROGRESS)
context_->OnUploadProgress(bytesSent, totalBytesToBeSent);
}
void CefWebURLLoaderClient::didReceiveResponse(
WebURLLoader* loader,
const WebURLResponse& response) {
context_->OnResponse(response);
}
void CefWebURLLoaderClient::didDownloadData(WebURLLoader* loader,
int dataLength) {
}
void CefWebURLLoaderClient::didReceiveData(WebURLLoader* loader,
const char* data,
int dataLength,
int encodedDataLength) {
context_->OnDownloadProgress(dataLength);
if (!(request_flags_ & UR_FLAG_NO_DOWNLOAD_DATA))
context_->OnDownloadData(data, dataLength);
}
void CefWebURLLoaderClient::didReceiveCachedMetadata(WebURLLoader* loader,
const char* data,
int dataLength) {
}
void CefWebURLLoaderClient::didFinishLoading(WebURLLoader* loader,
double finishTime) {
context_->OnComplete();
}
void CefWebURLLoaderClient::didFail(WebURLLoader* loader,
const WebURLError& error) {
context_->OnError(error);
}
} // namespace
// CefRenderURLRequest --------------------------------------------------------
CefRenderURLRequest::CefRenderURLRequest(
CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client) {
context_ = new Context(this, request, client);
}
CefRenderURLRequest::~CefRenderURLRequest() {
}
bool CefRenderURLRequest::Start() {
if (!VerifyContext())
return false;
return context_->Start();
}
CefRefPtr<CefRequest> CefRenderURLRequest::GetRequest() {
if (!VerifyContext())
return NULL;
return context_->request();
}
CefRefPtr<CefURLRequestClient> CefRenderURLRequest::GetClient() {
if (!VerifyContext())
return NULL;
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 NULL;
return context_->response();
}
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;
}