Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(131)

Side by Side Diff: webkit/plugins/ppapi/ppb_url_loader_impl.cc

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

Powered by Google App Engine
This is Rietveld 408576698