Home | History | Annotate | Download | only in proxy
      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   IPC_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   IPC_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