1 // Copyright (c) 2013 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 "content/renderer/pepper/pepper_url_loader_host.h"
7 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
8 #include "content/renderer/pepper/renderer_ppapi_host_impl.h"
9 #include "content/renderer/pepper/url_request_info_util.h"
10 #include "content/renderer/pepper/url_response_info_util.h"
11 #include "net/base/net_errors.h"
12 #include "ppapi/c/pp_errors.h"
13 #include "ppapi/host/dispatch_host_message.h"
14 #include "ppapi/host/host_message_context.h"
15 #include "ppapi/host/ppapi_host.h"
16 #include "ppapi/proxy/ppapi_messages.h"
17 #include "ppapi/shared_impl/ppapi_globals.h"
18 #include "third_party/WebKit/public/platform/WebURLError.h"
19 #include "third_party/WebKit/public/platform/WebURLLoader.h"
20 #include "third_party/WebKit/public/platform/WebURLRequest.h"
21 #include "third_party/WebKit/public/platform/WebURLResponse.h"
22 #include "third_party/WebKit/public/web/WebDocument.h"
23 #include "third_party/WebKit/public/web/WebElement.h"
24 #include "third_party/WebKit/public/web/WebKit.h"
25 #include "third_party/WebKit/public/web/WebLocalFrame.h"
26 #include "third_party/WebKit/public/web/WebPluginContainer.h"
27 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
28 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
30 using blink::WebLocalFrame
;
31 using blink::WebString
;
33 using blink::WebURLError
;
34 using blink::WebURLLoader
;
35 using blink::WebURLLoaderOptions
;
36 using blink::WebURLRequest
;
37 using blink::WebURLResponse
;
40 // Do not warn about use of std::copy with raw pointers.
41 #pragma warning(disable : 4996)
46 PepperURLLoaderHost::PepperURLLoaderHost(RendererPpapiHostImpl
* host
,
47 bool main_document_loader
,
50 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
51 renderer_ppapi_host_(host
),
52 main_document_loader_(main_document_loader
),
53 has_universal_access_(false),
55 total_bytes_to_be_sent_(-1),
57 total_bytes_to_be_received_(-1),
58 pending_response_(false),
60 DCHECK((main_document_loader
&& !resource
) ||
61 (!main_document_loader
&& resource
));
64 PepperURLLoaderHost::~PepperURLLoaderHost() {
65 // Normally deleting this object will delete the loader which will implicitly
66 // cancel the load. But this won't happen for the main document loader. So it
67 // would be nice to issue a Close() here.
69 // However, the PDF plugin will cancel the document load and then close the
70 // resource (which is reasonable). It then makes a second request to load the
71 // document so it can set the "want progress" flags (which is unreasonable --
72 // we should probably provide download progress on document loads).
74 // But a Close() on the main document (even if the request is already
75 // canceled) will cancel all pending subresources, of which the second
76 // request is one, and the load will fail. Even if we fixed the PDF reader to
77 // change the timing or to send progress events to avoid the second request,
78 // we don't want to cancel other loads when the main one is closed.
80 // "Leaking" the main document load here by not closing it will only affect
81 // plugins handling main document loads (which are very few, mostly only PDF)
82 // that dereference without explicitly closing the main document load (which
83 // PDF doesn't do -- it explicitly closes it before issuing the second
84 // request). And the worst thing that will happen is that any remaining data
85 // will get queued inside WebKit.
86 if (main_document_loader_
) {
87 // The PluginInstance has a non-owning pointer to us.
88 PepperPluginInstanceImpl
* instance_object
=
89 renderer_ppapi_host_
->GetPluginInstanceImpl(pp_instance());
90 if (instance_object
) {
91 DCHECK(instance_object
->document_loader() == this);
92 instance_object
->set_document_loader(NULL
);
96 // There is a path whereby the destructor for the loader_ member can
97 // invoke InstanceWasDeleted() upon this URLLoaderResource, thereby
98 // re-entering the scoped_ptr destructor with the same scoped_ptr object
99 // via loader_.reset(). Be sure that loader_ is first NULL then destroy
100 // the scoped_ptr. See http://crbug.com/159429.
101 scoped_ptr
<blink::WebURLLoader
> for_destruction_only(loader_
.release());
104 int32_t PepperURLLoaderHost::OnResourceMessageReceived(
105 const IPC::Message
& msg
,
106 ppapi::host::HostMessageContext
* context
) {
107 PPAPI_BEGIN_MESSAGE_MAP(PepperURLLoaderHost
, msg
)
108 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_URLLoader_Open
,
110 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_URLLoader_SetDeferLoading
,
111 OnHostMsgSetDeferLoading
)
112 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_URLLoader_Close
,
114 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
115 PpapiHostMsg_URLLoader_GrantUniversalAccess
,
116 OnHostMsgGrantUniversalAccess
)
117 PPAPI_END_MESSAGE_MAP()
118 return PP_ERROR_FAILED
;
121 void PepperURLLoaderHost::willSendRequest(
122 WebURLLoader
* loader
,
123 WebURLRequest
& new_request
,
124 const WebURLResponse
& redirect_response
) {
125 DCHECK(out_of_order_replies_
.empty());
126 if (!request_data_
.follow_redirects
) {
127 SaveResponse(redirect_response
);
128 SetDefersLoading(true);
132 void PepperURLLoaderHost::didSendData(
133 WebURLLoader
* loader
,
134 unsigned long long bytes_sent
,
135 unsigned long long total_bytes_to_be_sent
) {
136 // TODO(darin): Bounds check input?
137 bytes_sent_
= static_cast<int64_t>(bytes_sent
);
138 total_bytes_to_be_sent_
= static_cast<int64_t>(total_bytes_to_be_sent
);
142 void PepperURLLoaderHost::didReceiveResponse(WebURLLoader
* loader
,
143 const WebURLResponse
& response
) {
144 // Sets -1 if the content length is unknown. Send before issuing callback.
145 total_bytes_to_be_received_
= response
.expectedContentLength();
148 SaveResponse(response
);
151 void PepperURLLoaderHost::didDownloadData(WebURLLoader
* loader
,
153 int encoded_data_length
) {
154 bytes_received_
+= data_length
;
158 void PepperURLLoaderHost::didReceiveData(WebURLLoader
* loader
,
161 int encoded_data_length
) {
162 // Note that |loader| will be NULL for document loads.
163 bytes_received_
+= data_length
;
166 PpapiPluginMsg_URLLoader_SendData
* message
=
167 new PpapiPluginMsg_URLLoader_SendData
;
168 message
->WriteData(data
, data_length
);
169 SendUpdateToPlugin(message
);
172 void PepperURLLoaderHost::didFinishLoading(WebURLLoader
* loader
,
174 int64_t total_encoded_data_length
) {
175 // Note that |loader| will be NULL for document loads.
176 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(PP_OK
));
179 void PepperURLLoaderHost::didFail(WebURLLoader
* loader
,
180 const WebURLError
& error
) {
181 // Note that |loader| will be NULL for document loads.
182 int32_t pp_error
= PP_ERROR_FAILED
;
183 if (error
.domain
.equals(WebString::fromUTF8(net::kErrorDomain
))) {
184 // TODO(bbudge): Extend pp_errors.h to cover interesting network errors
185 // from the net error domain.
186 switch (error
.reason
) {
187 case net::ERR_ACCESS_DENIED
:
188 case net::ERR_NETWORK_ACCESS_DENIED
:
189 pp_error
= PP_ERROR_NOACCESS
;
193 // It's a WebKit error.
194 pp_error
= PP_ERROR_NOACCESS
;
196 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(pp_error
));
199 void PepperURLLoaderHost::DidConnectPendingHostToResource() {
200 for (size_t i
= 0; i
< pending_replies_
.size(); i
++)
201 host()->SendUnsolicitedReply(pp_resource(), *pending_replies_
[i
]);
202 pending_replies_
.clear();
205 int32_t PepperURLLoaderHost::OnHostMsgOpen(
206 ppapi::host::HostMessageContext
* context
,
207 const ppapi::URLRequestInfoData
& request_data
) {
208 // An "Open" isn't a resource Call so has no reply, but failure to open
209 // implies a load failure. To make it harder to forget to send the load
210 // failed reply from the open handler, we instead catch errors and convert
211 // them to load failed messages.
212 int32_t ret
= InternalOnHostMsgOpen(context
, request_data
);
213 DCHECK(ret
!= PP_OK_COMPLETIONPENDING
);
216 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(ret
));
220 // Since this is wrapped by OnHostMsgOpen, we can return errors here and they
221 // will be translated into a FinishedLoading call automatically.
222 int32_t PepperURLLoaderHost::InternalOnHostMsgOpen(
223 ppapi::host::HostMessageContext
* context
,
224 const ppapi::URLRequestInfoData
& request_data
) {
225 // Main document loads are already open, so don't allow people to open them
227 if (main_document_loader_
)
228 return PP_ERROR_INPROGRESS
;
230 // Create a copy of the request data since CreateWebURLRequest will populate
232 ppapi::URLRequestInfoData filled_in_request_data
= request_data
;
234 if (URLRequestRequiresUniversalAccess(filled_in_request_data
) &&
235 !has_universal_access_
) {
236 ppapi::PpapiGlobals::Get()->LogWithSource(
240 "PPB_URLLoader.Open: The URL you're requesting is "
241 " on a different security origin than your plugin. To request "
242 " cross-origin resources, see "
243 " PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS.");
244 return PP_ERROR_NOACCESS
;
248 return PP_ERROR_INPROGRESS
;
250 WebLocalFrame
* frame
= GetFrame();
252 return PP_ERROR_FAILED
;
254 WebURLRequest web_request
;
255 if (!CreateWebURLRequest(
256 pp_instance(), &filled_in_request_data
, frame
, &web_request
)) {
257 return PP_ERROR_FAILED
;
260 web_request
.setRequestContext(WebURLRequest::RequestContextPlugin
);
261 web_request
.setRequestorProcessID(renderer_ppapi_host_
->GetPluginPID());
262 // The requests from the plugins with private permission which can bypass same
263 // origin must skip the ServiceWorker.
264 web_request
.setSkipServiceWorker(
265 host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE
));
267 WebURLLoaderOptions options
;
268 if (has_universal_access_
) {
269 options
.allowCredentials
= true;
270 options
.crossOriginRequestPolicy
=
271 WebURLLoaderOptions::CrossOriginRequestPolicyAllow
;
273 // All other HTTP requests are untrusted.
274 options
.untrustedHTTP
= true;
275 if (filled_in_request_data
.allow_cross_origin_requests
) {
276 // Allow cross-origin requests with access control. The request specifies
277 // if credentials are to be sent.
278 options
.allowCredentials
= filled_in_request_data
.allow_credentials
;
279 options
.crossOriginRequestPolicy
=
280 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl
;
282 // Same-origin requests can always send credentials.
283 options
.allowCredentials
= true;
287 loader_
.reset(frame
->createAssociatedURLLoader(options
));
289 return PP_ERROR_FAILED
;
291 // Don't actually save the request until we know we're going to load.
292 request_data_
= filled_in_request_data
;
293 loader_
->loadAsynchronously(web_request
, this);
295 // Although the request is technically pending, this is not a "Call" message
296 // so we don't return COMPLETIONPENDING.
300 int32_t PepperURLLoaderHost::OnHostMsgSetDeferLoading(
301 ppapi::host::HostMessageContext
* context
,
302 bool defers_loading
) {
303 SetDefersLoading(defers_loading
);
307 int32_t PepperURLLoaderHost::OnHostMsgClose(
308 ppapi::host::HostMessageContext
* context
) {
313 int32_t PepperURLLoaderHost::OnHostMsgGrantUniversalAccess(
314 ppapi::host::HostMessageContext
* context
) {
315 // Only plugins with private permission can bypass same origin.
316 if (!host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE
))
317 return PP_ERROR_FAILED
;
318 has_universal_access_
= true;
322 void PepperURLLoaderHost::SendUpdateToPlugin(IPC::Message
* message
) {
323 // We must send messages to the plugin in the order that the responses are
324 // received from webkit, even when the host isn't ready to send messages or
325 // when the host performs an asynchronous operation.
327 // Only {FinishedLoading, ReceivedResponse, SendData} have ordering
328 // contraints; all other messages are immediately added to pending_replies_.
330 // Accepted orderings for {FinishedLoading, ReceivedResponse, SendData} are:
331 // - {ReceivedResponse, SendData (zero or more times), FinishedLoading}
332 // - {FinishedLoading (when status != PP_OK)}
333 if (message
->type() == PpapiPluginMsg_URLLoader_SendData::ID
||
334 message
->type() == PpapiPluginMsg_URLLoader_FinishedLoading::ID
) {
335 // Messages that must be sent after ReceivedResponse.
336 if (pending_response_
) {
337 out_of_order_replies_
.push_back(message
);
339 SendOrderedUpdateToPlugin(message
);
341 } else if (message
->type() == PpapiPluginMsg_URLLoader_ReceivedResponse::ID
) {
342 // Allow SendData and FinishedLoading into the ordered queue.
343 DCHECK(pending_response_
);
344 SendOrderedUpdateToPlugin(message
);
345 for (size_t i
= 0; i
< out_of_order_replies_
.size(); i
++)
346 SendOrderedUpdateToPlugin(out_of_order_replies_
[i
]);
347 // SendOrderedUpdateToPlugin destroys the messages for us.
348 out_of_order_replies_
.weak_clear();
349 pending_response_
= false;
351 // Messages without ordering constraints.
352 SendOrderedUpdateToPlugin(message
);
356 void PepperURLLoaderHost::SendOrderedUpdateToPlugin(IPC::Message
* message
) {
357 if (pp_resource() == 0) {
358 pending_replies_
.push_back(message
);
360 host()->SendUnsolicitedReply(pp_resource(), *message
);
365 void PepperURLLoaderHost::Close() {
368 } else if (main_document_loader_
) {
369 // TODO(raymes): Calling WebLocalFrame::stopLoading here is incorrect as it
370 // cancels all URL loaders associated with the frame. If a client has opened
371 // other URLLoaders and then closes the main one, the others should still
372 // remain connected. Work out how to only cancel the main request:
374 blink::WebLocalFrame
* frame
= GetFrame();
376 frame
->stopLoading();
380 blink::WebLocalFrame
* PepperURLLoaderHost::GetFrame() {
381 PepperPluginInstanceImpl
* instance_object
=
382 static_cast<PepperPluginInstanceImpl
*>(
383 renderer_ppapi_host_
->GetPluginInstance(pp_instance()));
384 if (!instance_object
|| instance_object
->is_deleted())
386 return instance_object
->GetContainer()->element().document().frame();
389 void PepperURLLoaderHost::SetDefersLoading(bool defers_loading
) {
391 loader_
->setDefersLoading(defers_loading
);
393 // TODO(brettw) bug 96770: We need a way to set the defers loading flag on
394 // main document loads (when the loader_ is null).
397 void PepperURLLoaderHost::SaveResponse(const WebURLResponse
& response
) {
398 // When we're the main document loader, we send the response data up front,
399 // so we don't want to trigger any callbacks in the plugin which aren't
400 // expected. We should not be getting redirects so the response sent
401 // up-front should be valid (plugin document loads happen after all
402 // redirects are processed since WebKit has to know the MIME type).
403 if (!main_document_loader_
) {
404 // We note when there's a callback in flight for a response to ensure that
405 // messages we send to the plugin are not sent out of order. See
406 // SendUpdateToPlugin() for more details.
407 DCHECK(!pending_response_
);
408 pending_response_
= true;
410 DataFromWebURLResponse(
411 renderer_ppapi_host_
,
414 base::Bind(&PepperURLLoaderHost::DidDataFromWebURLResponse
,
415 weak_factory_
.GetWeakPtr()));
419 void PepperURLLoaderHost::DidDataFromWebURLResponse(
420 const ppapi::URLResponseInfoData
& data
) {
421 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_ReceivedResponse(data
));
424 void PepperURLLoaderHost::UpdateProgress() {
425 bool record_download
= request_data_
.record_download_progress
;
426 bool record_upload
= request_data_
.record_upload_progress
;
427 if (record_download
|| record_upload
) {
428 // Here we go through some effort to only send the exact information that
429 // the requestor wanted in the request flags. It would be just as
430 // efficient to send all of it, but we don't want people to rely on
431 // getting download progress when they happen to set the upload progress
433 ppapi::proxy::ResourceMessageReplyParams params
;
434 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_UpdateProgress(
435 record_upload
? bytes_sent_
: -1,
436 record_upload
? total_bytes_to_be_sent_
: -1,
437 record_download
? bytes_received_
: -1,
438 record_download
? total_bytes_to_be_received_
: -1));
442 } // namespace content