// 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/Platform/chromium/public/Platform.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebURLError.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebURLLoader.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebURLLoaderClient.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebURLRequest.h" #include "third_party/WebKit/Source/Platform/chromium/public/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 { public: Context(CefRefPtr url_request, CefRefPtr request, CefRefPtr 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(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(request_.get())->Get(urlRequest); if (urlRequest.reportUploadProgress()) { // Attempt to determine the upload data size. CefRefPtr 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(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(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(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 request() { return request_; } CefRefPtr client() { return client_; } CefURLRequest::Status status() { return status_; } CefURLRequest::ErrorCode error_code() { return error_code_; } CefRefPtr 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 url_request_; CefRefPtr request_; CefRefPtr client_; scoped_refptr message_loop_proxy_; CefURLRequest::Status status_; CefURLRequest::ErrorCode error_code_; CefRefPtr response_; scoped_ptr loader_; scoped_ptr 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 request, CefRefPtr client) { context_ = new Context(this, request, client); } CefRenderURLRequest::~CefRenderURLRequest() { } bool CefRenderURLRequest::Start() { if (!VerifyContext()) return false; return context_->Start(); } CefRefPtr CefRenderURLRequest::GetRequest() { if (!VerifyContext()) return NULL; return context_->request(); } CefRefPtr 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 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; }