Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / webkit / plugins / ppapi / ppb_url_loader_impl.cc
blobebe08ef7685a9937260f7efb62b9d34c1a0dba8f
1 // Copyright (c) 2012 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 "webkit/plugins/ppapi/ppb_url_loader_impl.h"
7 #include "base/logging.h"
8 #include "net/base/net_errors.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/c/trusted/ppb_url_loader_trusted.h"
13 #include "ppapi/shared_impl/ppapi_globals.h"
14 #include "ppapi/shared_impl/url_response_info_data.h"
15 #include "ppapi/thunk/enter.h"
16 #include "ppapi/thunk/ppb_url_request_info_api.h"
17 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLError.h"
18 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLLoader.h"
19 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLRequest.h"
20 #include "third_party/WebKit/Source/Platform/chromium/public/WebURLResponse.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoaderOptions.h"
28 #include "webkit/appcache/web_application_cache_host_impl.h"
29 #include "webkit/plugins/ppapi/common.h"
30 #include "webkit/plugins/ppapi/plugin_module.h"
31 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
32 #include "webkit/plugins/ppapi/resource_helper.h"
33 #include "webkit/plugins/ppapi/url_request_info_util.h"
34 #include "webkit/plugins/ppapi/url_response_info_util.h"
36 using appcache::WebApplicationCacheHostImpl;
37 using ppapi::Resource;
38 using ppapi::thunk::EnterResourceNoLock;
39 using ppapi::thunk::PPB_URLLoader_API;
40 using ppapi::thunk::PPB_URLRequestInfo_API;
41 using ppapi::TrackedCallback;
42 using WebKit::WebFrame;
43 using WebKit::WebString;
44 using WebKit::WebURL;
45 using WebKit::WebURLError;
46 using WebKit::WebURLLoader;
47 using WebKit::WebURLLoaderOptions;
48 using WebKit::WebURLRequest;
49 using WebKit::WebURLResponse;
51 #ifdef _MSC_VER
52 // Do not warn about use of std::copy with raw pointers.
53 #pragma warning(disable : 4996)
54 #endif
56 namespace webkit {
57 namespace ppapi {
59 namespace {
61 WebFrame* GetFrameForResource(const Resource* resource) {
62 PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(resource);
63 if (!plugin_instance)
64 return NULL;
65 return plugin_instance->container()->element().document().frame();
68 } // namespace
70 PPB_URLLoader_Impl::PPB_URLLoader_Impl(PP_Instance instance,
71 bool main_document_loader)
72 : Resource(::ppapi::OBJECT_IS_IMPL, instance),
73 main_document_loader_(main_document_loader),
74 pending_callback_(),
75 bytes_sent_(0),
76 total_bytes_to_be_sent_(-1),
77 bytes_received_(0),
78 total_bytes_to_be_received_(-1),
79 user_buffer_(NULL),
80 user_buffer_size_(0),
81 done_status_(PP_OK_COMPLETIONPENDING),
82 is_streaming_to_file_(false),
83 is_asynchronous_load_suspended_(false),
84 has_universal_access_(false),
85 status_callback_(NULL) {
88 PPB_URLLoader_Impl::~PPB_URLLoader_Impl() {
89 // There is a path whereby the destructor for the loader_ member can
90 // invoke InstanceWasDeleted() upon this PPB_URLLoader_Impl, thereby
91 // re-entering the scoped_ptr destructor with the same scoped_ptr object
92 // via loader_.reset(). Be sure that loader_ is first NULL then destroy
93 // the scoped_ptr. See http://crbug.com/159429.
94 scoped_ptr<WebKit::WebURLLoader> for_destruction_only(loader_.release());
97 PPB_URLLoader_API* PPB_URLLoader_Impl::AsPPB_URLLoader_API() {
98 return this;
101 void PPB_URLLoader_Impl::InstanceWasDeleted() {
102 loader_.reset();
105 int32_t PPB_URLLoader_Impl::Open(PP_Resource request_id,
106 scoped_refptr<TrackedCallback> callback) {
107 EnterResourceNoLock<PPB_URLRequestInfo_API> enter_request(request_id, true);
108 if (enter_request.failed()) {
109 Log(PP_LOGLEVEL_ERROR,
110 "PPB_URLLoader.Open: invalid request resource ID. (Hint to C++ wrapper"
111 " users: use the ResourceRequest constructor that takes an instance or"
112 " else the request will be null.)");
113 return PP_ERROR_BADARGUMENT;
115 return Open(enter_request.object()->GetData(), 0, callback);
118 int32_t PPB_URLLoader_Impl::Open(
119 const ::ppapi::URLRequestInfoData& request_data,
120 int requestor_pid,
121 scoped_refptr<TrackedCallback> callback) {
122 // Main document loads are already open, so don't allow people to open them
123 // again.
124 if (main_document_loader_)
125 return PP_ERROR_INPROGRESS;
127 int32_t rv = ValidateCallback(callback);
128 if (rv != PP_OK)
129 return rv;
131 // Create a copy of the request data since CreateWebURLRequest will populate
132 // the file refs.
133 ::ppapi::URLRequestInfoData filled_in_request_data = request_data;
135 if (URLRequestRequiresUniversalAccess(filled_in_request_data) &&
136 !has_universal_access_) {
137 Log(PP_LOGLEVEL_ERROR, "PPB_URLLoader.Open: The URL you're requesting is "
138 " on a different security origin than your plugin. To request "
139 " cross-origin resources, see "
140 " PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS.");
141 return PP_ERROR_NOACCESS;
144 if (loader_.get())
145 return PP_ERROR_INPROGRESS;
147 WebFrame* frame = GetFrameForResource(this);
148 if (!frame)
149 return PP_ERROR_FAILED;
150 WebURLRequest web_request;
151 if (!CreateWebURLRequest(&filled_in_request_data, frame, &web_request))
152 return PP_ERROR_FAILED;
153 web_request.setRequestorProcessID(requestor_pid);
155 // Save a copy of the request info so the plugin can continue to use and
156 // change it while we're doing the request without affecting us. We must do
157 // this after CreateWebURLRequest since that fills out the file refs.
158 request_data_ = filled_in_request_data;
160 WebURLLoaderOptions options;
161 if (has_universal_access_) {
162 options.allowCredentials = true;
163 options.crossOriginRequestPolicy =
164 WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
165 } else {
166 // All other HTTP requests are untrusted.
167 options.untrustedHTTP = true;
168 if (request_data_.allow_cross_origin_requests) {
169 // Allow cross-origin requests with access control. The request specifies
170 // if credentials are to be sent.
171 options.allowCredentials = request_data_.allow_credentials;
172 options.crossOriginRequestPolicy =
173 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
174 } else {
175 // Same-origin requests can always send credentials.
176 options.allowCredentials = true;
180 is_asynchronous_load_suspended_ = false;
181 loader_.reset(frame->createAssociatedURLLoader(options));
182 if (!loader_.get())
183 return PP_ERROR_FAILED;
185 loader_->loadAsynchronously(web_request, this);
187 // Notify completion when we receive a redirect or response headers.
188 RegisterCallback(callback);
189 return PP_OK_COMPLETIONPENDING;
192 int32_t PPB_URLLoader_Impl::FollowRedirect(
193 scoped_refptr<TrackedCallback> callback) {
194 int32_t rv = ValidateCallback(callback);
195 if (rv != PP_OK)
196 return rv;
198 SetDefersLoading(false); // Allow the redirect to continue.
199 RegisterCallback(callback);
200 return PP_OK_COMPLETIONPENDING;
203 PP_Bool PPB_URLLoader_Impl::GetUploadProgress(int64_t* bytes_sent,
204 int64_t* total_bytes_to_be_sent) {
205 if (!RecordUploadProgress()) {
206 *bytes_sent = 0;
207 *total_bytes_to_be_sent = 0;
208 return PP_FALSE;
210 *bytes_sent = bytes_sent_;
211 *total_bytes_to_be_sent = total_bytes_to_be_sent_;
212 return PP_TRUE;
215 PP_Bool PPB_URLLoader_Impl::GetDownloadProgress(
216 int64_t* bytes_received,
217 int64_t* total_bytes_to_be_received) {
218 if (!RecordDownloadProgress()) {
219 *bytes_received = 0;
220 *total_bytes_to_be_received = 0;
221 return PP_FALSE;
223 *bytes_received = bytes_received_;
224 *total_bytes_to_be_received = total_bytes_to_be_received_;
225 return PP_TRUE;
228 PP_Resource PPB_URLLoader_Impl::GetResponseInfo() {
229 ::ppapi::thunk::EnterResourceCreationNoLock enter(pp_instance());
230 if (enter.failed() || !response_info_.get())
231 return 0;
233 // Since we're the "host" the process-local resource for the file ref is
234 // the same as the host resource. We pass a ref to the file ref.
235 if (!response_info_->body_as_file_ref.resource.is_null()) {
236 ::ppapi::PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(
237 response_info_->body_as_file_ref.resource.host_resource());
239 return enter.functions()->CreateURLResponseInfo(
240 pp_instance(),
241 *response_info_,
242 response_info_->body_as_file_ref.resource.host_resource());
245 int32_t PPB_URLLoader_Impl::ReadResponseBody(
246 void* buffer,
247 int32_t bytes_to_read,
248 scoped_refptr<TrackedCallback> callback) {
249 int32_t rv = ValidateCallback(callback);
250 if (rv != PP_OK)
251 return rv;
252 if (!response_info_.get() ||
253 !response_info_->body_as_file_ref.resource.is_null())
254 return PP_ERROR_FAILED;
255 if (bytes_to_read <= 0 || !buffer)
256 return PP_ERROR_BADARGUMENT;
258 user_buffer_ = static_cast<char*>(buffer);
259 user_buffer_size_ = bytes_to_read;
261 if (!buffer_.empty())
262 return FillUserBuffer();
264 // We may have already reached EOF.
265 if (done_status_ != PP_OK_COMPLETIONPENDING) {
266 user_buffer_ = NULL;
267 user_buffer_size_ = 0;
268 return done_status_;
271 RegisterCallback(callback);
272 return PP_OK_COMPLETIONPENDING;
275 int32_t PPB_URLLoader_Impl::FinishStreamingToFile(
276 scoped_refptr<TrackedCallback> callback) {
277 int32_t rv = ValidateCallback(callback);
278 if (rv != PP_OK)
279 return rv;
280 if (!response_info_.get() ||
281 response_info_->body_as_file_ref.resource.is_null())
282 return PP_ERROR_FAILED;
284 // We may have already reached EOF.
285 if (done_status_ != PP_OK_COMPLETIONPENDING)
286 return done_status_;
288 is_streaming_to_file_ = true;
289 if (is_asynchronous_load_suspended_)
290 SetDefersLoading(false);
292 // Wait for didFinishLoading / didFail.
293 RegisterCallback(callback);
294 return PP_OK_COMPLETIONPENDING;
297 void PPB_URLLoader_Impl::Close() {
298 if (loader_.get())
299 loader_->cancel();
300 else if (main_document_loader_)
301 GetFrameForResource(this)->stopLoading();
303 // We must not access the buffer provided by the caller from this point on.
304 user_buffer_ = NULL;
305 user_buffer_size_ = 0;
306 if (TrackedCallback::IsPending(pending_callback_))
307 pending_callback_->PostAbort();
310 void PPB_URLLoader_Impl::GrantUniversalAccess() {
311 has_universal_access_ = true;
314 void PPB_URLLoader_Impl::SetStatusCallback(
315 PP_URLLoaderTrusted_StatusCallback cb) {
316 status_callback_ = cb;
319 bool PPB_URLLoader_Impl::GetResponseInfoData(
320 ::ppapi::URLResponseInfoData* data) {
321 if (!response_info_.get())
322 return false;
324 *data = *response_info_;
326 // We transfer one plugin reference to the FileRef to the caller.
327 if (!response_info_->body_as_file_ref.resource.is_null()) {
328 ::ppapi::PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(
329 response_info_->body_as_file_ref.resource.host_resource());
331 return true;
334 void PPB_URLLoader_Impl::willSendRequest(
335 WebURLLoader* loader,
336 WebURLRequest& new_request,
337 const WebURLResponse& redirect_response) {
338 if (!request_data_.follow_redirects) {
339 SaveResponse(redirect_response);
340 SetDefersLoading(true);
341 RunCallback(PP_OK);
345 void PPB_URLLoader_Impl::didSendData(
346 WebURLLoader* loader,
347 unsigned long long bytes_sent,
348 unsigned long long total_bytes_to_be_sent) {
349 // TODO(darin): Bounds check input?
350 bytes_sent_ = static_cast<int64_t>(bytes_sent);
351 total_bytes_to_be_sent_ = static_cast<int64_t>(total_bytes_to_be_sent);
352 UpdateStatus();
355 void PPB_URLLoader_Impl::didReceiveResponse(WebURLLoader* loader,
356 const WebURLResponse& response) {
357 SaveResponse(response);
359 // Sets -1 if the content length is unknown.
360 total_bytes_to_be_received_ = response.expectedContentLength();
361 UpdateStatus();
363 RunCallback(PP_OK);
366 void PPB_URLLoader_Impl::didDownloadData(WebURLLoader* loader,
367 int data_length) {
368 bytes_received_ += data_length;
369 UpdateStatus();
372 void PPB_URLLoader_Impl::didReceiveData(WebURLLoader* loader,
373 const char* data,
374 int data_length,
375 int encoded_data_length) {
376 // Note that |loader| will be NULL for document loads.
377 bytes_received_ += data_length;
378 UpdateStatus();
380 buffer_.insert(buffer_.end(), data, data + data_length);
382 // To avoid letting the network stack download an entire stream all at once,
383 // defer loading when we have enough buffer.
384 // Check for this before we run the callback, even though that could move
385 // data out of the buffer. Doing anything after the callback is unsafe.
386 DCHECK(request_data_.prefetch_buffer_lower_threshold <
387 request_data_.prefetch_buffer_upper_threshold);
388 if (!is_streaming_to_file_ &&
389 !is_asynchronous_load_suspended_ &&
390 (buffer_.size() >= static_cast<size_t>(
391 request_data_.prefetch_buffer_upper_threshold))) {
392 DVLOG(1) << "Suspending async load - buffer size: " << buffer_.size();
393 SetDefersLoading(true);
396 if (user_buffer_) {
397 RunCallback(FillUserBuffer());
398 } else {
399 DCHECK(!TrackedCallback::IsPending(pending_callback_));
403 void PPB_URLLoader_Impl::didFinishLoading(WebURLLoader* loader,
404 double finish_time) {
405 FinishLoading(PP_OK);
408 void PPB_URLLoader_Impl::didFail(WebURLLoader* loader,
409 const WebURLError& error) {
410 int32_t pp_error = PP_ERROR_FAILED;
411 if (error.domain.equals(WebString::fromUTF8(net::kErrorDomain))) {
412 // TODO(bbudge): Extend pp_errors.h to cover interesting network errors
413 // from the net error domain.
414 switch (error.reason) {
415 case net::ERR_ABORTED:
416 pp_error = PP_ERROR_ABORTED;
417 break;
418 case net::ERR_ACCESS_DENIED:
419 case net::ERR_NETWORK_ACCESS_DENIED:
420 pp_error = PP_ERROR_NOACCESS;
421 break;
423 } else {
424 // It's a WebKit error.
425 pp_error = PP_ERROR_NOACCESS;
428 FinishLoading(pp_error);
431 void PPB_URLLoader_Impl::SetDefersLoading(bool defers_loading) {
432 if (loader_.get()) {
433 loader_->setDefersLoading(defers_loading);
434 is_asynchronous_load_suspended_ = defers_loading;
437 // TODO(brettw) bug 96770: We need a way to set the defers loading flag on
438 // main document loads (when the loader_ is null).
441 void PPB_URLLoader_Impl::FinishLoading(int32_t done_status) {
442 done_status_ = done_status;
443 user_buffer_ = NULL;
444 user_buffer_size_ = 0;
445 // If the client hasn't called any function that takes a callback since
446 // the initial call to Open, or called ReadResponseBody and got a
447 // synchronous return, then the callback will be NULL.
448 if (TrackedCallback::IsPending(pending_callback_))
449 RunCallback(done_status_);
452 int32_t PPB_URLLoader_Impl::ValidateCallback(
453 scoped_refptr<TrackedCallback> callback) {
454 DCHECK(callback);
456 if (TrackedCallback::IsPending(pending_callback_))
457 return PP_ERROR_INPROGRESS;
459 return PP_OK;
462 void PPB_URLLoader_Impl::RegisterCallback(
463 scoped_refptr<TrackedCallback> callback) {
464 DCHECK(!TrackedCallback::IsPending(pending_callback_));
466 PluginModule* plugin_module = ResourceHelper::GetPluginModule(this);
467 if (!plugin_module)
468 return;
470 pending_callback_ = callback;
473 void PPB_URLLoader_Impl::RunCallback(int32_t result) {
474 // This may be null only when this is a main document loader.
475 if (!pending_callback_.get()) {
476 CHECK(main_document_loader_);
477 return;
480 // If |user_buffer_| was set as part of registering a callback, the paths
481 // which trigger that callack must have cleared it since the callback is now
482 // free to delete it.
483 DCHECK(!user_buffer_);
485 // As a second line of defense, clear the |user_buffer_| in case the
486 // callbacks get called in an unexpected order.
487 user_buffer_ = NULL;
488 user_buffer_size_ = 0;
489 pending_callback_->Run(result);
492 size_t PPB_URLLoader_Impl::FillUserBuffer() {
493 DCHECK(user_buffer_);
494 DCHECK(user_buffer_size_);
496 size_t bytes_to_copy = std::min(buffer_.size(), user_buffer_size_);
497 std::copy(buffer_.begin(), buffer_.begin() + bytes_to_copy, user_buffer_);
498 buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_to_copy);
500 // If the buffer is getting too empty, resume asynchronous loading.
501 if (is_asynchronous_load_suspended_ &&
502 buffer_.size() <= static_cast<size_t>(
503 request_data_.prefetch_buffer_lower_threshold)) {
504 DVLOG(1) << "Resuming async load - buffer size: " << buffer_.size();
505 SetDefersLoading(false);
508 // Reset for next time.
509 user_buffer_ = NULL;
510 user_buffer_size_ = 0;
511 return bytes_to_copy;
514 void PPB_URLLoader_Impl::SaveResponse(const WebURLResponse& response) {
515 // DataFromWebURLResponse returns a file ref with one reference to it, which
516 // we take over via our ScopedPPResource.
517 response_info_.reset(new ::ppapi::URLResponseInfoData(
518 DataFromWebURLResponse(pp_instance(), response)));
519 response_info_file_ref_ = ::ppapi::ScopedPPResource(
520 ::ppapi::ScopedPPResource::PassRef(),
521 response_info_->body_as_file_ref.resource.host_resource());
524 void PPB_URLLoader_Impl::UpdateStatus() {
525 if (status_callback_ &&
526 (RecordDownloadProgress() || RecordUploadProgress())) {
527 // Here we go through some effort to only send the exact information that
528 // the requestor wanted in the request flags. It would be just as
529 // efficient to send all of it, but we don't want people to rely on
530 // getting download progress when they happen to set the upload progress
531 // flag.
532 status_callback_(
533 pp_instance(), pp_resource(),
534 RecordUploadProgress() ? bytes_sent_ : -1,
535 RecordUploadProgress() ? total_bytes_to_be_sent_ : -1,
536 RecordDownloadProgress() ? bytes_received_ : -1,
537 RecordDownloadProgress() ? total_bytes_to_be_received_ : -1);
541 bool PPB_URLLoader_Impl::RecordDownloadProgress() const {
542 return request_data_.record_download_progress;
545 bool PPB_URLLoader_Impl::RecordUploadProgress() const {
546 return request_data_.record_upload_progress;
549 } // namespace ppapi
550 } // namespace webkit