chrome/browser/extensions: Remove use of MessageLoopProxy and deprecated MessageLoop...
[chromium-blink-merge.git] / ppapi / proxy / url_loader_resource.cc
blob4ac345a2b80686797fd177c4425b32a83336de34
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.
5 #include "ppapi/proxy/url_loader_resource.h"
7 #include "base/logging.h"
8 #include "base/numerics/safe_conversions.h"
9 #include "ppapi/c/pp_completion_callback.h"
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/c/ppb_url_loader.h"
12 #include "ppapi/proxy/dispatch_reply_message.h"
13 #include "ppapi/proxy/file_ref_resource.h"
14 #include "ppapi/proxy/ppapi_messages.h"
15 #include "ppapi/proxy/url_request_info_resource.h"
16 #include "ppapi/proxy/url_response_info_resource.h"
17 #include "ppapi/shared_impl/ppapi_globals.h"
18 #include "ppapi/shared_impl/url_response_info_data.h"
19 #include "ppapi/thunk/enter.h"
20 #include "ppapi/thunk/resource_creation_api.h"
22 using ppapi::thunk::EnterResourceNoLock;
23 using ppapi::thunk::PPB_URLLoader_API;
24 using ppapi::thunk::PPB_URLRequestInfo_API;
26 #ifdef _MSC_VER
27 // Do not warn about use of std::copy with raw pointers.
28 #pragma warning(disable : 4996)
29 #endif
31 namespace ppapi {
32 namespace proxy {
34 URLLoaderResource::URLLoaderResource(Connection connection,
35 PP_Instance instance)
36 : PluginResource(connection, instance),
37 mode_(MODE_WAITING_TO_OPEN),
38 status_callback_(NULL),
39 bytes_sent_(0),
40 total_bytes_to_be_sent_(-1),
41 bytes_received_(0),
42 total_bytes_to_be_received_(-1),
43 user_buffer_(NULL),
44 user_buffer_size_(0),
45 done_status_(PP_OK_COMPLETIONPENDING),
46 is_streaming_to_file_(false),
47 is_asynchronous_load_suspended_(false) {
48 SendCreate(RENDERER, PpapiHostMsg_URLLoader_Create());
51 URLLoaderResource::URLLoaderResource(Connection connection,
52 PP_Instance instance,
53 int pending_main_document_loader_id,
54 const ppapi::URLResponseInfoData& data)
55 : PluginResource(connection, instance),
56 mode_(MODE_OPENING),
57 status_callback_(NULL),
58 bytes_sent_(0),
59 total_bytes_to_be_sent_(-1),
60 bytes_received_(0),
61 total_bytes_to_be_received_(-1),
62 user_buffer_(NULL),
63 user_buffer_size_(0),
64 done_status_(PP_OK_COMPLETIONPENDING),
65 is_streaming_to_file_(false),
66 is_asynchronous_load_suspended_(false) {
67 AttachToPendingHost(RENDERER, pending_main_document_loader_id);
68 SaveResponseInfo(data);
71 URLLoaderResource::~URLLoaderResource() {
74 PPB_URLLoader_API* URLLoaderResource::AsPPB_URLLoader_API() {
75 return this;
78 int32_t URLLoaderResource::Open(PP_Resource request_id,
79 scoped_refptr<TrackedCallback> callback) {
80 EnterResourceNoLock<PPB_URLRequestInfo_API> enter_request(request_id, true);
81 if (enter_request.failed()) {
82 Log(PP_LOGLEVEL_ERROR,
83 "PPB_URLLoader.Open: invalid request resource ID. (Hint to C++ wrapper"
84 " users: use the ResourceRequest constructor that takes an instance or"
85 " else the request will be null.)");
86 return PP_ERROR_BADARGUMENT;
88 return Open(enter_request.object()->GetData(), 0, callback);
91 int32_t URLLoaderResource::Open(
92 const ::ppapi::URLRequestInfoData& request_data,
93 int requestor_pid,
94 scoped_refptr<TrackedCallback> callback) {
95 int32_t rv = ValidateCallback(callback);
96 if (rv != PP_OK)
97 return rv;
98 if (mode_ != MODE_WAITING_TO_OPEN)
99 return PP_ERROR_INPROGRESS;
101 request_data_ = request_data;
103 mode_ = MODE_OPENING;
104 is_asynchronous_load_suspended_ = false;
106 RegisterCallback(callback);
107 Post(RENDERER, PpapiHostMsg_URLLoader_Open(request_data));
108 return PP_OK_COMPLETIONPENDING;
111 int32_t URLLoaderResource::FollowRedirect(
112 scoped_refptr<TrackedCallback> callback) {
113 int32_t rv = ValidateCallback(callback);
114 if (rv != PP_OK)
115 return rv;
116 if (mode_ != MODE_OPENING)
117 return PP_ERROR_INPROGRESS;
119 SetDefersLoading(false); // Allow the redirect to continue.
120 RegisterCallback(callback);
121 return PP_OK_COMPLETIONPENDING;
124 PP_Bool URLLoaderResource::GetUploadProgress(int64_t* bytes_sent,
125 int64_t* total_bytes_to_be_sent) {
126 if (!request_data_.record_upload_progress) {
127 *bytes_sent = 0;
128 *total_bytes_to_be_sent = 0;
129 return PP_FALSE;
131 *bytes_sent = bytes_sent_;
132 *total_bytes_to_be_sent = total_bytes_to_be_sent_;
133 return PP_TRUE;
136 PP_Bool URLLoaderResource::GetDownloadProgress(
137 int64_t* bytes_received,
138 int64_t* total_bytes_to_be_received) {
139 if (!request_data_.record_download_progress) {
140 *bytes_received = 0;
141 *total_bytes_to_be_received = 0;
142 return PP_FALSE;
144 *bytes_received = bytes_received_;
145 *total_bytes_to_be_received = total_bytes_to_be_received_;
146 return PP_TRUE;
149 PP_Resource URLLoaderResource::GetResponseInfo() {
150 if (response_info_.get())
151 return response_info_->GetReference();
152 return 0;
155 int32_t URLLoaderResource::ReadResponseBody(
156 void* buffer,
157 int32_t bytes_to_read,
158 scoped_refptr<TrackedCallback> callback) {
159 int32_t rv = ValidateCallback(callback);
160 if (rv != PP_OK)
161 return rv;
162 if (!response_info_.get())
163 return PP_ERROR_FAILED;
165 // Fail if we have a valid file ref.
166 // ReadResponseBody() is for reading to a user-provided buffer.
167 if (response_info_->data().body_as_file_ref.IsValid())
168 return PP_ERROR_FAILED;
170 if (bytes_to_read <= 0 || !buffer)
171 return PP_ERROR_BADARGUMENT;
173 user_buffer_ = static_cast<char*>(buffer);
174 user_buffer_size_ = bytes_to_read;
176 if (!buffer_.empty())
177 return FillUserBuffer();
179 // We may have already reached EOF.
180 if (done_status_ != PP_OK_COMPLETIONPENDING) {
181 user_buffer_ = NULL;
182 user_buffer_size_ = 0;
183 return done_status_;
186 RegisterCallback(callback);
187 return PP_OK_COMPLETIONPENDING;
190 int32_t URLLoaderResource::FinishStreamingToFile(
191 scoped_refptr<TrackedCallback> callback) {
192 int32_t rv = ValidateCallback(callback);
193 if (rv != PP_OK)
194 return rv;
195 if (!response_info_.get())
196 return PP_ERROR_FAILED;
198 // Fail if we do not have a valid file ref.
199 if (!response_info_->data().body_as_file_ref.IsValid())
200 return PP_ERROR_FAILED;
202 // We may have already reached EOF.
203 if (done_status_ != PP_OK_COMPLETIONPENDING)
204 return done_status_;
206 is_streaming_to_file_ = true;
207 if (is_asynchronous_load_suspended_)
208 SetDefersLoading(false);
210 // Wait for didFinishLoading / didFail.
211 RegisterCallback(callback);
212 return PP_OK_COMPLETIONPENDING;
215 void URLLoaderResource::Close() {
216 mode_ = MODE_LOAD_COMPLETE;
217 done_status_ = PP_ERROR_ABORTED;
219 Post(RENDERER, PpapiHostMsg_URLLoader_Close());
221 // Abort the callbacks, the plugin doesn't want to be called back after this.
222 // TODO(brettw) this should fix bug 69457, mark it fixed. ============
223 if (TrackedCallback::IsPending(pending_callback_))
224 pending_callback_->PostAbort();
227 void URLLoaderResource::GrantUniversalAccess() {
228 Post(RENDERER, PpapiHostMsg_URLLoader_GrantUniversalAccess());
231 void URLLoaderResource::RegisterStatusCallback(
232 PP_URLLoaderTrusted_StatusCallback callback) {
233 status_callback_ = callback;
236 void URLLoaderResource::OnReplyReceived(
237 const ResourceMessageReplyParams& params,
238 const IPC::Message& msg) {
239 PPAPI_BEGIN_MESSAGE_MAP(URLLoaderResource, msg)
240 case PpapiPluginMsg_URLLoader_SendData::ID:
241 // Special message, manually dispatch since we don't want the automatic
242 // unpickling.
243 OnPluginMsgSendData(params, msg);
244 break;
246 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
247 PpapiPluginMsg_URLLoader_ReceivedResponse,
248 OnPluginMsgReceivedResponse)
249 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
250 PpapiPluginMsg_URLLoader_FinishedLoading,
251 OnPluginMsgFinishedLoading)
252 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
253 PpapiPluginMsg_URLLoader_UpdateProgress,
254 OnPluginMsgUpdateProgress)
255 PPAPI_END_MESSAGE_MAP()
258 void URLLoaderResource::OnPluginMsgReceivedResponse(
259 const ResourceMessageReplyParams& params,
260 const URLResponseInfoData& data) {
261 SaveResponseInfo(data);
262 RunCallback(PP_OK);
265 void URLLoaderResource::OnPluginMsgSendData(
266 const ResourceMessageReplyParams& params,
267 const IPC::Message& message) {
268 base::PickleIterator iter(message);
269 const char* data;
270 int data_length;
271 if (!iter.ReadData(&data, &data_length)) {
272 NOTREACHED() << "Expecting data";
273 return;
276 mode_ = MODE_STREAMING_DATA;
277 buffer_.insert(buffer_.end(), data, data + data_length);
279 // To avoid letting the network stack download an entire stream all at once,
280 // defer loading when we have enough buffer.
281 // Check for this before we run the callback, even though that could move
282 // data out of the buffer. Doing anything after the callback is unsafe.
283 DCHECK(request_data_.prefetch_buffer_lower_threshold <
284 request_data_.prefetch_buffer_upper_threshold);
285 if (!is_streaming_to_file_ &&
286 !is_asynchronous_load_suspended_ &&
287 (buffer_.size() >= static_cast<size_t>(
288 request_data_.prefetch_buffer_upper_threshold))) {
289 DVLOG(1) << "Suspending async load - buffer size: " << buffer_.size();
290 SetDefersLoading(true);
293 if (user_buffer_)
294 RunCallback(FillUserBuffer());
295 else
296 DCHECK(!TrackedCallback::IsPending(pending_callback_));
299 void URLLoaderResource::OnPluginMsgFinishedLoading(
300 const ResourceMessageReplyParams& params,
301 int32_t result) {
302 mode_ = MODE_LOAD_COMPLETE;
303 done_status_ = result;
304 user_buffer_ = NULL;
305 user_buffer_size_ = 0;
307 // If the client hasn't called any function that takes a callback since
308 // the initial call to Open, or called ReadResponseBody and got a
309 // synchronous return, then the callback will be NULL.
310 if (TrackedCallback::IsPending(pending_callback_))
311 RunCallback(done_status_);
314 void URLLoaderResource::OnPluginMsgUpdateProgress(
315 const ResourceMessageReplyParams& params,
316 int64_t bytes_sent,
317 int64_t total_bytes_to_be_sent,
318 int64_t bytes_received,
319 int64_t total_bytes_to_be_received) {
320 bytes_sent_ = bytes_sent;
321 total_bytes_to_be_sent_ = total_bytes_to_be_sent;
322 bytes_received_ = bytes_received;
323 total_bytes_to_be_received_ = total_bytes_to_be_received;
325 if (status_callback_)
326 status_callback_(pp_instance(), pp_resource(),
327 bytes_sent_, total_bytes_to_be_sent_,
328 bytes_received_, total_bytes_to_be_received_);
331 void URLLoaderResource::SetDefersLoading(bool defers_loading) {
332 Post(RENDERER, PpapiHostMsg_URLLoader_SetDeferLoading(defers_loading));
335 int32_t URLLoaderResource::ValidateCallback(
336 scoped_refptr<TrackedCallback> callback) {
337 DCHECK(callback.get());
338 if (TrackedCallback::IsPending(pending_callback_))
339 return PP_ERROR_INPROGRESS;
340 return PP_OK;
343 void URLLoaderResource::RegisterCallback(
344 scoped_refptr<TrackedCallback> callback) {
345 DCHECK(!TrackedCallback::IsPending(pending_callback_));
346 pending_callback_ = callback;
349 void URLLoaderResource::RunCallback(int32_t result) {
350 // This may be null when this is a main document loader.
351 if (!pending_callback_.get())
352 return;
354 // If |user_buffer_| was set as part of registering a callback, the paths
355 // which trigger that callack must have cleared it since the callback is now
356 // free to delete it.
357 DCHECK(!user_buffer_);
359 // As a second line of defense, clear the |user_buffer_| in case the
360 // callbacks get called in an unexpected order.
361 user_buffer_ = NULL;
362 user_buffer_size_ = 0;
363 pending_callback_->Run(result);
366 void URLLoaderResource::SaveResponseInfo(const URLResponseInfoData& data) {
367 // Create a proxy resource for the the file ref host resource if needed.
368 PP_Resource body_as_file_ref = 0;
369 if (data.body_as_file_ref.IsValid()) {
370 body_as_file_ref = FileRefResource::CreateFileRef(connection(),
371 pp_instance(),
372 data.body_as_file_ref);
374 response_info_ = new URLResponseInfoResource(
375 connection(), pp_instance(), data, body_as_file_ref);
378 int32_t URLLoaderResource::FillUserBuffer() {
379 DCHECK(user_buffer_);
380 DCHECK(user_buffer_size_);
382 size_t bytes_to_copy = std::min(buffer_.size(), user_buffer_size_);
383 std::copy(buffer_.begin(), buffer_.begin() + bytes_to_copy, user_buffer_);
384 buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_to_copy);
386 // If the buffer is getting too empty, resume asynchronous loading.
387 if (is_asynchronous_load_suspended_ &&
388 buffer_.size() <= static_cast<size_t>(
389 request_data_.prefetch_buffer_lower_threshold)) {
390 DVLOG(1) << "Resuming async load - buffer size: " << buffer_.size();
391 SetDefersLoading(false);
394 // Reset for next time.
395 user_buffer_ = NULL;
396 user_buffer_size_ = 0;
397 return base::checked_cast<int32_t>(bytes_to_copy);
400 } // namespace proxy
401 } // namespace ppapi