Upstream version 11.40.277.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/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"
29
30 using blink::WebLocalFrame;
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   PPAPI_BEGIN_MESSAGE_MAP(PepperURLLoaderHost, msg)
108     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_URLLoader_Open,
109                                       OnHostMsgOpen)
110     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_URLLoader_SetDeferLoading,
111                                       OnHostMsgSetDeferLoading)
112     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_URLLoader_Close,
113                                         OnHostMsgClose);
114     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
115         PpapiHostMsg_URLLoader_GrantUniversalAccess,
116         OnHostMsgGrantUniversalAccess)
117   PPAPI_END_MESSAGE_MAP()
118   return PP_ERROR_FAILED;
119 }
120
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);
129   }
130 }
131
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);
139   UpdateProgress();
140 }
141
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();
146   UpdateProgress();
147
148   SaveResponse(response);
149 }
150
151 void PepperURLLoaderHost::didDownloadData(WebURLLoader* loader,
152                                           int data_length,
153                                           int encoded_data_length) {
154   bytes_received_ += data_length;
155   UpdateProgress();
156 }
157
158 void PepperURLLoaderHost::didReceiveData(WebURLLoader* loader,
159                                          const char* data,
160                                          int data_length,
161                                          int encoded_data_length) {
162   // Note that |loader| will be NULL for document loads.
163   bytes_received_ += data_length;
164   UpdateProgress();
165
166   PpapiPluginMsg_URLLoader_SendData* message =
167       new PpapiPluginMsg_URLLoader_SendData;
168   message->WriteData(data, data_length);
169   SendUpdateToPlugin(message);
170 }
171
172 void PepperURLLoaderHost::didFinishLoading(WebURLLoader* loader,
173                                            double finish_time,
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));
177 }
178
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;
190         break;
191     }
192   } else {
193     // It's a WebKit error.
194     pp_error = PP_ERROR_NOACCESS;
195   }
196   SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(pp_error));
197 }
198
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();
203 }
204
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);
214
215   if (ret != PP_OK)
216     SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(ret));
217   return PP_OK;
218 }
219
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
226   // again.
227   if (main_document_loader_)
228     return PP_ERROR_INPROGRESS;
229
230   // Create a copy of the request data since CreateWebURLRequest will populate
231   // the file refs.
232   ppapi::URLRequestInfoData filled_in_request_data = request_data;
233
234   if (URLRequestRequiresUniversalAccess(filled_in_request_data) &&
235       !has_universal_access_) {
236     ppapi::PpapiGlobals::Get()->LogWithSource(
237         pp_instance(),
238         PP_LOGLEVEL_ERROR,
239         std::string(),
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;
245   }
246
247   if (loader_.get())
248     return PP_ERROR_INPROGRESS;
249
250   WebLocalFrame* frame = GetFrame();
251   if (!frame)
252     return PP_ERROR_FAILED;
253
254   WebURLRequest web_request;
255   if (!CreateWebURLRequest(
256           pp_instance(), &filled_in_request_data, frame, &web_request)) {
257     return PP_ERROR_FAILED;
258   }
259
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));
266
267   WebURLLoaderOptions options;
268   if (has_universal_access_) {
269     options.allowCredentials = true;
270     options.crossOriginRequestPolicy =
271         WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
272   } else {
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;
281     } else {
282       // Same-origin requests can always send credentials.
283       options.allowCredentials = true;
284     }
285   }
286
287   loader_.reset(frame->createAssociatedURLLoader(options));
288   if (!loader_.get())
289     return PP_ERROR_FAILED;
290
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);
294
295   // Although the request is technically pending, this is not a "Call" message
296   // so we don't return COMPLETIONPENDING.
297   return PP_OK;
298 }
299
300 int32_t PepperURLLoaderHost::OnHostMsgSetDeferLoading(
301     ppapi::host::HostMessageContext* context,
302     bool defers_loading) {
303   SetDefersLoading(defers_loading);
304   return PP_OK;
305 }
306
307 int32_t PepperURLLoaderHost::OnHostMsgClose(
308     ppapi::host::HostMessageContext* context) {
309   Close();
310   return PP_OK;
311 }
312
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;
319   return PP_OK;
320 }
321
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.
326   //
327   // Only {FinishedLoading, ReceivedResponse, SendData} have ordering
328   // contraints; all other messages are immediately added to pending_replies_.
329   //
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);
338     } else {
339       SendOrderedUpdateToPlugin(message);
340     }
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;
350   } else {
351     // Messages without ordering constraints.
352     SendOrderedUpdateToPlugin(message);
353   }
354 }
355
356 void PepperURLLoaderHost::SendOrderedUpdateToPlugin(IPC::Message* message) {
357   if (pp_resource() == 0) {
358     pending_replies_.push_back(message);
359   } else {
360     host()->SendUnsolicitedReply(pp_resource(), *message);
361     delete message;
362   }
363 }
364
365 void PepperURLLoaderHost::Close() {
366   if (loader_.get()) {
367     loader_->cancel();
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:
373     // crbug.com/384197.
374     blink::WebLocalFrame* frame = GetFrame();
375     if (frame)
376       frame->stopLoading();
377   }
378 }
379
380 blink::WebLocalFrame* PepperURLLoaderHost::GetFrame() {
381   PepperPluginInstance* instance_object =
382       renderer_ppapi_host_->GetPluginInstance(pp_instance());
383   if (!instance_object)
384     return NULL;
385   return instance_object->GetContainer()->element().document().frame();
386 }
387
388 void PepperURLLoaderHost::SetDefersLoading(bool defers_loading) {
389   if (loader_.get())
390     loader_->setDefersLoading(defers_loading);
391
392   // TODO(brettw) bug 96770: We need a way to set the defers loading flag on
393   // main document loads (when the loader_ is null).
394 }
395
396 void PepperURLLoaderHost::SaveResponse(const WebURLResponse& response) {
397   // When we're the main document loader, we send the response data up front,
398   // so we don't want to trigger any callbacks in the plugin which aren't
399   // expected. We should not be getting redirects so the response sent
400   // up-front should be valid (plugin document loads happen after all
401   // redirects are processed since WebKit has to know the MIME type).
402   if (!main_document_loader_) {
403     // We note when there's a callback in flight for a response to ensure that
404     // messages we send to the plugin are not sent out of order. See
405     // SendUpdateToPlugin() for more details.
406     DCHECK(!pending_response_);
407     pending_response_ = true;
408
409     DataFromWebURLResponse(
410         renderer_ppapi_host_,
411         pp_instance(),
412         response,
413         base::Bind(&PepperURLLoaderHost::DidDataFromWebURLResponse,
414                    weak_factory_.GetWeakPtr()));
415   }
416 }
417
418 void PepperURLLoaderHost::DidDataFromWebURLResponse(
419     const ppapi::URLResponseInfoData& data) {
420   SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_ReceivedResponse(data));
421 }
422
423 void PepperURLLoaderHost::UpdateProgress() {
424   bool record_download = request_data_.record_download_progress;
425   bool record_upload = request_data_.record_upload_progress;
426   if (record_download || record_upload) {
427     // Here we go through some effort to only send the exact information that
428     // the requestor wanted in the request flags. It would be just as
429     // efficient to send all of it, but we don't want people to rely on
430     // getting download progress when they happen to set the upload progress
431     // flag.
432     ppapi::proxy::ResourceMessageReplyParams params;
433     SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_UpdateProgress(
434         record_upload ? bytes_sent_ : -1,
435         record_upload ? total_bytes_to_be_sent_ : -1,
436         record_download ? bytes_received_ : -1,
437         record_download ? total_bytes_to_be_received_ : -1));
438   }
439 }
440
441 }  // namespace content