url_loader_resource.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2013 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/url_loader_resource.h" 6 7#include "base/logging.h" 8#include "ppapi/c/pp_completion_callback.h" 9#include "ppapi/c/pp_errors.h" 10#include "ppapi/c/ppb_url_loader.h" 11#include "ppapi/proxy/dispatch_reply_message.h" 12#include "ppapi/proxy/ppapi_messages.h" 13#include "ppapi/proxy/ppb_file_ref_proxy.h" 14#include "ppapi/proxy/url_request_info_resource.h" 15#include "ppapi/proxy/url_response_info_resource.h" 16#include "ppapi/shared_impl/ppapi_globals.h" 17#include "ppapi/shared_impl/url_response_info_data.h" 18#include "ppapi/thunk/enter.h" 19#include "ppapi/thunk/resource_creation_api.h" 20 21using ppapi::thunk::EnterResourceNoLock; 22using ppapi::thunk::PPB_URLLoader_API; 23using ppapi::thunk::PPB_URLRequestInfo_API; 24 25#ifdef _MSC_VER 26// Do not warn about use of std::copy with raw pointers. 27#pragma warning(disable : 4996) 28#endif 29 30namespace ppapi { 31namespace proxy { 32 33URLLoaderResource::URLLoaderResource(Connection connection, 34 PP_Instance instance) 35 : PluginResource(connection, instance), 36 mode_(MODE_WAITING_TO_OPEN), 37 status_callback_(NULL), 38 bytes_sent_(0), 39 total_bytes_to_be_sent_(-1), 40 bytes_received_(0), 41 total_bytes_to_be_received_(-1), 42 user_buffer_(NULL), 43 user_buffer_size_(0), 44 done_status_(PP_OK_COMPLETIONPENDING), 45 is_streaming_to_file_(false), 46 is_asynchronous_load_suspended_(false) { 47 SendCreate(RENDERER, PpapiHostMsg_URLLoader_Create()); 48} 49 50URLLoaderResource::URLLoaderResource(Connection connection, 51 PP_Instance instance, 52 int pending_main_document_loader_id, 53 const ppapi::URLResponseInfoData& data) 54 : PluginResource(connection, instance), 55 mode_(MODE_OPENING), 56 status_callback_(NULL), 57 bytes_sent_(0), 58 total_bytes_to_be_sent_(-1), 59 bytes_received_(0), 60 total_bytes_to_be_received_(-1), 61 user_buffer_(NULL), 62 user_buffer_size_(0), 63 done_status_(PP_OK_COMPLETIONPENDING), 64 is_streaming_to_file_(false), 65 is_asynchronous_load_suspended_(false) { 66 AttachToPendingHost(RENDERER, pending_main_document_loader_id); 67 SaveResponseInfo(data); 68} 69 70URLLoaderResource::~URLLoaderResource() { 71} 72 73PPB_URLLoader_API* URLLoaderResource::AsPPB_URLLoader_API() { 74 return this; 75} 76 77int32_t URLLoaderResource::Open(PP_Resource request_id, 78 scoped_refptr<TrackedCallback> callback) { 79 EnterResourceNoLock<PPB_URLRequestInfo_API> enter_request(request_id, true); 80 if (enter_request.failed()) { 81 Log(PP_LOGLEVEL_ERROR, 82 "PPB_URLLoader.Open: invalid request resource ID. (Hint to C++ wrapper" 83 " users: use the ResourceRequest constructor that takes an instance or" 84 " else the request will be null.)"); 85 return PP_ERROR_BADARGUMENT; 86 } 87 return Open(enter_request.object()->GetData(), 0, callback); 88} 89 90int32_t URLLoaderResource::Open( 91 const ::ppapi::URLRequestInfoData& request_data, 92 int requestor_pid, 93 scoped_refptr<TrackedCallback> callback) { 94 int32_t rv = ValidateCallback(callback); 95 if (rv != PP_OK) 96 return rv; 97 if (mode_ != MODE_WAITING_TO_OPEN) 98 return PP_ERROR_INPROGRESS; 99 100 request_data_ = request_data; 101 102 mode_ = MODE_OPENING; 103 is_asynchronous_load_suspended_ = false; 104 105 RegisterCallback(callback); 106 Post(RENDERER, PpapiHostMsg_URLLoader_Open(request_data)); 107 return PP_OK_COMPLETIONPENDING; 108} 109 110int32_t URLLoaderResource::FollowRedirect( 111 scoped_refptr<TrackedCallback> callback) { 112 int32_t rv = ValidateCallback(callback); 113 if (rv != PP_OK) 114 return rv; 115 if (mode_ != MODE_OPENING) 116 return PP_ERROR_INPROGRESS; 117 118 SetDefersLoading(false); // Allow the redirect to continue. 119 RegisterCallback(callback); 120 return PP_OK_COMPLETIONPENDING; 121} 122 123PP_Bool URLLoaderResource::GetUploadProgress(int64_t* bytes_sent, 124 int64_t* total_bytes_to_be_sent) { 125 if (!request_data_.record_upload_progress) { 126 *bytes_sent = 0; 127 *total_bytes_to_be_sent = 0; 128 return PP_FALSE; 129 } 130 *bytes_sent = bytes_sent_; 131 *total_bytes_to_be_sent = total_bytes_to_be_sent_; 132 return PP_TRUE; 133} 134 135PP_Bool URLLoaderResource::GetDownloadProgress( 136 int64_t* bytes_received, 137 int64_t* total_bytes_to_be_received) { 138 if (!request_data_.record_download_progress) { 139 *bytes_received = 0; 140 *total_bytes_to_be_received = 0; 141 return PP_FALSE; 142 } 143 *bytes_received = bytes_received_; 144 *total_bytes_to_be_received = total_bytes_to_be_received_; 145 return PP_TRUE; 146} 147 148PP_Resource URLLoaderResource::GetResponseInfo() { 149 if (response_info_.get()) 150 return response_info_->GetReference(); 151 return 0; 152} 153 154int32_t URLLoaderResource::ReadResponseBody( 155 void* buffer, 156 int32_t bytes_to_read, 157 scoped_refptr<TrackedCallback> callback) { 158 int32_t rv = ValidateCallback(callback); 159 if (rv != PP_OK) 160 return rv; 161 if (!response_info_.get() || 162 !response_info_->data().body_as_file_ref.resource.is_null()) 163 return PP_ERROR_FAILED; 164 if (bytes_to_read <= 0 || !buffer) 165 return PP_ERROR_BADARGUMENT; 166 167 user_buffer_ = static_cast<char*>(buffer); 168 user_buffer_size_ = bytes_to_read; 169 170 if (!buffer_.empty()) 171 return FillUserBuffer(); 172 173 // We may have already reached EOF. 174 if (done_status_ != PP_OK_COMPLETIONPENDING) { 175 user_buffer_ = NULL; 176 user_buffer_size_ = 0; 177 return done_status_; 178 } 179 180 RegisterCallback(callback); 181 return PP_OK_COMPLETIONPENDING; 182} 183 184int32_t URLLoaderResource::FinishStreamingToFile( 185 scoped_refptr<TrackedCallback> callback) { 186 int32_t rv = ValidateCallback(callback); 187 if (rv != PP_OK) 188 return rv; 189 if (!response_info_.get() || 190 response_info_->data().body_as_file_ref.resource.is_null()) 191 return PP_ERROR_FAILED; 192 193 // We may have already reached EOF. 194 if (done_status_ != PP_OK_COMPLETIONPENDING) 195 return done_status_; 196 197 is_streaming_to_file_ = true; 198 if (is_asynchronous_load_suspended_) 199 SetDefersLoading(false); 200 201 // Wait for didFinishLoading / didFail. 202 RegisterCallback(callback); 203 return PP_OK_COMPLETIONPENDING; 204} 205 206void URLLoaderResource::Close() { 207 mode_ = MODE_LOAD_COMPLETE; 208 done_status_ = PP_ERROR_ABORTED; 209 210 Post(RENDERER, PpapiHostMsg_URLLoader_Close()); 211 212 // Abort the callbacks, the plugin doesn't want to be called back after this. 213 // TODO(brettw) this should fix bug 69457, mark it fixed. ============ 214 if (TrackedCallback::IsPending(pending_callback_)) 215 pending_callback_->PostAbort(); 216} 217 218void URLLoaderResource::GrantUniversalAccess() { 219 Post(RENDERER, PpapiHostMsg_URLLoader_GrantUniversalAccess()); 220} 221 222void URLLoaderResource::RegisterStatusCallback( 223 PP_URLLoaderTrusted_StatusCallback callback) { 224 status_callback_ = callback; 225} 226 227void URLLoaderResource::OnReplyReceived( 228 const ResourceMessageReplyParams& params, 229 const IPC::Message& msg) { 230 IPC_BEGIN_MESSAGE_MAP(URLLoaderResource, msg) 231 case PpapiPluginMsg_URLLoader_SendData::ID: 232 // Special message, manually dispatch since we don't want the automatic 233 // unpickling. 234 OnPluginMsgSendData(params, msg); 235 break; 236 237 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 238 PpapiPluginMsg_URLLoader_ReceivedResponse, 239 OnPluginMsgReceivedResponse) 240 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 241 PpapiPluginMsg_URLLoader_FinishedLoading, 242 OnPluginMsgFinishedLoading) 243 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 244 PpapiPluginMsg_URLLoader_UpdateProgress, 245 OnPluginMsgUpdateProgress) 246 IPC_END_MESSAGE_MAP() 247} 248 249void URLLoaderResource::OnPluginMsgReceivedResponse( 250 const ResourceMessageReplyParams& params, 251 const URLResponseInfoData& data) { 252 SaveResponseInfo(data); 253 RunCallback(PP_OK); 254} 255 256void URLLoaderResource::OnPluginMsgSendData( 257 const ResourceMessageReplyParams& params, 258 const IPC::Message& message) { 259 PickleIterator iter(message); 260 const char* data; 261 int data_length; 262 if (!iter.ReadData(&data, &data_length)) { 263 NOTREACHED() << "Expecting data"; 264 return; 265 } 266 267 mode_ = MODE_STREAMING_DATA; 268 buffer_.insert(buffer_.end(), data, data + data_length); 269 270 // To avoid letting the network stack download an entire stream all at once, 271 // defer loading when we have enough buffer. 272 // Check for this before we run the callback, even though that could move 273 // data out of the buffer. Doing anything after the callback is unsafe. 274 DCHECK(request_data_.prefetch_buffer_lower_threshold < 275 request_data_.prefetch_buffer_upper_threshold); 276 if (!is_streaming_to_file_ && 277 !is_asynchronous_load_suspended_ && 278 (buffer_.size() >= static_cast<size_t>( 279 request_data_.prefetch_buffer_upper_threshold))) { 280 DVLOG(1) << "Suspending async load - buffer size: " << buffer_.size(); 281 SetDefersLoading(true); 282 } 283 284 if (user_buffer_) 285 RunCallback(FillUserBuffer()); 286 else 287 DCHECK(!TrackedCallback::IsPending(pending_callback_)); 288} 289 290void URLLoaderResource::OnPluginMsgFinishedLoading( 291 const ResourceMessageReplyParams& params, 292 int32_t result) { 293 mode_ = MODE_LOAD_COMPLETE; 294 done_status_ = result; 295 user_buffer_ = NULL; 296 user_buffer_size_ = 0; 297 298 // If the client hasn't called any function that takes a callback since 299 // the initial call to Open, or called ReadResponseBody and got a 300 // synchronous return, then the callback will be NULL. 301 if (TrackedCallback::IsPending(pending_callback_)) 302 RunCallback(done_status_); 303} 304 305void URLLoaderResource::OnPluginMsgUpdateProgress( 306 const ResourceMessageReplyParams& params, 307 int64_t bytes_sent, 308 int64_t total_bytes_to_be_sent, 309 int64_t bytes_received, 310 int64_t total_bytes_to_be_received) { 311 bytes_sent_ = bytes_sent; 312 total_bytes_to_be_sent_ = total_bytes_to_be_sent; 313 bytes_received_ = bytes_received; 314 total_bytes_to_be_received_ = total_bytes_to_be_received; 315 316 if (status_callback_) 317 status_callback_(pp_instance(), pp_resource(), 318 bytes_sent_, total_bytes_to_be_sent_, 319 bytes_received_, total_bytes_to_be_received_); 320} 321 322void URLLoaderResource::SetDefersLoading(bool defers_loading) { 323 Post(RENDERER, PpapiHostMsg_URLLoader_SetDeferLoading(defers_loading)); 324} 325 326int32_t URLLoaderResource::ValidateCallback( 327 scoped_refptr<TrackedCallback> callback) { 328 DCHECK(callback); 329 if (TrackedCallback::IsPending(pending_callback_)) 330 return PP_ERROR_INPROGRESS; 331 return PP_OK; 332} 333 334void URLLoaderResource::RegisterCallback( 335 scoped_refptr<TrackedCallback> callback) { 336 DCHECK(!TrackedCallback::IsPending(pending_callback_)); 337 pending_callback_ = callback; 338} 339 340void URLLoaderResource::RunCallback(int32_t result) { 341 // This may be null when this is a main document loader. 342 if (!pending_callback_.get()) 343 return; 344 345 // If |user_buffer_| was set as part of registering a callback, the paths 346 // which trigger that callack must have cleared it since the callback is now 347 // free to delete it. 348 DCHECK(!user_buffer_); 349 350 // As a second line of defense, clear the |user_buffer_| in case the 351 // callbacks get called in an unexpected order. 352 user_buffer_ = NULL; 353 user_buffer_size_ = 0; 354 pending_callback_->Run(result); 355} 356 357void URLLoaderResource::SaveResponseInfo(const URLResponseInfoData& data) { 358 // Create a proxy resource for the the file ref host resource if needed. 359 PP_Resource body_as_file_ref = 0; 360 if (!data.body_as_file_ref.resource.is_null()) { 361 thunk::EnterResourceCreationNoLock enter(pp_instance()); 362 body_as_file_ref = 363 enter.functions()->CreateFileRef(data.body_as_file_ref); 364 } 365 response_info_ = new URLResponseInfoResource( 366 connection(), pp_instance(), data, body_as_file_ref); 367} 368 369size_t URLLoaderResource::FillUserBuffer() { 370 DCHECK(user_buffer_); 371 DCHECK(user_buffer_size_); 372 373 size_t bytes_to_copy = std::min(buffer_.size(), user_buffer_size_); 374 std::copy(buffer_.begin(), buffer_.begin() + bytes_to_copy, user_buffer_); 375 buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_to_copy); 376 377 // If the buffer is getting too empty, resume asynchronous loading. 378 if (is_asynchronous_load_suspended_ && 379 buffer_.size() <= static_cast<size_t>( 380 request_data_.prefetch_buffer_lower_threshold)) { 381 DVLOG(1) << "Resuming async load - buffer size: " << buffer_.size(); 382 SetDefersLoading(false); 383 } 384 385 // Reset for next time. 386 user_buffer_ = NULL; 387 user_buffer_size_ = 0; 388 return bytes_to_copy; 389} 390 391} // namespace proxy 392} // namespace ppapi