#include "ppapi/proxy/ppb_url_loader_proxy.h"
#include <algorithm>
#include <deque>
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/ppb_url_loader.h"
#include "ppapi/c/private/ppb_proxy_private.h"
#include "ppapi/c/trusted/ppb_url_loader_trusted.h"
#include "ppapi/proxy/enter_proxy.h"
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/plugin_resource_tracker.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/ppb_url_response_info_proxy.h"
#include "ppapi/shared_impl/scoped_pp_resource.h"
#include "ppapi/shared_impl/tracked_callback.h"
#include "ppapi/thunk/enter.h"
#include "ppapi/thunk/ppb_url_loader_api.h"
#include "ppapi/thunk/ppb_url_request_info_api.h"
#include "ppapi/thunk/resource_creation_api.h"
#include "ppapi/thunk/thunk.h"
#if defined(OS_LINUX)
#include <sys/shm.h>
#endif
using ppapi::thunk::EnterResourceNoLock;
using ppapi::thunk::PPB_URLLoader_API;
using ppapi::thunk::ResourceCreationAPI;
namespace ppapi {
namespace proxy {
namespace {
const int32_t kMaxReadBufferSize = 16777216;
void UpdateResourceLoadStatus(PP_Instance pp_instance,
PP_Resource pp_resource,
int64 bytes_sent,
int64 total_bytes_to_be_sent,
int64 bytes_received,
int64 total_bytes_to_be_received) {
#if !defined(OS_NACL)
Dispatcher* dispatcher = HostDispatcher::GetForInstance(pp_instance);
if (!dispatcher)
return;
PPBURLLoader_UpdateProgress_Params params;
params.instance = pp_instance;
params.resource.SetHostResource(pp_instance, pp_resource);
params.bytes_sent = bytes_sent;
params.total_bytes_to_be_sent = total_bytes_to_be_sent;
params.bytes_received = bytes_received;
params.total_bytes_to_be_received = total_bytes_to_be_received;
dispatcher->Send(new PpapiMsg_PPBURLLoader_UpdateProgress(
API_ID_PPB_URL_LOADER, params));
#endif
}
InterfaceProxy* CreateURLLoaderProxy(Dispatcher* dispatcher) {
return new PPB_URLLoader_Proxy(dispatcher);
}
}
class URLLoader : public Resource, public PPB_URLLoader_API {
public:
URLLoader(const HostResource& resource);
virtual ~URLLoader();
virtual PPB_URLLoader_API* AsPPB_URLLoader_API() OVERRIDE;
virtual int32_t Open(PP_Resource request_id,
scoped_refptr<TrackedCallback> callback) OVERRIDE;
virtual int32_t Open(const URLRequestInfoData& data,
scoped_refptr<TrackedCallback> callback) OVERRIDE;
virtual int32_t FollowRedirect(
scoped_refptr<TrackedCallback> callback) OVERRIDE;
virtual PP_Bool GetUploadProgress(int64_t* bytes_sent,
int64_t* total_bytes_to_be_sent) OVERRIDE;
virtual PP_Bool GetDownloadProgress(
int64_t* bytes_received,
int64_t* total_bytes_to_be_received) OVERRIDE;
virtual PP_Resource GetResponseInfo() OVERRIDE;
virtual int32_t ReadResponseBody(
void* buffer,
int32_t bytes_to_read,
scoped_refptr<TrackedCallback> callback) OVERRIDE;
virtual int32_t FinishStreamingToFile(
scoped_refptr<TrackedCallback> callback) OVERRIDE;
virtual void Close() OVERRIDE;
virtual void GrantUniversalAccess() OVERRIDE;
virtual void SetStatusCallback(
PP_URLLoaderTrusted_StatusCallback cb) OVERRIDE;
void UpdateProgress(const PPBURLLoader_UpdateProgress_Params& params);
void ReadResponseBodyAck(int32_t result, const char* data);
void CallbackComplete(int32_t result);
private:
void PopBuffer(void* output_buffer, int32_t output_size);
PluginDispatcher* GetDispatcher() const {
return PluginDispatcher::GetForResource(this);
}
int64_t bytes_sent_;
int64_t total_bytes_to_be_sent_;
int64_t bytes_received_;
int64_t total_bytes_to_be_received_;
scoped_refptr<TrackedCallback> current_callback_;
void* current_read_buffer_;
int32_t current_read_buffer_size_;
std::deque<char> buffer_;
PP_Resource response_info_;
private:
DISALLOW_COPY_AND_ASSIGN(URLLoader);
};
URLLoader::URLLoader(const HostResource& resource)
: Resource(OBJECT_IS_PROXY, resource),
bytes_sent_(-1),
total_bytes_to_be_sent_(-1),
bytes_received_(-1),
total_bytes_to_be_received_(-1),
current_read_buffer_(NULL),
current_read_buffer_size_(0),
response_info_(0) {
}
URLLoader::~URLLoader() {
if (response_info_)
PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(response_info_);
}
PPB_URLLoader_API* URLLoader::AsPPB_URLLoader_API() {
return this;
}
int32_t URLLoader::Open(PP_Resource request_id,
scoped_refptr<TrackedCallback> callback) {
EnterResourceNoLock<thunk::PPB_URLRequestInfo_API> enter(request_id, true);
if (enter.failed()) {
Log(PP_LOGLEVEL_ERROR, "PPB_URLLoader.Open: The URL you're requesting is "
" on a different security origin than your plugin. To request "
" cross-origin resources, see "
" PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS.");
return PP_ERROR_BADRESOURCE;
}
return Open(enter.object()->GetData(), callback);
}
int32_t URLLoader::Open(const URLRequestInfoData& data,
scoped_refptr<TrackedCallback> callback) {
if (TrackedCallback::IsPending(current_callback_))
return PP_ERROR_INPROGRESS;
current_callback_ = callback;
GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_Open(
API_ID_PPB_URL_LOADER, host_resource(), data));
return PP_OK_COMPLETIONPENDING;
}
int32_t URLLoader::FollowRedirect(scoped_refptr<TrackedCallback> callback) {
if (TrackedCallback::IsPending(current_callback_))
return PP_ERROR_INPROGRESS;
current_callback_ = callback;
GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_FollowRedirect(
API_ID_PPB_URL_LOADER, host_resource()));
return PP_OK_COMPLETIONPENDING;
}
PP_Bool URLLoader::GetUploadProgress(int64_t* bytes_sent,
int64_t* total_bytes_to_be_sent) {
if (bytes_sent_ == -1) {
*bytes_sent = 0;
*total_bytes_to_be_sent = 0;
return PP_FALSE;
}
*bytes_sent = bytes_sent_;
*total_bytes_to_be_sent = total_bytes_to_be_sent_;
return PP_TRUE;
}
PP_Bool URLLoader::GetDownloadProgress(
int64_t* bytes_received,
int64_t* total_bytes_to_be_received) {
if (bytes_received_ == -1) {
*bytes_received = 0;
*total_bytes_to_be_received = 0;
return PP_FALSE;
}
*bytes_received = bytes_received_;
*total_bytes_to_be_received = total_bytes_to_be_received_;
return PP_TRUE;
}
PP_Resource URLLoader::GetResponseInfo() {
if (!response_info_) {
HostResource response_id;
GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_GetResponseInfo(
API_ID_PPB_URL_LOADER, host_resource(), &response_id));
if (response_id.is_null())
return 0;
response_info_ = PPB_URLResponseInfo_Proxy::CreateResponseForResource(
response_id);
}
PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(response_info_);
return response_info_;
}
int32_t URLLoader::ReadResponseBody(void* buffer,
int32_t bytes_to_read,
scoped_refptr<TrackedCallback> callback) {
if (!buffer || bytes_to_read <= 0)
return PP_ERROR_BADARGUMENT;
if (TrackedCallback::IsPending(current_callback_))
return PP_ERROR_INPROGRESS;
if (buffer_.size()) {
int32_t bytes_to_return =
std::min(bytes_to_read, static_cast<int32_t>(buffer_.size()));
PopBuffer(buffer, bytes_to_return);
return bytes_to_return;
}
current_callback_ = callback;
current_read_buffer_ = buffer;
current_read_buffer_size_ = bytes_to_read;
GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_ReadResponseBody(
API_ID_PPB_URL_LOADER, host_resource(), bytes_to_read));
return PP_OK_COMPLETIONPENDING;
}
int32_t URLLoader::FinishStreamingToFile(
scoped_refptr<TrackedCallback> callback) {
if (TrackedCallback::IsPending(current_callback_))
return PP_ERROR_INPROGRESS;
current_callback_ = callback;
GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_FinishStreamingToFile(
API_ID_PPB_URL_LOADER, host_resource()));
return PP_OK_COMPLETIONPENDING;
}
void URLLoader::Close() {
GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_Close(
API_ID_PPB_URL_LOADER, host_resource()));
}
void URLLoader::GrantUniversalAccess() {
GetDispatcher()->Send(
new PpapiHostMsg_PPBURLLoader_GrantUniversalAccess(
API_ID_PPB_URL_LOADER, host_resource()));
}
void URLLoader::SetStatusCallback(
PP_URLLoaderTrusted_StatusCallback cb) {
}
void URLLoader::UpdateProgress(
const PPBURLLoader_UpdateProgress_Params& params) {
bytes_sent_ = params.bytes_sent;
total_bytes_to_be_sent_ = params.total_bytes_to_be_sent;
bytes_received_ = params.bytes_received;
total_bytes_to_be_received_ = params.total_bytes_to_be_received;
}
void URLLoader::ReadResponseBodyAck(int32 result, const char* data) {
if (!TrackedCallback::IsPending(current_callback_) || !current_read_buffer_) {
NOTREACHED();
return;
}
if (result >= 0) {
DCHECK_EQ(0U, buffer_.size());
int32_t bytes_to_return = std::min(current_read_buffer_size_, result);
std::copy(data,
data + bytes_to_return,
static_cast<char*>(current_read_buffer_));
if (result > bytes_to_return) {
buffer_.insert(buffer_.end(),
data + bytes_to_return,
data + result);
}
result = bytes_to_return;
}
TrackedCallback::ClearAndRun(¤t_callback_, result);
}
void URLLoader::CallbackComplete(int32_t result) {
TrackedCallback::ClearAndRun(¤t_callback_, result);
}
void URLLoader::PopBuffer(void* output_buffer, int32_t output_size) {
CHECK(output_size <= static_cast<int32_t>(buffer_.size()));
std::copy(buffer_.begin(),
buffer_.begin() + output_size,
static_cast<char*>(output_buffer));
buffer_.erase(buffer_.begin(),
buffer_.begin() + output_size);
}
PPB_URLLoader_Proxy::PPB_URLLoader_Proxy(Dispatcher* dispatcher)
: InterfaceProxy(dispatcher),
callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
}
PPB_URLLoader_Proxy::~PPB_URLLoader_Proxy() {
}
PP_Resource PPB_URLLoader_Proxy::TrackPluginResource(
const HostResource& url_loader_resource) {
return (new URLLoader(url_loader_resource))->GetReference();
}
const InterfaceProxy::Info* PPB_URLLoader_Proxy::GetTrustedInfo() {
static const Info info = {
thunk::GetPPB_URLLoaderTrusted_0_3_Thunk(),
PPB_URLLOADERTRUSTED_INTERFACE_0_3,
API_ID_NONE,
false,
&CreateURLLoaderProxy
};
return &info;
}
PP_Resource PPB_URLLoader_Proxy::CreateProxyResource(PP_Instance pp_instance) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(pp_instance);
if (!dispatcher)
return 0;
HostResource result;
dispatcher->Send(new PpapiHostMsg_PPBURLLoader_Create(
API_ID_PPB_URL_LOADER, pp_instance, &result));
if (result.is_null())
return 0;
return PPB_URLLoader_Proxy::TrackPluginResource(result);
}
bool PPB_URLLoader_Proxy::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PPB_URLLoader_Proxy, msg)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_Create,
OnMsgCreate)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_Open,
OnMsgOpen)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_FollowRedirect,
OnMsgFollowRedirect)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_GetResponseInfo,
OnMsgGetResponseInfo)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_ReadResponseBody,
OnMsgReadResponseBody)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_FinishStreamingToFile,
OnMsgFinishStreamingToFile)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_Close,
OnMsgClose)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_GrantUniversalAccess,
OnMsgGrantUniversalAccess)
#if !defined(OS_NACL)
IPC_MESSAGE_HANDLER(PpapiMsg_PPBURLLoader_UpdateProgress,
OnMsgUpdateProgress)
#endif
IPC_MESSAGE_HANDLER(PpapiMsg_PPBURLLoader_ReadResponseBody_Ack,
OnMsgReadResponseBodyAck)
IPC_MESSAGE_HANDLER(PpapiMsg_PPBURLLoader_CallbackComplete,
OnMsgCallbackComplete)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void PPB_URLLoader_Proxy::PrepareURLLoaderForSendingToPlugin(
PP_Resource resource) {
EnterResourceNoLock<PPB_URLLoader_API> enter(resource, false);
if (enter.succeeded())
enter.object()->SetStatusCallback(&UpdateResourceLoadStatus);
else
NOTREACHED();
}
void PPB_URLLoader_Proxy::OnMsgCreate(PP_Instance instance,
HostResource* result) {
thunk::EnterResourceCreation enter(instance);
if (enter.succeeded()) {
result->SetHostResource(instance,
enter.functions()->CreateURLLoader(instance));
PrepareURLLoaderForSendingToPlugin(result->host_resource());
}
}
void PPB_URLLoader_Proxy::OnMsgOpen(const HostResource& loader,
const URLRequestInfoData& data) {
EnterHostFromHostResourceForceCallback<PPB_URLLoader_API> enter(
loader, callback_factory_, &PPB_URLLoader_Proxy::OnCallback, loader);
enter.SetResult(enter.object()->Open(data, enter.callback()));
}
void PPB_URLLoader_Proxy::OnMsgFollowRedirect(
const HostResource& loader) {
EnterHostFromHostResourceForceCallback<PPB_URLLoader_API> enter(
loader, callback_factory_, &PPB_URLLoader_Proxy::OnCallback, loader);
if (enter.succeeded())
enter.SetResult(enter.object()->FollowRedirect(enter.callback()));
}
void PPB_URLLoader_Proxy::OnMsgGetResponseInfo(const HostResource& loader,
HostResource* result) {
EnterHostFromHostResource<PPB_URLLoader_API> enter(loader);
if (enter.succeeded()) {
result->SetHostResource(loader.instance(),
enter.object()->GetResponseInfo());
}
}
void PPB_URLLoader_Proxy::OnMsgReadResponseBody(
const HostResource& loader,
int32_t bytes_to_read) {
if (bytes_to_read < 0) {
bytes_to_read = 0;
}
int32_t synchronously_available_bytes =
static_cast<HostDispatcher*>(dispatcher())->ppb_proxy()->
GetURLLoaderBufferedBytes(loader.host_resource());
if (bytes_to_read < kMaxReadBufferSize) {
bytes_to_read =
std::max(bytes_to_read,
std::min(synchronously_available_bytes, kMaxReadBufferSize));
}
IPC::Message* message =
new PpapiMsg_PPBURLLoader_ReadResponseBody_Ack(API_ID_PPB_URL_LOADER);
IPC::ParamTraits<HostResource>::Write(message, loader);
char* ptr = message->BeginWriteData(bytes_to_read);
if (!ptr) {
}
EnterHostFromHostResourceForceCallback<PPB_URLLoader_API> enter(
loader, callback_factory_, &PPB_URLLoader_Proxy::OnReadCallback, message);
if (enter.succeeded()) {
enter.SetResult(enter.object()->ReadResponseBody(ptr, bytes_to_read,
enter.callback()));
}
}
void PPB_URLLoader_Proxy::OnMsgFinishStreamingToFile(
const HostResource& loader) {
EnterHostFromHostResourceForceCallback<PPB_URLLoader_API> enter(
loader, callback_factory_, &PPB_URLLoader_Proxy::OnCallback, loader);
if (enter.succeeded())
enter.SetResult(enter.object()->FinishStreamingToFile(enter.callback()));;
}
void PPB_URLLoader_Proxy::OnMsgClose(const HostResource& loader) {
EnterHostFromHostResource<PPB_URLLoader_API> enter(loader);
if (enter.succeeded())
enter.object()->Close();
}
void PPB_URLLoader_Proxy::OnMsgGrantUniversalAccess(
const HostResource& loader) {
EnterHostFromHostResource<PPB_URLLoader_API> enter(loader);
if (enter.succeeded())
enter.object()->GrantUniversalAccess();
}
void PPB_URLLoader_Proxy::OnMsgUpdateProgress(
const PPBURLLoader_UpdateProgress_Params& params) {
EnterPluginFromHostResource<PPB_URLLoader_API> enter(params.resource);
if (enter.succeeded())
static_cast<URLLoader*>(enter.object())->UpdateProgress(params);
}
void PPB_URLLoader_Proxy::OnMsgReadResponseBodyAck(
const IPC::Message& message) {
PickleIterator iter(message);
HostResource host_resource;
if (!IPC::ParamTraits<HostResource>::Read(&message, &iter, &host_resource)) {
NOTREACHED() << "Expecting HostResource";
return;
}
const char* data;
int data_len;
if (!iter.ReadData(&data, &data_len)) {
NOTREACHED() << "Expecting data";
return;
}
int result;
if (!iter.ReadInt(&result)) {
NOTREACHED() << "Expecting result";
return;
}
DCHECK(result < 0 || result == data_len);
EnterPluginFromHostResource<PPB_URLLoader_API> enter(host_resource);
if (enter.succeeded())
static_cast<URLLoader*>(enter.object())->ReadResponseBodyAck(result, data);
}
void PPB_URLLoader_Proxy::OnMsgCallbackComplete(
const HostResource& host_resource,
int32_t result) {
EnterPluginFromHostResource<PPB_URLLoader_API> enter(host_resource);
if (enter.succeeded())
static_cast<URLLoader*>(enter.object())->CallbackComplete(result);
}
void PPB_URLLoader_Proxy::OnReadCallback(int32_t result,
IPC::Message* message) {
int32_t bytes_read = 0;
if (result > 0)
bytes_read = result;
message->TrimWriteData(bytes_read);
message->WriteInt(result);
dispatcher()->Send(message);
}
void PPB_URLLoader_Proxy::OnCallback(int32_t result,
const HostResource& resource) {
dispatcher()->Send(new PpapiMsg_PPBURLLoader_CallbackComplete(
API_ID_PPB_URL_LOADER, resource, result));
}
}
}