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/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 
     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       !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 
    184 int32_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 
    206 void 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 
    218 void URLLoaderResource::GrantUniversalAccess() {
    219   Post(RENDERER, PpapiHostMsg_URLLoader_GrantUniversalAccess());
    220 }
    221 
    222 void URLLoaderResource::RegisterStatusCallback(
    223     PP_URLLoaderTrusted_StatusCallback callback) {
    224   status_callback_ = callback;
    225 }
    226 
    227 void 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 
    249 void URLLoaderResource::OnPluginMsgReceivedResponse(
    250     const ResourceMessageReplyParams& params,
    251     const URLResponseInfoData& data) {
    252   SaveResponseInfo(data);
    253   RunCallback(PP_OK);
    254 }
    255 
    256 void 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 
    290 void 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 
    305 void 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 
    322 void URLLoaderResource::SetDefersLoading(bool defers_loading) {
    323   Post(RENDERER, PpapiHostMsg_URLLoader_SetDeferLoading(defers_loading));
    324 }
    325 
    326 int32_t URLLoaderResource::ValidateCallback(
    327     scoped_refptr<TrackedCallback> callback) {
    328   DCHECK(callback.get());
    329   if (TrackedCallback::IsPending(pending_callback_))
    330     return PP_ERROR_INPROGRESS;
    331   return PP_OK;
    332 }
    333 
    334 void URLLoaderResource::RegisterCallback(
    335     scoped_refptr<TrackedCallback> callback) {
    336   DCHECK(!TrackedCallback::IsPending(pending_callback_));
    337   pending_callback_ = callback;
    338 }
    339 
    340 void 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 
    357 void 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 
    369 size_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