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/file_ref_resource.h" 13 #include "ppapi/proxy/ppapi_messages.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 21 using ppapi::thunk::EnterResourceNoLock; 22 using ppapi::thunk::PPB_URLLoader_API; 23 using 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 30 namespace ppapi { 31 namespace proxy { 32 33 URLLoaderResource::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 50 URLLoaderResource::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 70 URLLoaderResource::~URLLoaderResource() { 71 } 72 73 PPB_URLLoader_API* URLLoaderResource::AsPPB_URLLoader_API() { 74 return this; 75 } 76 77 int32_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 90 int32_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 110 int32_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 123 PP_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 135 PP_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 148 PP_Resource URLLoaderResource::GetResponseInfo() { 149 if (response_info_.get()) 150 return response_info_->GetReference(); 151 return 0; 152 } 153 154 int32_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 return PP_ERROR_FAILED; 163 164 // Fail if we have a valid file ref. 165 // ReadResponseBody() is for reading to a user-provided buffer. 166 if (response_info_->data().body_as_file_ref.IsValid()) 167 return PP_ERROR_FAILED; 168 169 if (bytes_to_read <= 0 || !buffer) 170 return PP_ERROR_BADARGUMENT; 171 172 user_buffer_ = static_cast<char*>(buffer); 173 user_buffer_size_ = bytes_to_read; 174 175 if (!buffer_.empty()) 176 return FillUserBuffer(); 177 178 // We may have already reached EOF. 179 if (done_status_ != PP_OK_COMPLETIONPENDING) { 180 user_buffer_ = NULL; 181 user_buffer_size_ = 0; 182 return done_status_; 183 } 184 185 RegisterCallback(callback); 186 return PP_OK_COMPLETIONPENDING; 187 } 188 189 int32_t URLLoaderResource::FinishStreamingToFile( 190 scoped_refptr<TrackedCallback> callback) { 191 int32_t rv = ValidateCallback(callback); 192 if (rv != PP_OK) 193 return rv; 194 if (!response_info_.get()) 195 return PP_ERROR_FAILED; 196 197 // Fail if we do not have a valid file ref. 198 if (!response_info_->data().body_as_file_ref.IsValid()) 199 return PP_ERROR_FAILED; 200 201 // We may have already reached EOF. 202 if (done_status_ != PP_OK_COMPLETIONPENDING) 203 return done_status_; 204 205 is_streaming_to_file_ = true; 206 if (is_asynchronous_load_suspended_) 207 SetDefersLoading(false); 208 209 // Wait for didFinishLoading / didFail. 210 RegisterCallback(callback); 211 return PP_OK_COMPLETIONPENDING; 212 } 213 214 void URLLoaderResource::Close() { 215 mode_ = MODE_LOAD_COMPLETE; 216 done_status_ = PP_ERROR_ABORTED; 217 218 Post(RENDERER, PpapiHostMsg_URLLoader_Close()); 219 220 // Abort the callbacks, the plugin doesn't want to be called back after this. 221 // TODO(brettw) this should fix bug 69457, mark it fixed. ============ 222 if (TrackedCallback::IsPending(pending_callback_)) 223 pending_callback_->PostAbort(); 224 } 225 226 void URLLoaderResource::GrantUniversalAccess() { 227 Post(RENDERER, PpapiHostMsg_URLLoader_GrantUniversalAccess()); 228 } 229 230 void URLLoaderResource::RegisterStatusCallback( 231 PP_URLLoaderTrusted_StatusCallback callback) { 232 status_callback_ = callback; 233 } 234 235 void URLLoaderResource::OnReplyReceived( 236 const ResourceMessageReplyParams& params, 237 const IPC::Message& msg) { 238 PPAPI_BEGIN_MESSAGE_MAP(URLLoaderResource, msg) 239 case PpapiPluginMsg_URLLoader_SendData::ID: 240 // Special message, manually dispatch since we don't want the automatic 241 // unpickling. 242 OnPluginMsgSendData(params, msg); 243 break; 244 245 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 246 PpapiPluginMsg_URLLoader_ReceivedResponse, 247 OnPluginMsgReceivedResponse) 248 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 249 PpapiPluginMsg_URLLoader_FinishedLoading, 250 OnPluginMsgFinishedLoading) 251 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 252 PpapiPluginMsg_URLLoader_UpdateProgress, 253 OnPluginMsgUpdateProgress) 254 PPAPI_END_MESSAGE_MAP() 255 } 256 257 void URLLoaderResource::OnPluginMsgReceivedResponse( 258 const ResourceMessageReplyParams& params, 259 const URLResponseInfoData& data) { 260 SaveResponseInfo(data); 261 RunCallback(PP_OK); 262 } 263 264 void URLLoaderResource::OnPluginMsgSendData( 265 const ResourceMessageReplyParams& params, 266 const IPC::Message& message) { 267 PickleIterator iter(message); 268 const char* data; 269 int data_length; 270 if (!iter.ReadData(&data, &data_length)) { 271 NOTREACHED() << "Expecting data"; 272 return; 273 } 274 275 mode_ = MODE_STREAMING_DATA; 276 buffer_.insert(buffer_.end(), data, data + data_length); 277 278 // To avoid letting the network stack download an entire stream all at once, 279 // defer loading when we have enough buffer. 280 // Check for this before we run the callback, even though that could move 281 // data out of the buffer. Doing anything after the callback is unsafe. 282 DCHECK(request_data_.prefetch_buffer_lower_threshold < 283 request_data_.prefetch_buffer_upper_threshold); 284 if (!is_streaming_to_file_ && 285 !is_asynchronous_load_suspended_ && 286 (buffer_.size() >= static_cast<size_t>( 287 request_data_.prefetch_buffer_upper_threshold))) { 288 DVLOG(1) << "Suspending async load - buffer size: " << buffer_.size(); 289 SetDefersLoading(true); 290 } 291 292 if (user_buffer_) 293 RunCallback(FillUserBuffer()); 294 else 295 DCHECK(!TrackedCallback::IsPending(pending_callback_)); 296 } 297 298 void URLLoaderResource::OnPluginMsgFinishedLoading( 299 const ResourceMessageReplyParams& params, 300 int32_t result) { 301 mode_ = MODE_LOAD_COMPLETE; 302 done_status_ = result; 303 user_buffer_ = NULL; 304 user_buffer_size_ = 0; 305 306 // If the client hasn't called any function that takes a callback since 307 // the initial call to Open, or called ReadResponseBody and got a 308 // synchronous return, then the callback will be NULL. 309 if (TrackedCallback::IsPending(pending_callback_)) 310 RunCallback(done_status_); 311 } 312 313 void URLLoaderResource::OnPluginMsgUpdateProgress( 314 const ResourceMessageReplyParams& params, 315 int64_t bytes_sent, 316 int64_t total_bytes_to_be_sent, 317 int64_t bytes_received, 318 int64_t total_bytes_to_be_received) { 319 bytes_sent_ = bytes_sent; 320 total_bytes_to_be_sent_ = total_bytes_to_be_sent; 321 bytes_received_ = bytes_received; 322 total_bytes_to_be_received_ = total_bytes_to_be_received; 323 324 if (status_callback_) 325 status_callback_(pp_instance(), pp_resource(), 326 bytes_sent_, total_bytes_to_be_sent_, 327 bytes_received_, total_bytes_to_be_received_); 328 } 329 330 void URLLoaderResource::SetDefersLoading(bool defers_loading) { 331 Post(RENDERER, PpapiHostMsg_URLLoader_SetDeferLoading(defers_loading)); 332 } 333 334 int32_t URLLoaderResource::ValidateCallback( 335 scoped_refptr<TrackedCallback> callback) { 336 DCHECK(callback.get()); 337 if (TrackedCallback::IsPending(pending_callback_)) 338 return PP_ERROR_INPROGRESS; 339 return PP_OK; 340 } 341 342 void URLLoaderResource::RegisterCallback( 343 scoped_refptr<TrackedCallback> callback) { 344 DCHECK(!TrackedCallback::IsPending(pending_callback_)); 345 pending_callback_ = callback; 346 } 347 348 void URLLoaderResource::RunCallback(int32_t result) { 349 // This may be null when this is a main document loader. 350 if (!pending_callback_.get()) 351 return; 352 353 // If |user_buffer_| was set as part of registering a callback, the paths 354 // which trigger that callack must have cleared it since the callback is now 355 // free to delete it. 356 DCHECK(!user_buffer_); 357 358 // As a second line of defense, clear the |user_buffer_| in case the 359 // callbacks get called in an unexpected order. 360 user_buffer_ = NULL; 361 user_buffer_size_ = 0; 362 pending_callback_->Run(result); 363 } 364 365 void URLLoaderResource::SaveResponseInfo(const URLResponseInfoData& data) { 366 // Create a proxy resource for the the file ref host resource if needed. 367 PP_Resource body_as_file_ref = 0; 368 if (data.body_as_file_ref.IsValid()) { 369 body_as_file_ref = FileRefResource::CreateFileRef(connection(), 370 pp_instance(), 371 data.body_as_file_ref); 372 } 373 response_info_ = new URLResponseInfoResource( 374 connection(), pp_instance(), data, body_as_file_ref); 375 } 376 377 size_t URLLoaderResource::FillUserBuffer() { 378 DCHECK(user_buffer_); 379 DCHECK(user_buffer_size_); 380 381 size_t bytes_to_copy = std::min(buffer_.size(), user_buffer_size_); 382 std::copy(buffer_.begin(), buffer_.begin() + bytes_to_copy, user_buffer_); 383 buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_to_copy); 384 385 // If the buffer is getting too empty, resume asynchronous loading. 386 if (is_asynchronous_load_suspended_ && 387 buffer_.size() <= static_cast<size_t>( 388 request_data_.prefetch_buffer_lower_threshold)) { 389 DVLOG(1) << "Resuming async load - buffer size: " << buffer_.size(); 390 SetDefersLoading(false); 391 } 392 393 // Reset for next time. 394 user_buffer_ = NULL; 395 user_buffer_size_ = 0; 396 return bytes_to_copy; 397 } 398 399 } // namespace proxy 400 } // namespace ppapi 401