OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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. |
| 4 |
| 5 #include "ppapi/proxy/ppb_url_loader_proxy.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "build/build_config.h" |
| 11 #include "ppapi/c/pp_completion_callback.h" |
| 12 #include "ppapi/c/pp_errors.h" |
| 13 #include "ppapi/c/pp_resource.h" |
| 14 #include "ppapi/c/dev/ppb_url_loader_dev.h" |
| 15 #include "ppapi/proxy/host_dispatcher.h" |
| 16 #include "ppapi/proxy/plugin_dispatcher.h" |
| 17 #include "ppapi/proxy/plugin_resource.h" |
| 18 #include "ppapi/proxy/ppapi_messages.h" |
| 19 #include "ppapi/proxy/ppb_url_response_info_proxy.h" |
| 20 |
| 21 #if defined(OS_LINUX) |
| 22 #include <sys/shm.h> |
| 23 #endif |
| 24 |
| 25 namespace pp { |
| 26 namespace proxy { |
| 27 |
| 28 class URLLoader : public PluginResource { |
| 29 public: |
| 30 URLLoader(); |
| 31 virtual ~URLLoader(); |
| 32 |
| 33 // Resource overrides. |
| 34 virtual URLLoader* AsURLLoader() { return this; } |
| 35 |
| 36 // Initialized to -1. Will be set to nonnegative values by the UpdateProgress |
| 37 // message when the values are known. |
| 38 int64_t bytes_sent_; |
| 39 int64_t total_bytes_to_be_sent_; |
| 40 int64_t bytes_received_; |
| 41 int64_t total_bytes_to_be_received_; |
| 42 |
| 43 // When an asynchronous read is pending, this will contain the callback and |
| 44 // the buffer to put the data. |
| 45 PP_CompletionCallback current_read_callback_; |
| 46 char* current_read_buffer_; |
| 47 |
| 48 private: |
| 49 DISALLOW_COPY_AND_ASSIGN(URLLoader); |
| 50 }; |
| 51 |
| 52 URLLoader::URLLoader() |
| 53 : bytes_sent_(-1), |
| 54 total_bytes_to_be_sent_(-1), |
| 55 bytes_received_(-1), |
| 56 total_bytes_to_be_received_(-1), |
| 57 current_read_callback_(PP_MakeCompletionCallback(NULL, NULL)), |
| 58 current_read_buffer_(NULL) { |
| 59 } |
| 60 |
| 61 URLLoader::~URLLoader() { |
| 62 } |
| 63 |
| 64 namespace { |
| 65 |
| 66 // Plugin interface implmentation ---------------------------------------------- |
| 67 |
| 68 PP_Resource Create(PP_Instance instance_id) { |
| 69 PluginDispatcher* dispatcher = PluginDispatcher::Get(); |
| 70 PP_Resource result = 0; |
| 71 dispatcher->Send(new PpapiHostMsg_PPBURLLoader_Create( |
| 72 INTERFACE_ID_PPB_URL_LOADER, instance_id, &result)); |
| 73 if (result) |
| 74 PPB_URLLoader_Proxy::TrackPluginResource(result); |
| 75 return result; |
| 76 } |
| 77 |
| 78 bool IsURLLoader(PP_Resource resource) { |
| 79 URLLoader* object = PluginResource::GetAs<URLLoader>(resource); |
| 80 return !!object; |
| 81 } |
| 82 |
| 83 int32_t Open(PP_Resource loader_id, |
| 84 PP_Resource request_id, |
| 85 PP_CompletionCallback callback) { |
| 86 Dispatcher* dispatcher = PluginDispatcher::Get(); |
| 87 dispatcher->Send(new PpapiHostMsg_PPBURLLoader_Open( |
| 88 INTERFACE_ID_PPB_URL_LOADER, loader_id, request_id, |
| 89 dispatcher->callback_tracker().SendCallback(callback))); |
| 90 return PP_ERROR_WOULDBLOCK; |
| 91 } |
| 92 |
| 93 int32_t FollowRedirect(PP_Resource loader_id, |
| 94 PP_CompletionCallback callback) { |
| 95 Dispatcher* dispatcher = PluginDispatcher::Get(); |
| 96 dispatcher->Send(new PpapiHostMsg_PPBURLLoader_FollowRedirect( |
| 97 INTERFACE_ID_PPB_URL_LOADER, loader_id, |
| 98 dispatcher->callback_tracker().SendCallback(callback))); |
| 99 return PP_ERROR_WOULDBLOCK; |
| 100 } |
| 101 |
| 102 bool GetUploadProgress(PP_Resource loader_id, |
| 103 int64_t* bytes_sent, |
| 104 int64_t* total_bytes_to_be_sent) { |
| 105 URLLoader* object = PluginResource::GetAs<URLLoader>(loader_id); |
| 106 if (!object || object->bytes_sent_ == -1) { |
| 107 *bytes_sent = 0; |
| 108 *total_bytes_to_be_sent = 0; |
| 109 return false; |
| 110 } |
| 111 *bytes_sent = object->bytes_sent_; |
| 112 *total_bytes_to_be_sent = object->total_bytes_to_be_sent_; |
| 113 return true; |
| 114 } |
| 115 |
| 116 bool GetDownloadProgress(PP_Resource loader_id, |
| 117 int64_t* bytes_received, |
| 118 int64_t* total_bytes_to_be_received) { |
| 119 URLLoader* object = PluginResource::GetAs<URLLoader>(loader_id); |
| 120 if (!object || object->bytes_received_ == -1) { |
| 121 *bytes_received = 0; |
| 122 *total_bytes_to_be_received = 0; |
| 123 return false; |
| 124 } |
| 125 *bytes_received = object->bytes_received_; |
| 126 *total_bytes_to_be_received = object->total_bytes_to_be_received_; |
| 127 return true; |
| 128 } |
| 129 |
| 130 PP_Resource GetResponseInfo(PP_Resource loader_id) { |
| 131 // If we find that plugins are frequently requesting the response info, we |
| 132 // can improve performance by caching the PP_Resource in the URLLoader |
| 133 // object. This way we only have to do IPC for the first request. However, |
| 134 // it seems that most plugins will only call this once so there's no use |
| 135 // optimizing this case. |
| 136 |
| 137 PP_Resource result; |
| 138 PluginDispatcher* dispatcher = PluginDispatcher::Get(); |
| 139 dispatcher->Send(new PpapiHostMsg_PPBURLLoader_GetResponseInfo( |
| 140 INTERFACE_ID_PPB_URL_LOADER, loader_id, &result)); |
| 141 if (dispatcher->plugin_resource_tracker()->PreparePreviouslyTrackedResource( |
| 142 result)) |
| 143 return result; |
| 144 |
| 145 // Tell the response info to create a tracking object and add it to the |
| 146 // resource tracker. |
| 147 PPB_URLResponseInfo_Proxy::TrackPluginResource(result); |
| 148 return result; |
| 149 } |
| 150 |
| 151 int32_t ReadResponseBody(PP_Resource loader_id, |
| 152 char* buffer, |
| 153 int32_t bytes_to_read, |
| 154 PP_CompletionCallback callback) { |
| 155 URLLoader* object = PluginResource::GetAs<URLLoader>(loader_id); |
| 156 if (!object) |
| 157 return PP_ERROR_BADRESOURCE; |
| 158 |
| 159 if (!buffer) |
| 160 return PP_ERROR_BADARGUMENT; // Must specify an output buffer. |
| 161 if (object->current_read_callback_.func) |
| 162 return PP_ERROR_INPROGRESS; // Can only have one request pending. |
| 163 |
| 164 // Currently we don't support sync calls to read. We'll need to revisit |
| 165 // how this works when we allow blocking calls (from background threads). |
| 166 if (!callback.func) |
| 167 return PP_ERROR_BADARGUMENT; |
| 168 |
| 169 object->current_read_callback_ = callback; |
| 170 object->current_read_buffer_ = buffer; |
| 171 |
| 172 PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBURLLoader_ReadResponseBody( |
| 173 INTERFACE_ID_PPB_URL_LOADER, loader_id, bytes_to_read)); |
| 174 return PP_ERROR_WOULDBLOCK; |
| 175 } |
| 176 |
| 177 int32_t FinishStreamingToFile(PP_Resource loader_id, |
| 178 PP_CompletionCallback callback) { |
| 179 Dispatcher* dispatcher = PluginDispatcher::Get(); |
| 180 dispatcher->Send(new PpapiHostMsg_PPBURLLoader_FinishStreamingToFile( |
| 181 INTERFACE_ID_PPB_URL_LOADER, loader_id, |
| 182 dispatcher->callback_tracker().SendCallback(callback))); |
| 183 return PP_ERROR_WOULDBLOCK; |
| 184 } |
| 185 |
| 186 void Close(PP_Resource loader_id) { |
| 187 PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBURLLoader_Close( |
| 188 INTERFACE_ID_PPB_URL_LOADER, loader_id)); |
| 189 } |
| 190 |
| 191 const PPB_URLLoader_Dev ppb_urlloader = { |
| 192 &Create, |
| 193 &IsURLLoader, |
| 194 &Open, |
| 195 &FollowRedirect, |
| 196 &GetUploadProgress, |
| 197 &GetDownloadProgress, |
| 198 &GetResponseInfo, |
| 199 &ReadResponseBody, |
| 200 &FinishStreamingToFile, |
| 201 &Close |
| 202 }; |
| 203 |
| 204 // Renderer status updates ----------------------------------------------------- |
| 205 |
| 206 // Called in the renderer when the byte counts have changed. We send a message |
| 207 // to the plugin to synchronize its counts so it can respond to status polls |
| 208 // from the plugin. |
| 209 void UpdateResourceLoadStatus(PP_Instance pp_instance, |
| 210 PP_Resource pp_resource, |
| 211 int64 bytes_sent, |
| 212 int64 total_bytes_to_be_sent, |
| 213 int64 bytes_received, |
| 214 int64 total_bytes_to_be_received) { |
| 215 Dispatcher* dispatcher = HostDispatcher::GetForInstance(pp_instance); |
| 216 dispatcher->Send(new PpapiMsg_PPBURLLoader_UpdateProgress( |
| 217 INTERFACE_ID_PPB_URL_LOADER, pp_resource, |
| 218 bytes_sent, total_bytes_to_be_sent, |
| 219 bytes_received, total_bytes_to_be_received)); |
| 220 } |
| 221 |
| 222 // Data associated with callbacks for ReadResponseBody. |
| 223 struct ReadCallbackInfo { |
| 224 base::WeakPtr<PPB_URLLoader_Proxy> loader; |
| 225 PP_Resource pp_resource; |
| 226 std::string read_buffer; |
| 227 }; |
| 228 |
| 229 // Callback for renderer calls to ReadResponseBody. This function will forward |
| 230 // the result to the plugin and clean up the callback info. |
| 231 void ReadCallbackHandler(void* user_data, int32_t result) { |
| 232 scoped_ptr<ReadCallbackInfo> info(static_cast<ReadCallbackInfo*>(user_data)); |
| 233 if (!info->loader) |
| 234 return; |
| 235 |
| 236 int32_t bytes_read = 0; |
| 237 if (result > 0) |
| 238 bytes_read = result; // Positive results indicate bytes read. |
| 239 info->read_buffer.resize(bytes_read); |
| 240 |
| 241 info->loader->dispatcher()->Send( |
| 242 new PpapiMsg_PPBURLLoader_ReadResponseBody_Ack( |
| 243 INTERFACE_ID_PPB_URL_LOADER, info->pp_resource, |
| 244 result, info->read_buffer)); |
| 245 } |
| 246 |
| 247 } // namespace |
| 248 |
| 249 // PPB_URLLoader_Proxy --------------------------------------------------------- |
| 250 |
| 251 PPB_URLLoader_Proxy::PPB_URLLoader_Proxy(Dispatcher* dispatcher, |
| 252 const void* target_interface) |
| 253 : InterfaceProxy(dispatcher, target_interface) { |
| 254 } |
| 255 |
| 256 PPB_URLLoader_Proxy::~PPB_URLLoader_Proxy() { |
| 257 } |
| 258 |
| 259 // static |
| 260 void PPB_URLLoader_Proxy::TrackPluginResource(PP_Resource url_loader_resource) { |
| 261 linked_ptr<URLLoader> object(new URLLoader); |
| 262 PluginDispatcher::Get()->plugin_resource_tracker()->AddResource( |
| 263 url_loader_resource, object); |
| 264 } |
| 265 |
| 266 const void* PPB_URLLoader_Proxy::GetSourceInterface() const { |
| 267 return &ppb_urlloader; |
| 268 } |
| 269 |
| 270 InterfaceID PPB_URLLoader_Proxy::GetInterfaceId() const { |
| 271 return INTERFACE_ID_PPB_URL_LOADER; |
| 272 } |
| 273 |
| 274 void PPB_URLLoader_Proxy::OnMessageReceived(const IPC::Message& msg) { |
| 275 IPC_BEGIN_MESSAGE_MAP(PPB_URLLoader_Proxy, msg) |
| 276 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_Create, |
| 277 OnMsgCreate) |
| 278 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_Open, |
| 279 OnMsgOpen) |
| 280 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_FollowRedirect, |
| 281 OnMsgFollowRedirect) |
| 282 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_GetResponseInfo, |
| 283 OnMsgGetResponseInfo) |
| 284 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_ReadResponseBody, |
| 285 OnMsgReadResponseBody) |
| 286 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_FinishStreamingToFile, |
| 287 OnMsgFinishStreamingToFile) |
| 288 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLLoader_Close, |
| 289 OnMsgClose) |
| 290 |
| 291 IPC_MESSAGE_HANDLER(PpapiMsg_PPBURLLoader_UpdateProgress, |
| 292 OnMsgUpdateProgress) |
| 293 IPC_MESSAGE_HANDLER(PpapiMsg_PPBURLLoader_ReadResponseBody_Ack, |
| 294 OnMsgReadResponseBodyAck) |
| 295 IPC_END_MESSAGE_MAP() |
| 296 // TODO(brettw) handle bad messages! |
| 297 } |
| 298 |
| 299 void PPB_URLLoader_Proxy::OnMsgCreate(PP_Instance instance, |
| 300 PP_Resource* result) { |
| 301 *result = ppb_url_loader_target()->Create(instance); |
| 302 } |
| 303 |
| 304 void PPB_URLLoader_Proxy::OnMsgOpen(PP_Resource loader, |
| 305 PP_Resource request_info, |
| 306 uint32_t serialized_callback) { |
| 307 PP_CompletionCallback callback = ReceiveCallback(serialized_callback); |
| 308 int32_t result = ppb_url_loader_target()->Open( |
| 309 loader, request_info, callback); |
| 310 if (result != PP_ERROR_WOULDBLOCK) |
| 311 PP_RunCompletionCallback(&callback, result); |
| 312 } |
| 313 |
| 314 void PPB_URLLoader_Proxy::OnMsgFollowRedirect( |
| 315 PP_Resource loader, |
| 316 uint32_t serialized_callback) { |
| 317 PP_CompletionCallback callback = ReceiveCallback(serialized_callback); |
| 318 int32_t result = ppb_url_loader_target()->FollowRedirect( |
| 319 loader, callback); |
| 320 if (result != PP_ERROR_WOULDBLOCK) |
| 321 PP_RunCompletionCallback(&callback, result); |
| 322 } |
| 323 |
| 324 void PPB_URLLoader_Proxy::OnMsgGetResponseInfo(PP_Resource loader, |
| 325 PP_Resource* result) { |
| 326 *result = ppb_url_loader_target()->GetResponseInfo(loader); |
| 327 } |
| 328 |
| 329 void PPB_URLLoader_Proxy::OnMsgReadResponseBody( |
| 330 PP_Resource loader, |
| 331 int32_t bytes_to_read) { |
| 332 // The plugin could be sending us malicious messages, don't accept negative |
| 333 // sizes. |
| 334 if (bytes_to_read < 0) { |
| 335 // TODO(brettw) kill plugin. |
| 336 bytes_to_read = 0; |
| 337 } |
| 338 |
| 339 // This heap object will get deleted by the callback handler. |
| 340 // TODO(brettw) this will be leaked if the plugin closes the resource! |
| 341 // (Also including the plugin unloading and having the resource implicitly |
| 342 // destroyed. Depending on the cleanup ordering, we may not need the weak |
| 343 // pointer here.) |
| 344 ReadCallbackInfo* info = new ReadCallbackInfo; |
| 345 info->loader = AsWeakPtr(); |
| 346 info->pp_resource = loader; |
| 347 info->read_buffer.resize(bytes_to_read); |
| 348 |
| 349 int32_t result = ppb_url_loader_target()->ReadResponseBody( |
| 350 loader, const_cast<char*>(info->read_buffer.c_str()), bytes_to_read, |
| 351 PP_MakeCompletionCallback(&ReadCallbackHandler, info)); |
| 352 if (result != PP_ERROR_WOULDBLOCK) { |
| 353 // Send error (or perhaps success for synchronous reads) back to plugin. |
| 354 // The callback function is already set up to do this and also delete the |
| 355 // callback info. |
| 356 ReadCallbackHandler(info, result); |
| 357 } |
| 358 } |
| 359 |
| 360 void PPB_URLLoader_Proxy::OnMsgFinishStreamingToFile( |
| 361 PP_Resource loader, |
| 362 uint32_t serialized_callback) { |
| 363 PP_CompletionCallback callback = ReceiveCallback(serialized_callback); |
| 364 int32_t result = ppb_url_loader_target()->FinishStreamingToFile( |
| 365 loader, callback); |
| 366 if (result != PP_ERROR_WOULDBLOCK) |
| 367 PP_RunCompletionCallback(&callback, result); |
| 368 } |
| 369 |
| 370 void PPB_URLLoader_Proxy::OnMsgClose(PP_Resource loader) { |
| 371 ppb_url_loader_target()->Close(loader); |
| 372 } |
| 373 |
| 374 void PPB_URLLoader_Proxy::OnMsgUpdateProgress( |
| 375 PP_Resource resource, |
| 376 int64_t bytes_sent, |
| 377 int64_t total_bytes_to_be_sent, |
| 378 int64_t bytes_received, |
| 379 int64_t total_bytes_to_be_received) { |
| 380 URLLoader* object = PluginResource::GetAs<URLLoader>(resource); |
| 381 if (!object) { |
| 382 NOTREACHED(); |
| 383 return; |
| 384 } |
| 385 |
| 386 object->bytes_sent_ = bytes_sent; |
| 387 object->total_bytes_to_be_sent_ = total_bytes_to_be_sent; |
| 388 object->bytes_received_ = bytes_received; |
| 389 object->total_bytes_to_be_received_ = total_bytes_to_be_received; |
| 390 } |
| 391 |
| 392 void PPB_URLLoader_Proxy::OnMsgReadResponseBodyAck(PP_Resource pp_resource, |
| 393 int32 result, |
| 394 const std::string& data) { |
| 395 URLLoader* object = PluginResource::GetAs<URLLoader>(pp_resource); |
| 396 if (!object) { |
| 397 NOTREACHED(); |
| 398 return; |
| 399 } |
| 400 |
| 401 if (!object->current_read_callback_.func || !object->current_read_buffer_) { |
| 402 NOTREACHED(); |
| 403 return; |
| 404 } |
| 405 |
| 406 // In the error case, the string will be empty, so we can always just copy |
| 407 // out of it before issuing the callback. |
| 408 memcpy(object->current_read_buffer_, data.c_str(), data.length()); |
| 409 |
| 410 // The plugin should be able to make a new request from their callback, so |
| 411 // we have to clear our copy first. |
| 412 PP_CompletionCallback temp_callback = object->current_read_callback_; |
| 413 object->current_read_callback_ = PP_BlockUntilComplete(); |
| 414 object->current_read_buffer_ = NULL; |
| 415 PP_RunCompletionCallback(&temp_callback, result); |
| 416 } |
| 417 |
| 418 } // namespace proxy |
| 419 } // namespace pp |
OLD | NEW |