blob: 73bdb2342f4e7362fd21f3fa7fd33590092fbf9b [file] [log] [blame]
[email protected]47cb253f2013-05-16 01:50:401// 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"
[email protected]c6420f082013-09-18 22:42:4112#include "ppapi/proxy/file_ref_resource.h"
[email protected]47cb253f2013-05-16 01:50:4013#include "ppapi/proxy/ppapi_messages.h"
[email protected]47cb253f2013-05-16 01:50:4014#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;
[email protected]c6420f082013-09-18 22:42:41161 if (!response_info_.get())
[email protected]47cb253f2013-05-16 01:50:40162 return PP_ERROR_FAILED;
[email protected]c6420f082013-09-18 22:42:41163
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
[email protected]47cb253f2013-05-16 01:50:40169 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
189int32_t URLLoaderResource::FinishStreamingToFile(
190 scoped_refptr<TrackedCallback> callback) {
191 int32_t rv = ValidateCallback(callback);
192 if (rv != PP_OK)
193 return rv;
[email protected]c6420f082013-09-18 22:42:41194 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())
[email protected]47cb253f2013-05-16 01:50:40199 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
214void 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
226void URLLoaderResource::GrantUniversalAccess() {
227 Post(RENDERER, PpapiHostMsg_URLLoader_GrantUniversalAccess());
228}
229
230void URLLoaderResource::RegisterStatusCallback(
231 PP_URLLoaderTrusted_StatusCallback callback) {
232 status_callback_ = callback;
233}
234
235void URLLoaderResource::OnReplyReceived(
236 const ResourceMessageReplyParams& params,
237 const IPC::Message& msg) {
[email protected]dade5f82014-05-13 21:59:21238 PPAPI_BEGIN_MESSAGE_MAP(URLLoaderResource, msg)
[email protected]47cb253f2013-05-16 01:50:40239 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)
[email protected]dade5f82014-05-13 21:59:21254 PPAPI_END_MESSAGE_MAP()
[email protected]47cb253f2013-05-16 01:50:40255}
256
257void URLLoaderResource::OnPluginMsgReceivedResponse(
258 const ResourceMessageReplyParams& params,
259 const URLResponseInfoData& data) {
260 SaveResponseInfo(data);
261 RunCallback(PP_OK);
262}
263
264void 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
298void 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
313void 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
330void URLLoaderResource::SetDefersLoading(bool defers_loading) {
331 Post(RENDERER, PpapiHostMsg_URLLoader_SetDeferLoading(defers_loading));
332}
333
334int32_t URLLoaderResource::ValidateCallback(
335 scoped_refptr<TrackedCallback> callback) {
[email protected]f0c86242013-06-02 21:25:43336 DCHECK(callback.get());
[email protected]47cb253f2013-05-16 01:50:40337 if (TrackedCallback::IsPending(pending_callback_))
338 return PP_ERROR_INPROGRESS;
339 return PP_OK;
340}
341
342void URLLoaderResource::RegisterCallback(
343 scoped_refptr<TrackedCallback> callback) {
344 DCHECK(!TrackedCallback::IsPending(pending_callback_));
345 pending_callback_ = callback;
346}
347
348void 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
365void 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;
[email protected]c6420f082013-09-18 22:42:41368 if (data.body_as_file_ref.IsValid()) {
369 body_as_file_ref = FileRefResource::CreateFileRef(connection(),
370 pp_instance(),
371 data.body_as_file_ref);
[email protected]47cb253f2013-05-16 01:50:40372 }
373 response_info_ = new URLResponseInfoResource(
374 connection(), pp_instance(), data, body_as_file_ref);
375}
376
377size_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
[email protected]d5b04b832013-09-12 19:48:15400} // namespace ppapi