1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ppapi/proxy/ppb_url_loader_proxy.h"
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "build/build_config.h"
14 #include "ppapi/c/pp_completion_callback.h"
15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/c/pp_resource.h"
17 #include "ppapi/c/ppb_url_loader.h"
18 #include "ppapi/c/private/ppb_proxy_private.h"
19 #include "ppapi/c/trusted/ppb_url_loader_trusted.h"
20 #include "ppapi/proxy/enter_proxy.h"
21 #include "ppapi/proxy/host_dispatcher.h"
22 #include "ppapi/proxy/plugin_dispatcher.h"
23 #include "ppapi/proxy/plugin_resource_tracker.h"
24 #include "ppapi/proxy/ppapi_messages.h"
25 #include "ppapi/proxy/ppb_url_response_info_proxy.h"
26 #include "ppapi/shared_impl/scoped_pp_resource.h"
27 #include "ppapi/thunk/enter.h"
28 #include "ppapi/thunk/ppb_url_loader_api.h"
29 #include "ppapi/thunk/resource_creation_api.h"
30 #include "ppapi/thunk/thunk.h"
36 using ppapi::thunk::EnterFunctionNoLock
;
37 using ppapi::thunk::EnterResourceNoLock
;
38 using ppapi::thunk::PPB_URLLoader_API
;
39 using ppapi::thunk::ResourceCreationAPI
;
46 // The maximum size we'll read into the plugin without being explicitly
47 // asked for a larger buffer.
48 const int32_t kMaxReadBufferSize
= 16777216; // 16MB
50 // Called in the renderer when the byte counts have changed. We send a message
51 // to the plugin to synchronize its counts so it can respond to status polls
53 void UpdateResourceLoadStatus(PP_Instance pp_instance
,
54 PP_Resource pp_resource
,
56 int64 total_bytes_to_be_sent
,
58 int64 total_bytes_to_be_received
) {
59 Dispatcher
* dispatcher
= HostDispatcher::GetForInstance(pp_instance
);
63 PPBURLLoader_UpdateProgress_Params params
;
64 params
.instance
= pp_instance
;
65 params
.resource
.SetHostResource(pp_instance
, pp_resource
);
66 params
.bytes_sent
= bytes_sent
;
67 params
.total_bytes_to_be_sent
= total_bytes_to_be_sent
;
68 params
.bytes_received
= bytes_received
;
69 params
.total_bytes_to_be_received
= total_bytes_to_be_received
;
70 dispatcher
->Send(new PpapiMsg_PPBURLLoader_UpdateProgress(
71 API_ID_PPB_URL_LOADER
, params
));
74 InterfaceProxy
* CreateURLLoaderProxy(Dispatcher
* dispatcher
) {
75 return new PPB_URLLoader_Proxy(dispatcher
);
80 // URLLoader -------------------------------------------------------------------
82 class URLLoader
: public Resource
, public PPB_URLLoader_API
{
84 URLLoader(const HostResource
& resource
);
87 // Resource overrides.
88 virtual PPB_URLLoader_API
* AsPPB_URLLoader_API() OVERRIDE
;
90 // PPB_URLLoader_API implementation.
91 virtual int32_t Open(PP_Resource request_id
,
92 PP_CompletionCallback callback
) OVERRIDE
;
93 virtual int32_t FollowRedirect(PP_CompletionCallback callback
) OVERRIDE
;
94 virtual PP_Bool
GetUploadProgress(int64_t* bytes_sent
,
95 int64_t* total_bytes_to_be_sent
) OVERRIDE
;
96 virtual PP_Bool
GetDownloadProgress(
97 int64_t* bytes_received
,
98 int64_t* total_bytes_to_be_received
) OVERRIDE
;
99 virtual PP_Resource
GetResponseInfo() OVERRIDE
;
100 virtual int32_t ReadResponseBody(void* buffer
,
101 int32_t bytes_to_read
,
102 PP_CompletionCallback callback
) OVERRIDE
;
103 virtual int32_t FinishStreamingToFile(
104 PP_CompletionCallback callback
) OVERRIDE
;
105 virtual void Close() OVERRIDE
;
106 virtual void GrantUniversalAccess() OVERRIDE
;
107 virtual void SetStatusCallback(
108 PP_URLLoaderTrusted_StatusCallback cb
) OVERRIDE
;
110 // Called when the browser has new up/download progress to report.
111 void UpdateProgress(const PPBURLLoader_UpdateProgress_Params
& params
);
113 // Called when the browser responds to our ReadResponseBody request.
114 void ReadResponseBodyAck(int32_t result
, const std::string
& data
);
116 // Called when any callback other than the read callback has been executed.
117 void CallbackComplete(int32_t result
);
120 // Reads the give bytes out of the buffer_, placing them in the given output
121 // buffer, and removes the bytes from the buffer.
123 // The size must be not more than the current size of the buffer.
124 void PopBuffer(void* output_buffer
, int32_t output_size
);
126 PluginDispatcher
* GetDispatcher() const {
127 return PluginDispatcher::GetForResource(this);
130 // Initialized to -1. Will be set to nonnegative values by the UpdateProgress
131 // message when the values are known.
133 int64_t total_bytes_to_be_sent_
;
134 int64_t bytes_received_
;
135 int64_t total_bytes_to_be_received_
;
137 // Current completion callback for the current phase of loading. We have only
138 // one thing (open, follow redirect, read, etc.) outstanding at once.
139 PP_CompletionCallback current_callback_
;
141 // When an asynchronous read is pending, this will contain the buffer to put
142 // the data. The current_callback_ will identify the read callback.
143 void* current_read_buffer_
;
144 int32_t current_read_buffer_size_
;
146 // A buffer of all the data that's been sent to us from the host that we
147 // have yet to send out to the plugin.
148 std::deque
<char> buffer_
;
150 // Cached copy of the response info. When nonzero, we're holding a reference
152 PP_Resource response_info_
;
155 DISALLOW_COPY_AND_ASSIGN(URLLoader
);
158 URLLoader::URLLoader(const HostResource
& resource
)
159 : Resource(resource
),
161 total_bytes_to_be_sent_(-1),
163 total_bytes_to_be_received_(-1),
164 current_callback_(PP_MakeCompletionCallback(NULL
, NULL
)),
165 current_read_buffer_(NULL
),
166 current_read_buffer_size_(0),
170 URLLoader::~URLLoader() {
171 // Always need to fire completion callbacks to prevent a leak in the plugin.
172 if (current_callback_
.func
) {
173 // TODO(brettw) the callbacks at this level should be refactored with a
174 // more automatic tracking system like we have in the renderer.
175 MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(
176 current_callback_
.func
, current_callback_
.user_data
,
177 static_cast<int32_t>(PP_ERROR_ABORTED
)));
181 PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(response_info_
);
184 PPB_URLLoader_API
* URLLoader::AsPPB_URLLoader_API() {
188 int32_t URLLoader::Open(PP_Resource request_id
,
189 PP_CompletionCallback callback
) {
190 EnterResourceNoLock
<thunk::PPB_URLRequestInfo_API
> enter(request_id
, true);
192 return PP_ERROR_BADRESOURCE
;
194 if (current_callback_
.func
)
195 return PP_ERROR_INPROGRESS
;
198 return PP_ERROR_BLOCKS_MAIN_THREAD
;
199 current_callback_
= callback
;
201 GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_Open(
202 API_ID_PPB_URL_LOADER
, host_resource(), enter
.object()->GetData()));
203 return PP_OK_COMPLETIONPENDING
;
206 int32_t URLLoader::FollowRedirect(PP_CompletionCallback callback
) {
207 if (current_callback_
.func
)
208 return PP_ERROR_INPROGRESS
;
211 return PP_ERROR_BLOCKS_MAIN_THREAD
;
212 current_callback_
= callback
;
214 GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_FollowRedirect(
215 API_ID_PPB_URL_LOADER
, host_resource()));
216 return PP_OK_COMPLETIONPENDING
;
219 PP_Bool
URLLoader::GetUploadProgress(int64_t* bytes_sent
,
220 int64_t* total_bytes_to_be_sent
) {
221 if (bytes_sent_
== -1) {
223 *total_bytes_to_be_sent
= 0;
226 *bytes_sent
= bytes_sent_
;
227 *total_bytes_to_be_sent
= total_bytes_to_be_sent_
;
231 PP_Bool
URLLoader::GetDownloadProgress(
232 int64_t* bytes_received
,
233 int64_t* total_bytes_to_be_received
) {
234 if (bytes_received_
== -1) {
236 *total_bytes_to_be_received
= 0;
239 *bytes_received
= bytes_received_
;
240 *total_bytes_to_be_received
= total_bytes_to_be_received_
;
244 PP_Resource
URLLoader::GetResponseInfo() {
245 if (!response_info_
) {
246 HostResource response_id
;
247 GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_GetResponseInfo(
248 API_ID_PPB_URL_LOADER
, host_resource(), &response_id
));
249 if (response_id
.is_null())
252 response_info_
= PPB_URLResponseInfo_Proxy::CreateResponseForResource(
256 // The caller expects to get a ref, and we want to keep holding ours.
257 PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(response_info_
);
258 return response_info_
;
261 int32_t URLLoader::ReadResponseBody(void* buffer
,
262 int32_t bytes_to_read
,
263 PP_CompletionCallback callback
) {
264 if (!buffer
|| bytes_to_read
<= 0)
265 return PP_ERROR_BADARGUMENT
; // Must specify an output buffer.
266 if (current_callback_
.func
)
267 return PP_ERROR_INPROGRESS
; // Can only have one request pending.
269 // Currently we don't support sync calls to read. We'll need to revisit
270 // how this works when we allow blocking calls (from background threads).
272 return PP_ERROR_BADARGUMENT
;
274 if (static_cast<size_t>(bytes_to_read
) <= buffer_
.size()) {
275 // Special case: we've buffered enough data to be able to synchronously
276 // return data to the caller. Do so without making IPCs.
277 PopBuffer(buffer
, bytes_to_read
);
278 return bytes_to_read
;
281 current_callback_
= callback
;
282 current_read_buffer_
= buffer
;
283 current_read_buffer_size_
= bytes_to_read
;
285 GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_ReadResponseBody(
286 API_ID_PPB_URL_LOADER
, host_resource(), bytes_to_read
));
287 return PP_OK_COMPLETIONPENDING
;
290 int32_t URLLoader::FinishStreamingToFile(PP_CompletionCallback callback
) {
291 if (current_callback_
.func
)
292 return PP_ERROR_INPROGRESS
;
295 return PP_ERROR_BLOCKS_MAIN_THREAD
;
296 current_callback_
= callback
;
298 GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_FinishStreamingToFile(
299 API_ID_PPB_URL_LOADER
, host_resource()));
300 return PP_OK_COMPLETIONPENDING
;
303 void URLLoader::Close() {
304 GetDispatcher()->Send(new PpapiHostMsg_PPBURLLoader_Close(
305 API_ID_PPB_URL_LOADER
, host_resource()));
308 void URLLoader::GrantUniversalAccess() {
309 GetDispatcher()->Send(
310 new PpapiHostMsg_PPBURLLoader_GrantUniversalAccess(
311 API_ID_PPB_URL_LOADER
, host_resource()));
314 void URLLoader::SetStatusCallback(
315 PP_URLLoaderTrusted_StatusCallback cb
) {
316 // Not implemented in the proxied version, this is for implementing the
317 // proxy itself in the host.
320 void URLLoader::UpdateProgress(
321 const PPBURLLoader_UpdateProgress_Params
& params
) {
322 bytes_sent_
= params
.bytes_sent
;
323 total_bytes_to_be_sent_
= params
.total_bytes_to_be_sent
;
324 bytes_received_
= params
.bytes_received
;
325 total_bytes_to_be_received_
= params
.total_bytes_to_be_received
;
328 void URLLoader::ReadResponseBodyAck(int32 result
, const std::string
& data
) {
329 if (!current_callback_
.func
|| !current_read_buffer_
) {
334 // Append the data we requested to the internal buffer.
335 // TODO(brettw) avoid double-copying data that's coming from IPC and going
336 // into the plugin buffer (we can skip the internal buffer in this case).
337 buffer_
.insert(buffer_
.end(), data
.begin(), data
.end());
340 // Fill the user buffer. We may get fewer bytes than requested in the
341 // case of stream end.
342 int32_t bytes_to_return
= std::min(current_read_buffer_size_
,
343 static_cast<int32_t>(buffer_
.size()));
344 PopBuffer(current_read_buffer_
, bytes_to_return
);
345 result
= bytes_to_return
;
348 // The plugin should be able to make a new request from their callback, so
349 // we have to clear our copy first.
350 PP_RunAndClearCompletionCallback(¤t_callback_
, result
);
353 void URLLoader::CallbackComplete(int32_t result
) {
354 PP_RunAndClearCompletionCallback(¤t_callback_
, result
);
357 void URLLoader::PopBuffer(void* output_buffer
, int32_t output_size
) {
358 CHECK(output_size
<= static_cast<int32_t>(buffer_
.size()));
359 std::copy(buffer_
.begin(),
360 buffer_
.begin() + output_size
,
361 static_cast<char*>(output_buffer
));
362 buffer_
.erase(buffer_
.begin(),
363 buffer_
.begin() + output_size
);
366 // PPB_URLLoader_Proxy ---------------------------------------------------------
368 struct PPB_URLLoader_Proxy::ReadCallbackInfo
{
369 HostResource resource
;
370 std::string read_buffer
;
373 PPB_URLLoader_Proxy::PPB_URLLoader_Proxy(Dispatcher
* dispatcher
)
374 : InterfaceProxy(dispatcher
),
375 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
376 host_urlloader_trusted_interface_(NULL
) {
379 PPB_URLLoader_Proxy::~PPB_URLLoader_Proxy() {
383 PP_Resource
PPB_URLLoader_Proxy::TrackPluginResource(
384 const HostResource
& url_loader_resource
) {
385 return (new URLLoader(url_loader_resource
))->GetReference();
389 const InterfaceProxy::Info
* PPB_URLLoader_Proxy::GetTrustedInfo() {
390 static const Info info
= {
391 thunk::GetPPB_URLLoaderTrusted_Thunk(),
392 PPB_URLLOADERTRUSTED_INTERFACE
,
393 API_ID_NONE
, // URL_LOADER is the canonical one.
395 &CreateURLLoaderProxy
401 PP_Resource
PPB_URLLoader_Proxy::CreateProxyResource(PP_Instance pp_instance
) {
402 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForInstance(pp_instance
);
407 dispatcher
->Send(new PpapiHostMsg_PPBURLLoader_Create(
408 API_ID_PPB_URL_LOADER
, pp_instance
, &result
));
409 if (result
.is_null())
411 return PPB_URLLoader_Proxy::TrackPluginResource(result
);
414 bool PPB_URLLoader_Proxy::OnMessageReceived(const IPC::Message
& msg
) {
416 IPC_BEGIN_MESSAGE_MAP(PPB_URLLoader_Proxy
, msg
)
417 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_Create
,
419 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_Open
,
421 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_FollowRedirect
,
423 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_GetResponseInfo
,
424 OnMsgGetResponseInfo
)
425 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_ReadResponseBody
,
426 OnMsgReadResponseBody
)
427 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_FinishStreamingToFile
,
428 OnMsgFinishStreamingToFile
)
429 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_Close
,
431 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_GrantUniversalAccess
,
432 OnMsgGrantUniversalAccess
)
434 IPC_MESSAGE_HANDLER(PpapiMsg_PPBURLLoader_UpdateProgress
,
436 IPC_MESSAGE_HANDLER(PpapiMsg_PPBURLLoader_ReadResponseBody_Ack
,
437 OnMsgReadResponseBodyAck
)
438 IPC_MESSAGE_HANDLER(PpapiMsg_PPBURLLoader_CallbackComplete
,
439 OnMsgCallbackComplete
)
440 IPC_MESSAGE_UNHANDLED(handled
= false)
441 IPC_END_MESSAGE_MAP()
442 // TODO(brettw) handle bad messages!
446 void PPB_URLLoader_Proxy::PrepareURLLoaderForSendingToPlugin(
447 PP_Resource resource
) {
448 // So the plugin can query load status, we need to register our status
449 // callback before sending any URLLoader to the plugin.
450 EnterResourceNoLock
<PPB_URLLoader_API
> enter(resource
, false);
451 if (enter
.succeeded())
452 enter
.object()->SetStatusCallback(&UpdateResourceLoadStatus
);
454 NOTREACHED(); // Only called internally, resource should be valid.
457 void PPB_URLLoader_Proxy::OnMsgCreate(PP_Instance instance
,
458 HostResource
* result
) {
459 thunk::EnterResourceCreation
enter(instance
);
460 if (enter
.succeeded()) {
461 result
->SetHostResource(instance
,
462 enter
.functions()->CreateURLLoader(instance
));
463 PrepareURLLoaderForSendingToPlugin(result
->host_resource());
467 void PPB_URLLoader_Proxy::OnMsgOpen(const HostResource
& loader
,
468 const PPB_URLRequestInfo_Data
& data
) {
469 EnterHostFromHostResourceForceCallback
<PPB_URLLoader_API
> enter(
470 loader
, callback_factory_
, &PPB_URLLoader_Proxy::OnCallback
, loader
);
471 thunk::EnterResourceCreation
enter_creation(loader
.instance());
472 if (enter
.failed() || enter_creation
.failed())
475 ScopedPPResource
request_resource(
476 ScopedPPResource::PassRef(),
477 enter_creation
.functions()->CreateURLRequestInfo(loader
.instance(),
479 enter
.SetResult(enter
.object()->Open(request_resource
, enter
.callback()));
480 // TODO(brettw) bug 73236 register for the status callbacks.
483 void PPB_URLLoader_Proxy::OnMsgFollowRedirect(
484 const HostResource
& loader
) {
485 EnterHostFromHostResourceForceCallback
<PPB_URLLoader_API
> enter(
486 loader
, callback_factory_
, &PPB_URLLoader_Proxy::OnCallback
, loader
);
487 if (enter
.succeeded())
488 enter
.SetResult(enter
.object()->FollowRedirect(enter
.callback()));
491 void PPB_URLLoader_Proxy::OnMsgGetResponseInfo(const HostResource
& loader
,
492 HostResource
* result
) {
493 EnterHostFromHostResource
<PPB_URLLoader_API
> enter(loader
);
494 if (enter
.succeeded()) {
495 result
->SetHostResource(loader
.instance(),
496 enter
.object()->GetResponseInfo());
500 void PPB_URLLoader_Proxy::OnMsgReadResponseBody(
501 const HostResource
& loader
,
502 int32_t bytes_to_read
) {
503 // The plugin could be sending us malicious messages, don't accept negative
505 if (bytes_to_read
< 0) {
506 // TODO(brettw) kill plugin.
510 // Read more than requested if there are bytes available for synchronous
511 // reading. This prevents us from getting too far behind due to IPC message
512 // latency. Any extra data will get buffered in the plugin.
513 int32_t synchronously_available_bytes
=
514 static_cast<HostDispatcher
*>(dispatcher())->ppb_proxy()->
515 GetURLLoaderBufferedBytes(loader
.host_resource());
516 if (bytes_to_read
< kMaxReadBufferSize
) {
517 // Grow the amount to read so we read ahead synchronously, if possible.
519 std::max(bytes_to_read
,
520 std::min(synchronously_available_bytes
, kMaxReadBufferSize
));
523 // This heap object will get deleted by the callback handler.
524 // TODO(brettw) this will be leaked if the plugin closes the resource!
525 // (Also including the plugin unloading and having the resource implicitly
526 // destroyed. Depending on the cleanup ordering, we may not need the weak
528 ReadCallbackInfo
* info
= new ReadCallbackInfo
;
529 info
->resource
= loader
;
530 // TODO(brettw) have a way to check for out-of-memory.
531 info
->read_buffer
.resize(bytes_to_read
);
533 EnterHostFromHostResourceForceCallback
<PPB_URLLoader_API
> enter(
534 loader
, callback_factory_
, &PPB_URLLoader_Proxy::OnReadCallback
, info
);
535 if (enter
.succeeded()) {
536 enter
.SetResult(enter
.object()->ReadResponseBody(
537 const_cast<char*>(info
->read_buffer
.c_str()),
538 bytes_to_read
, enter
.callback()));
542 void PPB_URLLoader_Proxy::OnMsgFinishStreamingToFile(
543 const HostResource
& loader
) {
544 EnterHostFromHostResourceForceCallback
<PPB_URLLoader_API
> enter(
545 loader
, callback_factory_
, &PPB_URLLoader_Proxy::OnCallback
, loader
);
546 if (enter
.succeeded())
547 enter
.SetResult(enter
.object()->FinishStreamingToFile(enter
.callback()));;
550 void PPB_URLLoader_Proxy::OnMsgClose(const HostResource
& loader
) {
551 EnterHostFromHostResource
<PPB_URLLoader_API
> enter(loader
);
552 if (enter
.succeeded())
553 enter
.object()->Close();
556 void PPB_URLLoader_Proxy::OnMsgGrantUniversalAccess(
557 const HostResource
& loader
) {
558 EnterHostFromHostResource
<PPB_URLLoader_API
> enter(loader
);
559 if (enter
.succeeded())
560 enter
.object()->GrantUniversalAccess();
563 // Called in the Plugin.
564 void PPB_URLLoader_Proxy::OnMsgUpdateProgress(
565 const PPBURLLoader_UpdateProgress_Params
& params
) {
566 EnterPluginFromHostResource
<PPB_URLLoader_API
> enter(params
.resource
);
567 if (enter
.succeeded())
568 static_cast<URLLoader
*>(enter
.object())->UpdateProgress(params
);
571 // Called in the Plugin.
572 void PPB_URLLoader_Proxy::OnMsgReadResponseBodyAck(
573 const HostResource
& host_resource
,
575 const std::string
& data
) {
576 EnterPluginFromHostResource
<PPB_URLLoader_API
> enter(host_resource
);
577 if (enter
.succeeded())
578 static_cast<URLLoader
*>(enter
.object())->ReadResponseBodyAck(result
, data
);
581 // Called in the plugin.
582 void PPB_URLLoader_Proxy::OnMsgCallbackComplete(
583 const HostResource
& host_resource
,
585 EnterPluginFromHostResource
<PPB_URLLoader_API
> enter(host_resource
);
586 if (enter
.succeeded())
587 static_cast<URLLoader
*>(enter
.object())->CallbackComplete(result
);
590 void PPB_URLLoader_Proxy::OnReadCallback(int32_t result
,
591 ReadCallbackInfo
* info
) {
592 int32_t bytes_read
= 0;
594 bytes_read
= result
; // Positive results indicate bytes read.
595 info
->read_buffer
.resize(bytes_read
);
597 dispatcher()->Send(new PpapiMsg_PPBURLLoader_ReadResponseBody_Ack(
598 API_ID_PPB_URL_LOADER
, info
->resource
, result
, info
->read_buffer
));
603 void PPB_URLLoader_Proxy::OnCallback(int32_t result
,
604 const HostResource
& resource
) {
605 dispatcher()->Send(new PpapiMsg_PPBURLLoader_CallbackComplete(
606 API_ID_PPB_URL_LOADER
, resource
, result
));