Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / pepper_url_loader_host.cc
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.
4
5 #include "content/renderer/pepper/pepper_url_loader_host.h"
6
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/WebFrame.h"
25 #include "third_party/WebKit/public/web/WebKit.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"
29
30 using blink::WebFrame;
31 using blink::WebString;
32 using blink::WebURL;
33 using blink::WebURLError;
34 using blink::WebURLLoader;
35 using blink::WebURLLoaderOptions;
36 using blink::WebURLRequest;
37 using blink::WebURLResponse;
38
39 #ifdef _MSC_VER
40 // Do not warn about use of std::copy with raw pointers.
41 #pragma warning(disable : 4996)
42 #endif
43
44 namespace content {
45
46 PepperURLLoaderHost::PepperURLLoaderHost(RendererPpapiHostImpl* host,
47                                          bool main_document_loader,
48                                          PP_Instance instance,
49                                          PP_Resource resource)
50     : ResourceHost(host->GetPpapiHost(), instance, resource),
51       renderer_ppapi_host_(host),
52       main_document_loader_(main_document_loader),
53       has_universal_access_(false),
54       bytes_sent_(0),
55       total_bytes_to_be_sent_(-1),
56       bytes_received_(0),
57       total_bytes_to_be_received_(-1),
58       pending_response_(false),
59       weak_factory_(this) {
60   DCHECK((main_document_loader && !resource) ||
61          (!main_document_loader && resource));
62 }
63
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.
68   //
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).
73   //
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.
79   //
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);
93     }
94   }
95
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());
102 }
103
104 int32_t PepperURLLoaderHost::OnResourceMessageReceived(
105     const IPC::Message& msg,
106     ppapi::host::HostMessageContext* context) {
107   IPC_BEGIN_MESSAGE_MAP(PepperURLLoaderHost, msg)
108     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
109         PpapiHostMsg_URLLoader_Open,
110         OnHostMsgOpen)
111     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
112         PpapiHostMsg_URLLoader_SetDeferLoading,
113         OnHostMsgSetDeferLoading)
114     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
115         PpapiHostMsg_URLLoader_Close,
116         OnHostMsgClose);
117     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
118         PpapiHostMsg_URLLoader_GrantUniversalAccess,
119         OnHostMsgGrantUniversalAccess)
120   IPC_END_MESSAGE_MAP()
121   return PP_ERROR_FAILED;
122 }
123
124 void PepperURLLoaderHost::willSendRequest(
125     WebURLLoader* loader,
126     WebURLRequest& new_request,
127     const WebURLResponse& redirect_response) {
128   DCHECK(out_of_order_replies_.empty());
129   if (!request_data_.follow_redirects) {
130     SaveResponse(redirect_response);
131     SetDefersLoading(true);
132   }
133 }
134
135 void PepperURLLoaderHost::didSendData(
136     WebURLLoader* loader,
137     unsigned long long bytes_sent,
138     unsigned long long total_bytes_to_be_sent) {
139   // TODO(darin): Bounds check input?
140   bytes_sent_ = static_cast<int64_t>(bytes_sent);
141   total_bytes_to_be_sent_ = static_cast<int64_t>(total_bytes_to_be_sent);
142   UpdateProgress();
143 }
144
145 void PepperURLLoaderHost::didReceiveResponse(WebURLLoader* loader,
146                                              const WebURLResponse& response) {
147   // Sets -1 if the content length is unknown. Send before issuing callback.
148   total_bytes_to_be_received_ = response.expectedContentLength();
149   UpdateProgress();
150
151   SaveResponse(response);
152 }
153
154 void PepperURLLoaderHost::didDownloadData(WebURLLoader* loader,
155                                           int data_length,
156                                           int encoded_data_length) {
157   bytes_received_ += data_length;
158   UpdateProgress();
159 }
160
161 void PepperURLLoaderHost::didReceiveData(WebURLLoader* loader,
162                                          const char* data,
163                                          int data_length,
164                                          int encoded_data_length) {
165   // Note that |loader| will be NULL for document loads.
166   bytes_received_ += data_length;
167   UpdateProgress();
168
169   PpapiPluginMsg_URLLoader_SendData* message =
170       new PpapiPluginMsg_URLLoader_SendData;
171   message->WriteData(data, data_length);
172   SendUpdateToPlugin(message);
173 }
174
175 void PepperURLLoaderHost::didFinishLoading(WebURLLoader* loader,
176                                            double finish_time,
177                                            int64_t total_encoded_data_length) {
178   // Note that |loader| will be NULL for document loads.
179   SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(PP_OK));
180 }
181
182 void PepperURLLoaderHost::didFail(WebURLLoader* loader,
183                                  const WebURLError& error) {
184   // Note that |loader| will be NULL for document loads.
185   int32_t pp_error = PP_ERROR_FAILED;
186   if (error.domain.equals(WebString::fromUTF8(net::kErrorDomain))) {
187     // TODO(bbudge): Extend pp_errors.h to cover interesting network errors
188     // from the net error domain.
189     switch (error.reason) {
190       case net::ERR_ACCESS_DENIED:
191       case net::ERR_NETWORK_ACCESS_DENIED:
192         pp_error = PP_ERROR_NOACCESS;
193         break;
194     }
195   } else {
196     // It's a WebKit error.
197     pp_error = PP_ERROR_NOACCESS;
198   }
199   SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(pp_error));
200 }
201
202 void PepperURLLoaderHost::DidConnectPendingHostToResource() {
203   for (size_t i = 0; i < pending_replies_.size(); i++)
204     host()->SendUnsolicitedReply(pp_resource(), *pending_replies_[i]);
205   pending_replies_.clear();
206 }
207
208 int32_t PepperURLLoaderHost::OnHostMsgOpen(
209     ppapi::host::HostMessageContext* context,
210     const ppapi::URLRequestInfoData& request_data) {
211   // An "Open" isn't a resource Call so has no reply, but failure to open
212   // implies a load failure. To make it harder to forget to send the load
213   // failed reply from the open handler, we instead catch errors and convert
214   // them to load failed messages.
215   int32_t ret = InternalOnHostMsgOpen(context, request_data);
216   DCHECK(ret != PP_OK_COMPLETIONPENDING);
217
218   if (ret != PP_OK)
219     SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(ret));
220   return PP_OK;
221 }
222
223 // Since this is wrapped by OnHostMsgOpen, we can return errors here and they
224 // will be translated into a FinishedLoading call automatically.
225 int32_t PepperURLLoaderHost::InternalOnHostMsgOpen(
226     ppapi::host::HostMessageContext* context,
227     const ppapi::URLRequestInfoData& request_data) {
228   // Main document loads are already open, so don't allow people to open them
229   // again.
230   if (main_document_loader_)
231     return PP_ERROR_INPROGRESS;
232
233   // Create a copy of the request data since CreateWebURLRequest will populate
234   // the file refs.
235   ppapi::URLRequestInfoData filled_in_request_data = request_data;
236
237   if (URLRequestRequiresUniversalAccess(filled_in_request_data) &&
238       !has_universal_access_) {
239     ppapi::PpapiGlobals::Get()->LogWithSource(
240         pp_instance(), PP_LOGLEVEL_ERROR, std::string(),
241         "PPB_URLLoader.Open: The URL you're requesting is "
242         " on a different security origin than your plugin. To request "
243         " cross-origin resources, see "
244         " PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS.");
245     return PP_ERROR_NOACCESS;
246   }
247
248   if (loader_.get())
249     return PP_ERROR_INPROGRESS;
250
251   WebFrame* frame = GetFrame();
252   if (!frame)
253     return PP_ERROR_FAILED;
254
255   WebURLRequest web_request;
256   if (!CreateWebURLRequest(pp_instance(),
257                            &filled_in_request_data,
258                            frame,
259                            &web_request)) {
260     return PP_ERROR_FAILED;
261   }
262
263   web_request.setTargetType(WebURLRequest::TargetIsObject);
264   web_request.setRequestorProcessID(renderer_ppapi_host_->GetPluginPID());
265
266   WebURLLoaderOptions options;
267   if (has_universal_access_) {
268     options.allowCredentials = true;
269     options.crossOriginRequestPolicy =
270         WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
271   } else {
272     // All other HTTP requests are untrusted.
273     options.untrustedHTTP = true;
274     if (filled_in_request_data.allow_cross_origin_requests) {
275       // Allow cross-origin requests with access control. The request specifies
276       // if credentials are to be sent.
277       options.allowCredentials = filled_in_request_data.allow_credentials;
278       options.crossOriginRequestPolicy =
279           WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
280     } else {
281       // Same-origin requests can always send credentials.
282       options.allowCredentials = true;
283     }
284   }
285
286   loader_.reset(frame->createAssociatedURLLoader(options));
287   if (!loader_.get())
288     return PP_ERROR_FAILED;
289
290   // Don't actually save the request until we know we're going to load.
291   request_data_ = filled_in_request_data;
292   loader_->loadAsynchronously(web_request, this);
293
294   // Although the request is technically pending, this is not a "Call" message
295   // so we don't return COMPLETIONPENDING.
296   return PP_OK;
297 }
298
299 int32_t PepperURLLoaderHost::OnHostMsgSetDeferLoading(
300     ppapi::host::HostMessageContext* context,
301     bool defers_loading) {
302   SetDefersLoading(defers_loading);
303   return PP_OK;
304 }
305
306 int32_t PepperURLLoaderHost::OnHostMsgClose(
307     ppapi::host::HostMessageContext* context) {
308   Close();
309   return PP_OK;
310 }
311
312 int32_t PepperURLLoaderHost::OnHostMsgGrantUniversalAccess(
313     ppapi::host::HostMessageContext* context) {
314   // Only plugins with private permission can bypass same origin.
315   if (!host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE))
316     return PP_ERROR_FAILED;
317   has_universal_access_ = true;
318   return PP_OK;
319 }
320
321 void PepperURLLoaderHost::SendUpdateToPlugin(IPC::Message* message) {
322   // We must send messages to the plugin in the order that the responses are
323   // received from webkit, even when the host isn't ready to send messages or
324   // when the host performs an asynchronous operation.
325   //
326   // Only {FinishedLoading, ReceivedResponse, SendData} have ordering
327   // contraints; all other messages are immediately added to pending_replies_.
328   //
329   // Accepted orderings for {FinishedLoading, ReceivedResponse, SendData} are:
330   //   - {ReceivedResponse, SendData (zero or more times), FinishedLoading}
331   //   - {FinishedLoading (when status != PP_OK)}
332   if (message->type() == PpapiPluginMsg_URLLoader_SendData::ID ||
333       message->type() == PpapiPluginMsg_URLLoader_FinishedLoading::ID) {
334     // Messages that must be sent after ReceivedResponse.
335     if (pending_response_) {
336       out_of_order_replies_.push_back(message);
337     } else {
338       SendOrderedUpdateToPlugin(message);
339     }
340   } else if (message->type() == PpapiPluginMsg_URLLoader_ReceivedResponse::ID) {
341     // Allow SendData and FinishedLoading into the ordered queue.
342     DCHECK(pending_response_);
343     SendOrderedUpdateToPlugin(message);
344     for (size_t i = 0; i < out_of_order_replies_.size(); i++)
345       SendOrderedUpdateToPlugin(out_of_order_replies_[i]);
346     // SendOrderedUpdateToPlugin destroys the messages for us.
347     out_of_order_replies_.weak_clear();
348     pending_response_ = false;
349   } else {
350     // Messages without ordering constraints.
351     SendOrderedUpdateToPlugin(message);
352   }
353 }
354
355 void PepperURLLoaderHost::SendOrderedUpdateToPlugin(IPC::Message* message) {
356   if (pp_resource() == 0) {
357     pending_replies_.push_back(message);
358   } else {
359     host()->SendUnsolicitedReply(pp_resource(), *message);
360     delete message;
361   }
362 }
363
364 void PepperURLLoaderHost::Close() {
365   if (loader_.get())
366     loader_->cancel();
367   else if (main_document_loader_)
368     GetFrame()->stopLoading();
369 }
370
371 blink::WebFrame* PepperURLLoaderHost::GetFrame() {
372   PepperPluginInstance* instance_object =
373       renderer_ppapi_host_->GetPluginInstance(pp_instance());
374   if (!instance_object)
375     return NULL;
376   return instance_object->GetContainer()->element().document().frame();
377 }
378
379 void PepperURLLoaderHost::SetDefersLoading(bool defers_loading) {
380   if (loader_.get())
381     loader_->setDefersLoading(defers_loading);
382
383   // TODO(brettw) bug 96770: We need a way to set the defers loading flag on
384   // main document loads (when the loader_ is null).
385 }
386
387 void PepperURLLoaderHost::SaveResponse(const WebURLResponse& response) {
388   // When we're the main document loader, we send the response data up front,
389   // so we don't want to trigger any callbacks in the plugin which aren't
390   // expected. We should not be getting redirects so the response sent
391   // up-front should be valid (plugin document loads happen after all
392   // redirects are processed since WebKit has to know the MIME type).
393   if (!main_document_loader_) {
394     // We note when there's a callback in flight for a response to ensure that
395     // messages we send to the plugin are not sent out of order. See
396     // SendUpdateToPlugin() for more details.
397     DCHECK(!pending_response_);
398     pending_response_ = true;
399
400     DataFromWebURLResponse(
401         renderer_ppapi_host_,
402         pp_instance(),
403         response,
404         base::Bind(&PepperURLLoaderHost::DidDataFromWebURLResponse,
405             weak_factory_.GetWeakPtr()));
406   }
407 }
408
409 void PepperURLLoaderHost::DidDataFromWebURLResponse(
410     const ppapi::URLResponseInfoData& data) {
411   SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_ReceivedResponse(data));
412 }
413
414 void PepperURLLoaderHost::UpdateProgress() {
415   bool record_download = request_data_.record_download_progress;
416   bool record_upload = request_data_.record_upload_progress;
417   if (record_download || record_upload) {
418     // Here we go through some effort to only send the exact information that
419     // the requestor wanted in the request flags. It would be just as
420     // efficient to send all of it, but we don't want people to rely on
421     // getting download progress when they happen to set the upload progress
422     // flag.
423     ppapi::proxy::ResourceMessageReplyParams params;
424     SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_UpdateProgress(
425             record_upload ? bytes_sent_ : -1,
426             record_upload ? total_bytes_to_be_sent_ : -1,
427             record_download ? bytes_received_ : -1,
428             record_download ? total_bytes_to_be_received_ : -1));
429   }
430 }
431
432 }  // namespace content