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
;
40 using WebKit::WebURLError
;
41 using WebKit::WebURLLoader
;
42 using WebKit::WebURLLoaderOptions
;
43 using WebKit::WebURLRequest
;
44 using WebKit::WebURLResponse
;
47 // Do not warn about use of std::copy with raw pointers.
48 #pragma warning(disable : 4996)
54 PPB_URLLoader_Impl::PPB_URLLoader_Impl(PluginInstance
* instance
,
55 bool main_document_loader
)
57 main_document_loader_(main_document_loader
),
60 total_bytes_to_be_sent_(-1),
62 total_bytes_to_be_received_(-1),
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() {
79 void PPB_URLLoader_Impl::ClearInstance() {
80 Resource::ClearInstance();
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
);
96 if (request
->RequiresUniversalAccess() && !has_universal_access_
)
97 return PP_ERROR_NOACCESS
;
100 return PP_ERROR_INPROGRESS
;
102 WebFrame
* frame
= instance()->container()->element().document().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
));
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
);
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()) {
150 *total_bytes_to_be_sent
= 0;
153 *bytes_sent
= bytes_sent_
;
154 *total_bytes_to_be_sent
= total_bytes_to_be_sent_
;
158 PP_Bool
PPB_URLLoader_Impl::GetDownloadProgress(
159 int64_t* bytes_received
,
160 int64_t* total_bytes_to_be_received
) {
161 if (!RecordDownloadProgress()) {
163 *total_bytes_to_be_received
= 0;
166 *bytes_received
= bytes_received_
;
167 *total_bytes_to_be_received
= total_bytes_to_be_received_
;
171 PP_Resource
PPB_URLLoader_Impl::GetResponseInfo() {
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
);
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
) {
197 user_buffer_size_
= 0;
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
);
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
)
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() {
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);
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
);
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();
280 void PPB_URLLoader_Impl::didDownloadData(WebURLLoader
* loader
,
282 bytes_received_
+= data_length
;
286 void PPB_URLLoader_Impl::didReceiveData(WebURLLoader
* loader
,
289 int encoded_data_length
) {
290 bytes_received_
+= data_length
;
292 buffer_
.insert(buffer_
.end(), data
, data
+ data_length
);
294 RunCallback(FillUserBuffer());
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_
&&
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
;
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.
353 return PP_ERROR_BADARGUMENT
;
355 if (pending_callback_
.get() && !pending_callback_
->completed())
356 return PP_ERROR_INPROGRESS
;
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();
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_
);
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.
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();
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
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();
447 } // namespace webkit