OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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/glue/plugins/pepper_url_loader.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "ppapi/c/pp_completion_callback.h" | |
9 #include "ppapi/c/pp_errors.h" | |
10 #include "ppapi/c/ppb_url_loader.h" | |
11 #include "ppapi/c/trusted/ppb_url_loader_trusted.h" | |
12 #include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" | |
13 #include "third_party/WebKit/WebKit/chromium/public/WebElement.h" | |
14 #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" | |
15 #include "third_party/WebKit/WebKit/chromium/public/WebKit.h" | |
16 #include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h" | |
17 #include "third_party/WebKit/WebKit/chromium/public/WebPluginContainer.h" | |
18 #include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h" | |
19 #include "third_party/WebKit/WebKit/chromium/public/WebURLLoader.h" | |
20 #include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h" | |
21 #include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h" | |
22 #include "webkit/appcache/web_application_cache_host_impl.h" | |
23 #include "webkit/glue/plugins/pepper_common.h" | |
24 #include "webkit/glue/plugins/pepper_plugin_instance.h" | |
25 #include "webkit/glue/plugins/pepper_url_request_info.h" | |
26 #include "webkit/glue/plugins/pepper_url_response_info.h" | |
27 | |
28 using appcache::WebApplicationCacheHostImpl; | |
29 using WebKit::WebFrame; | |
30 using WebKit::WebString; | |
31 using WebKit::WebURL; | |
32 using WebKit::WebURLError; | |
33 using WebKit::WebURLLoader; | |
34 using WebKit::WebURLRequest; | |
35 using WebKit::WebURLResponse; | |
36 | |
37 #ifdef _MSC_VER | |
38 // Do not warn about use of std::copy with raw pointers. | |
39 #pragma warning(disable : 4996) | |
40 #endif | |
41 | |
42 namespace pepper { | |
43 | |
44 namespace { | |
45 | |
46 PP_Resource Create(PP_Instance instance_id) { | |
47 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); | |
48 if (!instance) | |
49 return 0; | |
50 | |
51 URLLoader* loader = new URLLoader(instance, false); | |
52 return loader->GetReference(); | |
53 } | |
54 | |
55 PP_Bool IsURLLoader(PP_Resource resource) { | |
56 return BoolToPPBool(!!Resource::GetAs<URLLoader>(resource)); | |
57 } | |
58 | |
59 int32_t Open(PP_Resource loader_id, | |
60 PP_Resource request_id, | |
61 PP_CompletionCallback callback) { | |
62 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
63 if (!loader) | |
64 return PP_ERROR_BADRESOURCE; | |
65 | |
66 scoped_refptr<URLRequestInfo> request( | |
67 Resource::GetAs<URLRequestInfo>(request_id)); | |
68 if (!request) | |
69 return PP_ERROR_BADRESOURCE; | |
70 | |
71 return loader->Open(request, callback); | |
72 } | |
73 | |
74 int32_t FollowRedirect(PP_Resource loader_id, | |
75 PP_CompletionCallback callback) { | |
76 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
77 if (!loader) | |
78 return PP_ERROR_BADRESOURCE; | |
79 | |
80 return loader->FollowRedirect(callback); | |
81 } | |
82 | |
83 PP_Bool GetUploadProgress(PP_Resource loader_id, | |
84 int64_t* bytes_sent, | |
85 int64_t* total_bytes_to_be_sent) { | |
86 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
87 if (!loader) | |
88 return PP_FALSE; | |
89 | |
90 return BoolToPPBool(loader->GetUploadProgress(bytes_sent, | |
91 total_bytes_to_be_sent)); | |
92 } | |
93 | |
94 PP_Bool GetDownloadProgress(PP_Resource loader_id, | |
95 int64_t* bytes_received, | |
96 int64_t* total_bytes_to_be_received) { | |
97 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
98 if (!loader) | |
99 return PP_FALSE; | |
100 | |
101 return BoolToPPBool(loader->GetDownloadProgress(bytes_received, | |
102 total_bytes_to_be_received)); | |
103 } | |
104 | |
105 PP_Resource GetResponseInfo(PP_Resource loader_id) { | |
106 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
107 if (!loader) | |
108 return 0; | |
109 | |
110 URLResponseInfo* response_info = loader->response_info(); | |
111 if (!response_info) | |
112 return 0; | |
113 | |
114 return response_info->GetReference(); | |
115 } | |
116 | |
117 int32_t ReadResponseBody(PP_Resource loader_id, | |
118 char* buffer, | |
119 int32_t bytes_to_read, | |
120 PP_CompletionCallback callback) { | |
121 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
122 if (!loader) | |
123 return PP_ERROR_BADRESOURCE; | |
124 | |
125 return loader->ReadResponseBody(buffer, bytes_to_read, callback); | |
126 } | |
127 | |
128 int32_t FinishStreamingToFile(PP_Resource loader_id, | |
129 PP_CompletionCallback callback) { | |
130 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
131 if (!loader) | |
132 return PP_ERROR_BADRESOURCE; | |
133 | |
134 return loader->FinishStreamingToFile(callback); | |
135 } | |
136 | |
137 void Close(PP_Resource loader_id) { | |
138 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
139 if (!loader) | |
140 return; | |
141 | |
142 loader->Close(); | |
143 } | |
144 | |
145 const PPB_URLLoader ppb_urlloader = { | |
146 &Create, | |
147 &IsURLLoader, | |
148 &Open, | |
149 &FollowRedirect, | |
150 &GetUploadProgress, | |
151 &GetDownloadProgress, | |
152 &GetResponseInfo, | |
153 &ReadResponseBody, | |
154 &FinishStreamingToFile, | |
155 &Close | |
156 }; | |
157 | |
158 void GrantUniversalAccess(PP_Resource loader_id) { | |
159 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
160 if (!loader) | |
161 return; | |
162 | |
163 loader->GrantUniversalAccess(); | |
164 } | |
165 | |
166 void SetStatusCallback(PP_Resource loader_id, | |
167 PP_URLLoaderTrusted_StatusCallback cb) { | |
168 scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id)); | |
169 if (!loader) | |
170 return; | |
171 loader->SetStatusCallback(cb); | |
172 } | |
173 | |
174 const PPB_URLLoaderTrusted ppb_urlloadertrusted = { | |
175 &GrantUniversalAccess, | |
176 &SetStatusCallback | |
177 }; | |
178 | |
179 WebKit::WebFrame* GetFrame(PluginInstance* instance) { | |
180 return instance->container()->element().document().frame(); | |
181 } | |
182 | |
183 } // namespace | |
184 | |
185 URLLoader::URLLoader(PluginInstance* instance, bool main_document_loader) | |
186 : Resource(instance->module()), | |
187 instance_(instance), | |
188 main_document_loader_(main_document_loader), | |
189 pending_callback_(), | |
190 bytes_sent_(0), | |
191 total_bytes_to_be_sent_(-1), | |
192 bytes_received_(0), | |
193 total_bytes_to_be_received_(-1), | |
194 user_buffer_(NULL), | |
195 user_buffer_size_(0), | |
196 done_status_(PP_ERROR_WOULDBLOCK), | |
197 has_universal_access_(false), | |
198 status_callback_(NULL) { | |
199 instance->AddObserver(this); | |
200 } | |
201 | |
202 URLLoader::~URLLoader() { | |
203 if (instance_) | |
204 instance_->RemoveObserver(this); | |
205 } | |
206 | |
207 // static | |
208 const PPB_URLLoader* URLLoader::GetInterface() { | |
209 return &ppb_urlloader; | |
210 } | |
211 | |
212 // static | |
213 const PPB_URLLoaderTrusted* URLLoader::GetTrustedInterface() { | |
214 return &ppb_urlloadertrusted; | |
215 } | |
216 | |
217 URLLoader* URLLoader::AsURLLoader() { | |
218 return this; | |
219 } | |
220 | |
221 int32_t URLLoader::Open(URLRequestInfo* request, | |
222 PP_CompletionCallback callback) { | |
223 if (loader_.get()) | |
224 return PP_ERROR_INPROGRESS; | |
225 | |
226 // We only support non-blocking calls. | |
227 if (!callback.func) | |
228 return PP_ERROR_BADARGUMENT; | |
229 | |
230 WebFrame* frame = GetFrame(instance_); | |
231 if (!frame) | |
232 return PP_ERROR_FAILED; | |
233 WebURLRequest web_request(request->ToWebURLRequest(frame)); | |
234 | |
235 int32_t rv = CanRequest(frame, web_request.url()); | |
236 if (rv != PP_OK) | |
237 return rv; | |
238 | |
239 frame->dispatchWillSendRequest(web_request); | |
240 | |
241 // Sets the appcache host id to allow retrieval from the appcache. | |
242 if (WebApplicationCacheHostImpl* appcache_host = | |
243 WebApplicationCacheHostImpl::FromFrame(frame)) { | |
244 appcache_host->willStartSubResourceRequest(web_request); | |
245 } | |
246 | |
247 loader_.reset(WebKit::webKitClient()->createURLLoader()); | |
248 if (!loader_.get()) | |
249 return PP_ERROR_FAILED; | |
250 | |
251 loader_->loadAsynchronously(web_request, this); | |
252 | |
253 request_info_ = scoped_refptr<URLRequestInfo>(request); | |
254 pending_callback_ = callback; | |
255 | |
256 // Notify completion when we receive a redirect or response headers. | |
257 return PP_ERROR_WOULDBLOCK; | |
258 } | |
259 | |
260 int32_t URLLoader::FollowRedirect(PP_CompletionCallback callback) { | |
261 if (pending_callback_.func) | |
262 return PP_ERROR_INPROGRESS; | |
263 | |
264 // We only support non-blocking calls. | |
265 if (!callback.func) | |
266 return PP_ERROR_BADARGUMENT; | |
267 | |
268 WebURL redirect_url = GURL(response_info_->redirect_url()); | |
269 | |
270 int32_t rv = CanRequest(GetFrame(instance_), redirect_url); | |
271 if (rv != PP_OK) | |
272 return rv; | |
273 | |
274 pending_callback_ = callback; | |
275 loader_->setDefersLoading(false); // Allow the redirect to continue. | |
276 return PP_ERROR_WOULDBLOCK; | |
277 } | |
278 | |
279 bool URLLoader::GetUploadProgress(int64_t* bytes_sent, | |
280 int64_t* total_bytes_to_be_sent) { | |
281 if (!RecordUploadProgress()) { | |
282 *bytes_sent = 0; | |
283 *total_bytes_to_be_sent = 0; | |
284 return false; | |
285 } | |
286 *bytes_sent = bytes_sent_; | |
287 *total_bytes_to_be_sent = total_bytes_to_be_sent_; | |
288 return true; | |
289 } | |
290 | |
291 bool URLLoader::GetDownloadProgress(int64_t* bytes_received, | |
292 int64_t* total_bytes_to_be_received) { | |
293 if (!RecordDownloadProgress()) { | |
294 *bytes_received = 0; | |
295 *total_bytes_to_be_received = 0; | |
296 return false; | |
297 } | |
298 *bytes_received = bytes_received_; | |
299 *total_bytes_to_be_received = total_bytes_to_be_received_; | |
300 return true; | |
301 } | |
302 | |
303 int32_t URLLoader::ReadResponseBody(char* buffer, int32_t bytes_to_read, | |
304 PP_CompletionCallback callback) { | |
305 if (!response_info_ || response_info_->body()) | |
306 return PP_ERROR_FAILED; | |
307 if (bytes_to_read <= 0 || !buffer) | |
308 return PP_ERROR_BADARGUMENT; | |
309 if (pending_callback_.func) | |
310 return PP_ERROR_INPROGRESS; | |
311 | |
312 // We only support non-blocking calls. | |
313 if (!callback.func) | |
314 return PP_ERROR_BADARGUMENT; | |
315 | |
316 user_buffer_ = buffer; | |
317 user_buffer_size_ = bytes_to_read; | |
318 | |
319 if (!buffer_.empty()) | |
320 return FillUserBuffer(); | |
321 | |
322 // We may have already reached EOF. | |
323 if (done_status_ != PP_ERROR_WOULDBLOCK) { | |
324 user_buffer_ = NULL; | |
325 user_buffer_size_ = 0; | |
326 return done_status_; | |
327 } | |
328 | |
329 pending_callback_ = callback; | |
330 return PP_ERROR_WOULDBLOCK; | |
331 } | |
332 | |
333 int32_t URLLoader::FinishStreamingToFile(PP_CompletionCallback callback) { | |
334 if (!response_info_ || !response_info_->body()) | |
335 return PP_ERROR_FAILED; | |
336 if (pending_callback_.func) | |
337 return PP_ERROR_INPROGRESS; | |
338 | |
339 // We may have already reached EOF. | |
340 if (done_status_ != PP_ERROR_WOULDBLOCK) | |
341 return done_status_; | |
342 | |
343 // Wait for didFinishLoading / didFail. | |
344 pending_callback_ = callback; | |
345 return PP_ERROR_WOULDBLOCK; | |
346 } | |
347 | |
348 void URLLoader::Close() { | |
349 if (loader_.get()) { | |
350 loader_->cancel(); | |
351 } else if (main_document_loader_) { | |
352 WebFrame* frame = instance_->container()->element().document().frame(); | |
353 frame->stopLoading(); | |
354 } | |
355 } | |
356 | |
357 void URLLoader::GrantUniversalAccess() { | |
358 has_universal_access_ = true; | |
359 } | |
360 | |
361 void URLLoader::SetStatusCallback(PP_URLLoaderTrusted_StatusCallback cb) { | |
362 status_callback_ = cb; | |
363 } | |
364 | |
365 void URLLoader::willSendRequest(WebURLLoader* loader, | |
366 WebURLRequest& new_request, | |
367 const WebURLResponse& redirect_response) { | |
368 if (!request_info_->follow_redirects()) { | |
369 SaveResponse(redirect_response); | |
370 loader_->setDefersLoading(true); | |
371 RunCallback(PP_OK); | |
372 } else { | |
373 int32_t rv = CanRequest(GetFrame(instance_), new_request.url()); | |
374 if (rv != PP_OK) { | |
375 loader_->setDefersLoading(true); | |
376 RunCallback(rv); | |
377 } | |
378 } | |
379 } | |
380 | |
381 void URLLoader::didSendData(WebURLLoader* loader, | |
382 unsigned long long bytes_sent, | |
383 unsigned long long total_bytes_to_be_sent) { | |
384 // TODO(darin): Bounds check input? | |
385 bytes_sent_ = static_cast<int64_t>(bytes_sent); | |
386 total_bytes_to_be_sent_ = static_cast<int64_t>(total_bytes_to_be_sent); | |
387 UpdateStatus(); | |
388 } | |
389 | |
390 void URLLoader::didReceiveResponse(WebURLLoader* loader, | |
391 const WebURLResponse& response) { | |
392 SaveResponse(response); | |
393 | |
394 // Sets -1 if the content length is unknown. | |
395 total_bytes_to_be_received_ = response.expectedContentLength(); | |
396 UpdateStatus(); | |
397 | |
398 RunCallback(PP_OK); | |
399 } | |
400 | |
401 void URLLoader::didDownloadData(WebURLLoader* loader, | |
402 int data_length) { | |
403 bytes_received_ += data_length; | |
404 UpdateStatus(); | |
405 } | |
406 | |
407 void URLLoader::didReceiveData(WebURLLoader* loader, | |
408 const char* data, | |
409 int data_length) { | |
410 bytes_received_ += data_length; | |
411 | |
412 buffer_.insert(buffer_.end(), data, data + data_length); | |
413 if (user_buffer_) { | |
414 RunCallback(FillUserBuffer()); | |
415 } else { | |
416 DCHECK(!pending_callback_.func); | |
417 } | |
418 } | |
419 | |
420 void URLLoader::didFinishLoading(WebURLLoader* loader, double finish_time) { | |
421 done_status_ = PP_OK; | |
422 RunCallback(done_status_); | |
423 } | |
424 | |
425 void URLLoader::didFail(WebURLLoader* loader, const WebURLError& error) { | |
426 // TODO(darin): Provide more detailed error information. | |
427 done_status_ = PP_ERROR_FAILED; | |
428 RunCallback(done_status_); | |
429 } | |
430 | |
431 void URLLoader::InstanceDestroyed(PluginInstance* instance) { | |
432 // When the instance is destroyed, we force delete any associated loads. | |
433 DCHECK(instance == instance_); | |
434 instance_ = NULL; | |
435 | |
436 // Normally the only ref to this class will be from the plugin which | |
437 // ForceDeletePluginResourceRefs will free. We don't want our object to be | |
438 // deleted out from under us until the function completes. | |
439 scoped_refptr<URLLoader> death_grip(this); | |
440 | |
441 // Force delete any plugin refs to us. If the instance is being deleted, we | |
442 // don't want to allow the requests to continue to use bandwidth and send us | |
443 // callbacks (for which we might have no plugin). | |
444 ResourceTracker *tracker = ResourceTracker::Get(); | |
445 PP_Resource loader_resource = GetReferenceNoAddRef(); | |
446 if (loader_resource) | |
447 tracker->ForceDeletePluginResourceRefs(loader_resource); | |
448 | |
449 // Also force free the response from the plugin, both the plugin's ref(s) | |
450 // and ours. | |
451 if (response_info_.get()) { | |
452 PP_Resource response_info_resource = response_info_->GetReferenceNoAddRef(); | |
453 if (response_info_resource) | |
454 tracker->ForceDeletePluginResourceRefs(response_info_resource); | |
455 response_info_ = NULL; | |
456 } | |
457 | |
458 // Free the WebKit request. | |
459 loader_.reset(); | |
460 | |
461 // Often, |this| will be deleted at the end of this function when death_grip | |
462 // goes out of scope. | |
463 } | |
464 | |
465 void URLLoader::RunCallback(int32_t result) { | |
466 if (!pending_callback_.func) | |
467 return; | |
468 | |
469 PP_CompletionCallback callback = {0}; | |
470 std::swap(callback, pending_callback_); | |
471 PP_RunCompletionCallback(&callback, result); | |
472 } | |
473 | |
474 size_t URLLoader::FillUserBuffer() { | |
475 DCHECK(user_buffer_); | |
476 DCHECK(user_buffer_size_); | |
477 | |
478 size_t bytes_to_copy = std::min(buffer_.size(), user_buffer_size_); | |
479 std::copy(buffer_.begin(), buffer_.begin() + bytes_to_copy, user_buffer_); | |
480 buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_to_copy); | |
481 | |
482 // Reset for next time. | |
483 user_buffer_ = NULL; | |
484 user_buffer_size_ = 0; | |
485 return bytes_to_copy; | |
486 } | |
487 | |
488 void URLLoader::SaveResponse(const WebKit::WebURLResponse& response) { | |
489 scoped_refptr<URLResponseInfo> response_info(new URLResponseInfo(module())); | |
490 if (response_info->Initialize(response)) | |
491 response_info_ = response_info; | |
492 } | |
493 | |
494 // Checks that the client can request the URL. Returns a PPAPI error code. | |
495 int32_t URLLoader::CanRequest(const WebKit::WebFrame* frame, | |
496 const WebKit::WebURL& url) { | |
497 if (!has_universal_access_ && | |
498 !frame->securityOrigin().canRequest(url)) | |
499 return PP_ERROR_NOACCESS; | |
500 | |
501 return PP_OK; | |
502 } | |
503 | |
504 void URLLoader::UpdateStatus() { | |
505 if (status_callback_ && | |
506 (RecordDownloadProgress() || RecordUploadProgress())) { | |
507 PP_Resource pp_resource = GetReferenceNoAddRef(); | |
508 if (pp_resource) { | |
509 // The PP_Resource on the plugin will be NULL if the plugin has no | |
510 // reference to this object. That's fine, because then we don't need to | |
511 // call UpdateStatus. | |
512 // | |
513 // Here we go through some effort to only send the exact information that | |
514 // the requestor wanted in the request flags. It would be just as | |
515 // efficient to send all of it, but we don't want people to rely on | |
516 // getting download progress when they happen to set the upload progress | |
517 // flag. | |
518 status_callback_( | |
519 instance_->pp_instance(), pp_resource, | |
520 RecordUploadProgress() ? bytes_sent_ : -1, | |
521 RecordUploadProgress() ? total_bytes_to_be_sent_ : -1, | |
522 RecordDownloadProgress() ? bytes_received_ : -1, | |
523 RecordDownloadProgress() ? total_bytes_to_be_received_ : -1); | |
524 } | |
525 } | |
526 } | |
527 | |
528 bool URLLoader::RecordDownloadProgress() const { | |
529 return request_info_ && request_info_->record_download_progress(); | |
530 } | |
531 | |
532 bool URLLoader::RecordUploadProgress() const { | |
533 return request_info_ && request_info_->record_upload_progress(); | |
534 } | |
535 | |
536 } // namespace pepper | |
OLD | NEW |