• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
URLLoaderResource(Connection connection,PP_Instance instance)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 
URLLoaderResource(Connection connection,PP_Instance instance,int pending_main_document_loader_id,const ppapi::URLResponseInfoData & data)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 
~URLLoaderResource()70 URLLoaderResource::~URLLoaderResource() {
71 }
72 
AsPPB_URLLoader_API()73 PPB_URLLoader_API* URLLoaderResource::AsPPB_URLLoader_API() {
74   return this;
75 }
76 
Open(PP_Resource request_id,scoped_refptr<TrackedCallback> callback)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 
Open(const::ppapi::URLRequestInfoData & request_data,int requestor_pid,scoped_refptr<TrackedCallback> callback)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 
FollowRedirect(scoped_refptr<TrackedCallback> callback)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 
GetUploadProgress(int64_t * bytes_sent,int64_t * total_bytes_to_be_sent)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 
GetDownloadProgress(int64_t * bytes_received,int64_t * total_bytes_to_be_received)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 
GetResponseInfo()148 PP_Resource URLLoaderResource::GetResponseInfo() {
149   if (response_info_.get())
150     return response_info_->GetReference();
151   return 0;
152 }
153 
ReadResponseBody(void * buffer,int32_t bytes_to_read,scoped_refptr<TrackedCallback> callback)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 
FinishStreamingToFile(scoped_refptr<TrackedCallback> callback)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 
Close()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 
GrantUniversalAccess()226 void URLLoaderResource::GrantUniversalAccess() {
227   Post(RENDERER, PpapiHostMsg_URLLoader_GrantUniversalAccess());
228 }
229 
RegisterStatusCallback(PP_URLLoaderTrusted_StatusCallback callback)230 void URLLoaderResource::RegisterStatusCallback(
231     PP_URLLoaderTrusted_StatusCallback callback) {
232   status_callback_ = callback;
233 }
234 
OnReplyReceived(const ResourceMessageReplyParams & params,const IPC::Message & msg)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 
OnPluginMsgReceivedResponse(const ResourceMessageReplyParams & params,const URLResponseInfoData & data)257 void URLLoaderResource::OnPluginMsgReceivedResponse(
258     const ResourceMessageReplyParams& params,
259     const URLResponseInfoData& data) {
260   SaveResponseInfo(data);
261   RunCallback(PP_OK);
262 }
263 
OnPluginMsgSendData(const ResourceMessageReplyParams & params,const IPC::Message & message)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 
OnPluginMsgFinishedLoading(const ResourceMessageReplyParams & params,int32_t result)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 
OnPluginMsgUpdateProgress(const ResourceMessageReplyParams & params,int64_t bytes_sent,int64_t total_bytes_to_be_sent,int64_t bytes_received,int64_t total_bytes_to_be_received)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 
SetDefersLoading(bool defers_loading)330 void URLLoaderResource::SetDefersLoading(bool defers_loading) {
331   Post(RENDERER, PpapiHostMsg_URLLoader_SetDeferLoading(defers_loading));
332 }
333 
ValidateCallback(scoped_refptr<TrackedCallback> callback)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 
RegisterCallback(scoped_refptr<TrackedCallback> callback)342 void URLLoaderResource::RegisterCallback(
343     scoped_refptr<TrackedCallback> callback) {
344   DCHECK(!TrackedCallback::IsPending(pending_callback_));
345   pending_callback_ = callback;
346 }
347 
RunCallback(int32_t result)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 
SaveResponseInfo(const URLResponseInfoData & data)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 
FillUserBuffer()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