Revert 93987 - Close the URL request in the destroctor. This ensures the load is...
[chromium-blink-merge.git] / webkit / plugins / ppapi / ppb_url_loader_impl.cc
blob6fb46ef43b8299f0c7b7f86aae2b685793477be2
1 // Copyright (c) 2011 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/thunk/enter.h"
14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKitClient.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoader.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoaderOptions.h"
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h"
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h"
26 #include "webkit/appcache/web_application_cache_host_impl.h"
27 #include "webkit/plugins/ppapi/common.h"
28 #include "webkit/plugins/ppapi/plugin_module.h"
29 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
30 #include "webkit/plugins/ppapi/ppb_url_request_info_impl.h"
31 #include "webkit/plugins/ppapi/ppb_url_response_info_impl.h"
33 using appcache::WebApplicationCacheHostImpl;
34 using ppapi::thunk::EnterResourceNoLock;
35 using ppapi::thunk::PPB_URLLoader_API;
36 using ppapi::thunk::PPB_URLRequestInfo_API;
37 using WebKit::WebFrame;
38 using WebKit::WebString;
39 using WebKit::WebURL;
40 using WebKit::WebURLError;
41 using WebKit::WebURLLoader;
42 using WebKit::WebURLLoaderOptions;
43 using WebKit::WebURLRequest;
44 using WebKit::WebURLResponse;
46 #ifdef _MSC_VER
47 // Do not warn about use of std::copy with raw pointers.
48 #pragma warning(disable : 4996)
49 #endif
51 namespace webkit {
52 namespace ppapi {
54 PPB_URLLoader_Impl::PPB_URLLoader_Impl(PluginInstance* instance,
55 bool main_document_loader)
56 : Resource(instance),
57 main_document_loader_(main_document_loader),
58 pending_callback_(),
59 bytes_sent_(0),
60 total_bytes_to_be_sent_(-1),
61 bytes_received_(0),
62 total_bytes_to_be_received_(-1),
63 user_buffer_(NULL),
64 user_buffer_size_(0),
65 done_status_(PP_OK_COMPLETIONPENDING),
66 is_streaming_to_file_(false),
67 is_asynchronous_load_suspended_(false),
68 has_universal_access_(false),
69 status_callback_(NULL) {
72 PPB_URLLoader_Impl::~PPB_URLLoader_Impl() {
75 PPB_URLLoader_API* PPB_URLLoader_Impl::AsPPB_URLLoader_API() {
76 return this;
79 void PPB_URLLoader_Impl::ClearInstance() {
80 Resource::ClearInstance();
81 loader_.reset();
84 int32_t PPB_URLLoader_Impl::Open(PP_Resource request_id,
85 PP_CompletionCallback callback) {
86 EnterResourceNoLock<PPB_URLRequestInfo_API> enter_request(request_id, true);
87 if (enter_request.failed())
88 return PP_ERROR_BADARGUMENT;
89 PPB_URLRequestInfo_Impl* request = static_cast<PPB_URLRequestInfo_Impl*>(
90 enter_request.object());
92 int32_t rv = ValidateCallback(callback);
93 if (rv != PP_OK)
94 return rv;
96 if (request->RequiresUniversalAccess() && !has_universal_access_)
97 return PP_ERROR_NOACCESS;
99 if (loader_.get())
100 return PP_ERROR_INPROGRESS;
102 WebFrame* frame = instance()->container()->element().document().frame();
103 if (!frame)
104 return PP_ERROR_FAILED;
105 WebURLRequest web_request(request->ToWebURLRequest(frame));
107 WebURLLoaderOptions options;
108 if (has_universal_access_) {
109 // Universal access allows cross-origin requests and sends credentials.
110 options.crossOriginRequestPolicy =
111 WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
112 options.allowCredentials = true;
113 } else if (request->allow_cross_origin_requests()) {
114 // Otherwise, allow cross-origin requests with access control.
115 options.crossOriginRequestPolicy =
116 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
117 options.allowCredentials = request->allow_credentials();
120 is_asynchronous_load_suspended_ = false;
121 loader_.reset(frame->createAssociatedURLLoader(options));
122 if (!loader_.get())
123 return PP_ERROR_FAILED;
125 loader_->loadAsynchronously(web_request, this);
127 request_info_ = scoped_refptr<PPB_URLRequestInfo_Impl>(request);
129 // Notify completion when we receive a redirect or response headers.
130 RegisterCallback(callback);
131 return PP_OK_COMPLETIONPENDING;
134 int32_t PPB_URLLoader_Impl::FollowRedirect(PP_CompletionCallback callback) {
135 int32_t rv = ValidateCallback(callback);
136 if (rv != PP_OK)
137 return rv;
139 WebURL redirect_url = GURL(response_info_->redirect_url());
141 loader_->setDefersLoading(false); // Allow the redirect to continue.
142 RegisterCallback(callback);
143 return PP_OK_COMPLETIONPENDING;
146 PP_Bool PPB_URLLoader_Impl::GetUploadProgress(int64_t* bytes_sent,
147 int64_t* total_bytes_to_be_sent) {
148 if (!RecordUploadProgress()) {
149 *bytes_sent = 0;
150 *total_bytes_to_be_sent = 0;
151 return PP_FALSE;
153 *bytes_sent = bytes_sent_;
154 *total_bytes_to_be_sent = total_bytes_to_be_sent_;
155 return PP_TRUE;
158 PP_Bool PPB_URLLoader_Impl::GetDownloadProgress(
159 int64_t* bytes_received,
160 int64_t* total_bytes_to_be_received) {
161 if (!RecordDownloadProgress()) {
162 *bytes_received = 0;
163 *total_bytes_to_be_received = 0;
164 return PP_FALSE;
166 *bytes_received = bytes_received_;
167 *total_bytes_to_be_received = total_bytes_to_be_received_;
168 return PP_TRUE;
171 PP_Resource PPB_URLLoader_Impl::GetResponseInfo() {
172 if (!response_info_)
173 return 0;
174 return response_info_->GetReference();
177 int32_t PPB_URLLoader_Impl::ReadResponseBody(void* buffer,
178 int32_t bytes_to_read,
179 PP_CompletionCallback callback) {
180 int32_t rv = ValidateCallback(callback);
181 if (rv != PP_OK)
182 return rv;
183 if (!response_info_ || response_info_->body())
184 return PP_ERROR_FAILED;
185 if (bytes_to_read <= 0 || !buffer)
186 return PP_ERROR_BADARGUMENT;
188 user_buffer_ = static_cast<char*>(buffer);
189 user_buffer_size_ = bytes_to_read;
191 if (!buffer_.empty())
192 return FillUserBuffer();
194 // We may have already reached EOF.
195 if (done_status_ != PP_OK_COMPLETIONPENDING) {
196 user_buffer_ = NULL;
197 user_buffer_size_ = 0;
198 return done_status_;
201 RegisterCallback(callback);
202 return PP_OK_COMPLETIONPENDING;
205 int32_t PPB_URLLoader_Impl::FinishStreamingToFile(
206 PP_CompletionCallback callback) {
207 int32_t rv = ValidateCallback(callback);
208 if (rv != PP_OK)
209 return rv;
210 if (!response_info_ || !response_info_->body())
211 return PP_ERROR_FAILED;
213 // We may have already reached EOF.
214 if (done_status_ != PP_OK_COMPLETIONPENDING)
215 return done_status_;
217 is_streaming_to_file_ = true;
218 if (is_asynchronous_load_suspended_) {
219 loader_->setDefersLoading(false);
220 is_asynchronous_load_suspended_ = false;
223 // Wait for didFinishLoading / didFail.
224 RegisterCallback(callback);
225 return PP_OK_COMPLETIONPENDING;
228 void PPB_URLLoader_Impl::Close() {
229 if (loader_.get()) {
230 loader_->cancel();
231 } else if (main_document_loader_) {
232 WebFrame* frame = instance()->container()->element().document().frame();
233 frame->stopLoading();
235 // TODO(viettrungluu): Check what happens to the callback (probably the
236 // wrong thing). May need to post abort here. crbug.com/69457
239 void PPB_URLLoader_Impl::GrantUniversalAccess() {
240 has_universal_access_ = true;
243 void PPB_URLLoader_Impl::SetStatusCallback(
244 PP_URLLoaderTrusted_StatusCallback cb) {
245 status_callback_ = cb;
248 void PPB_URLLoader_Impl::willSendRequest(
249 WebURLLoader* loader,
250 WebURLRequest& new_request,
251 const WebURLResponse& redirect_response) {
252 if (!request_info_->follow_redirects()) {
253 SaveResponse(redirect_response);
254 loader_->setDefersLoading(true);
255 RunCallback(PP_OK);
259 void PPB_URLLoader_Impl::didSendData(
260 WebURLLoader* loader,
261 unsigned long long bytes_sent,
262 unsigned long long total_bytes_to_be_sent) {
263 // TODO(darin): Bounds check input?
264 bytes_sent_ = static_cast<int64_t>(bytes_sent);
265 total_bytes_to_be_sent_ = static_cast<int64_t>(total_bytes_to_be_sent);
266 UpdateStatus();
269 void PPB_URLLoader_Impl::didReceiveResponse(WebURLLoader* loader,
270 const WebURLResponse& response) {
271 SaveResponse(response);
273 // Sets -1 if the content length is unknown.
274 total_bytes_to_be_received_ = response.expectedContentLength();
275 UpdateStatus();
277 RunCallback(PP_OK);
280 void PPB_URLLoader_Impl::didDownloadData(WebURLLoader* loader,
281 int data_length) {
282 bytes_received_ += data_length;
283 UpdateStatus();
286 void PPB_URLLoader_Impl::didReceiveData(WebURLLoader* loader,
287 const char* data,
288 int data_length,
289 int encoded_data_length) {
290 bytes_received_ += data_length;
292 buffer_.insert(buffer_.end(), data, data + data_length);
293 if (user_buffer_) {
294 RunCallback(FillUserBuffer());
295 } else {
296 DCHECK(!pending_callback_.get() || pending_callback_->completed());
299 // To avoid letting the network stack download an entire stream all at once,
300 // defer loading when we have enough buffer.
301 // Check the buffer size after potentially moving some to the user buffer.
302 DCHECK(!request_info_ ||
303 (request_info_->prefetch_buffer_lower_threshold() <
304 request_info_->prefetch_buffer_upper_threshold()));
305 if (!is_streaming_to_file_ &&
306 !is_asynchronous_load_suspended_ &&
307 request_info_ &&
308 (buffer_.size() >= static_cast<size_t>(
309 request_info_->prefetch_buffer_upper_threshold()))) {
310 DVLOG(1) << "Suspending async load - buffer size: " << buffer_.size();
311 loader->setDefersLoading(true);
312 is_asynchronous_load_suspended_ = true;
316 void PPB_URLLoader_Impl::didFinishLoading(WebURLLoader* loader,
317 double finish_time) {
318 FinishLoading(PP_OK);
321 void PPB_URLLoader_Impl::didFail(WebURLLoader* loader,
322 const WebURLError& error) {
323 int32_t pp_error = PP_ERROR_FAILED;
324 if (error.domain.equals(WebString::fromUTF8(net::kErrorDomain))) {
325 // TODO(bbudge): Extend pp_errors.h to cover interesting network errors
326 // from the net error domain.
327 switch (error.reason) {
328 case net::ERR_ACCESS_DENIED:
329 case net::ERR_NETWORK_ACCESS_DENIED:
330 pp_error = PP_ERROR_NOACCESS;
331 break;
333 } else {
334 // It's a WebKit error.
335 pp_error = PP_ERROR_NOACCESS;
338 FinishLoading(pp_error);
341 void PPB_URLLoader_Impl::FinishLoading(int32_t done_status) {
342 done_status_ = done_status;
343 // If the client hasn't called any function that takes a callback since
344 // the initial call to Open, or called ReadResponseBody and got a
345 // synchronous return, then the callback will be NULL.
346 if (pending_callback_.get() && !pending_callback_->completed())
347 RunCallback(done_status_);
350 int32_t PPB_URLLoader_Impl::ValidateCallback(PP_CompletionCallback callback) {
351 // We only support non-blocking calls.
352 if (!callback.func)
353 return PP_ERROR_BADARGUMENT;
355 if (pending_callback_.get() && !pending_callback_->completed())
356 return PP_ERROR_INPROGRESS;
358 return PP_OK;
361 void PPB_URLLoader_Impl::RegisterCallback(PP_CompletionCallback callback) {
362 DCHECK(callback.func);
363 DCHECK(!pending_callback_.get() || pending_callback_->completed());
365 PP_Resource resource_id = GetReferenceNoAddRef();
366 CHECK(resource_id);
367 pending_callback_ = new TrackedCompletionCallback(
368 instance()->module()->GetCallbackTracker(), resource_id, callback);
371 void PPB_URLLoader_Impl::RunCallback(int32_t result) {
372 // This may be null only when this is a main document loader.
373 if (!pending_callback_.get()) {
374 CHECK(main_document_loader_);
375 return;
378 scoped_refptr<TrackedCompletionCallback> callback;
379 callback.swap(pending_callback_);
380 callback->Run(result); // Will complete abortively if necessary.
383 size_t PPB_URLLoader_Impl::FillUserBuffer() {
384 DCHECK(user_buffer_);
385 DCHECK(user_buffer_size_);
387 size_t bytes_to_copy = std::min(buffer_.size(), user_buffer_size_);
388 std::copy(buffer_.begin(), buffer_.begin() + bytes_to_copy, user_buffer_);
389 buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_to_copy);
391 // If the buffer is getting too empty, resume asynchronous loading.
392 DCHECK(!is_asynchronous_load_suspended_ || request_info_);
393 if (is_asynchronous_load_suspended_ &&
394 buffer_.size() <= static_cast<size_t>(
395 request_info_->prefetch_buffer_lower_threshold())) {
396 DVLOG(1) << "Resuming async load - buffer size: " << buffer_.size();
397 loader_->setDefersLoading(false);
398 is_asynchronous_load_suspended_ = false;
401 // Reset for next time.
402 user_buffer_ = NULL;
403 user_buffer_size_ = 0;
404 return bytes_to_copy;
407 void PPB_URLLoader_Impl::SaveResponse(const WebURLResponse& response) {
408 scoped_refptr<PPB_URLResponseInfo_Impl> response_info(
409 new PPB_URLResponseInfo_Impl(instance()));
410 if (response_info->Initialize(response))
411 response_info_ = response_info;
414 void PPB_URLLoader_Impl::UpdateStatus() {
415 if (status_callback_ &&
416 (RecordDownloadProgress() || RecordUploadProgress())) {
417 PP_Resource pp_resource = GetReferenceNoAddRef();
418 if (pp_resource) {
419 // The PP_Resource on the plugin will be NULL if the plugin has no
420 // reference to this object. That's fine, because then we don't need to
421 // call UpdateStatus.
423 // Here we go through some effort to only send the exact information that
424 // the requestor wanted in the request flags. It would be just as
425 // efficient to send all of it, but we don't want people to rely on
426 // getting download progress when they happen to set the upload progress
427 // flag.
428 status_callback_(
429 instance()->pp_instance(), pp_resource,
430 RecordUploadProgress() ? bytes_sent_ : -1,
431 RecordUploadProgress() ? total_bytes_to_be_sent_ : -1,
432 RecordDownloadProgress() ? bytes_received_ : -1,
433 RecordDownloadProgress() ? total_bytes_to_be_received_ : -1);
438 bool PPB_URLLoader_Impl::RecordDownloadProgress() const {
439 return request_info_ && request_info_->record_download_progress();
442 bool PPB_URLLoader_Impl::RecordUploadProgress() const {
443 return request_info_ && request_info_->record_upload_progress();
446 } // namespace ppapi
447 } // namespace webkit