[M108 Aura Migration][NaCl][PPFwk] Add error logs + SVACE/DLOG/Static analysis fix
[platform/framework/web/chromium-efl.git] / content / renderer / pepper / pepper_url_loader_host.cc
1 // Copyright 2013 The Chromium Authors
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 <stddef.h>
8
9 #include "base/feature_list.h"
10 #include "base/logging.h"
11 #include "base/numerics/safe_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "content/public/common/content_features.h"
14 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
15 #include "content/renderer/pepper/renderer_ppapi_host_impl.h"
16 #include "content/renderer/pepper/url_request_info_util.h"
17 #include "content/renderer/pepper/url_response_info_util.h"
18 #include "net/base/net_errors.h"
19 #include "net/http/http_request_headers.h"
20 #include "ppapi/c/pp_errors.h"
21 #include "ppapi/host/dispatch_host_message.h"
22 #include "ppapi/host/host_message_context.h"
23 #include "ppapi/host/ppapi_host.h"
24 #include "ppapi/proxy/ppapi_messages.h"
25 #include "ppapi/shared_impl/ppapi_globals.h"
26 #include "third_party/blink/public/platform/web_security_origin.h"
27 #include "third_party/blink/public/platform/web_url_error.h"
28 #include "third_party/blink/public/platform/web_url_request.h"
29 #include "third_party/blink/public/platform/web_url_response.h"
30 #include "third_party/blink/public/web/blink.h"
31 #include "third_party/blink/public/web/web_associated_url_loader.h"
32 #include "third_party/blink/public/web/web_associated_url_loader_options.h"
33 #include "third_party/blink/public/web/web_document.h"
34 #include "third_party/blink/public/web/web_element.h"
35 #include "third_party/blink/public/web/web_local_frame.h"
36 #include "third_party/blink/public/web/web_plugin_container.h"
37
38 using blink::WebAssociatedURLLoader;
39 using blink::WebAssociatedURLLoaderOptions;
40 using blink::WebLocalFrame;
41 using blink::WebString;
42 using blink::WebURL;
43 using blink::WebURLError;
44 using blink::WebURLRequest;
45 using blink::WebURLResponse;
46
47 #ifdef _MSC_VER
48 // Do not warn about use of std::copy with raw pointers.
49 #pragma warning(disable : 4996)
50 #endif
51
52 namespace content {
53
54 PepperURLLoaderHost::PepperURLLoaderHost(RendererPpapiHostImpl* host,
55                                          bool main_document_loader,
56                                          PP_Instance instance,
57                                          PP_Resource resource)
58     : ResourceHost(host->GetPpapiHost(), instance, resource),
59       renderer_ppapi_host_(host),
60       main_document_loader_(main_document_loader),
61       has_universal_access_(false),
62       bytes_sent_(0),
63       total_bytes_to_be_sent_(-1),
64       bytes_received_(0),
65       total_bytes_to_be_received_(-1),
66       pending_response_(false) {
67   DCHECK((main_document_loader && !resource) ||
68          (!main_document_loader && resource));
69 }
70
71 PepperURLLoaderHost::~PepperURLLoaderHost() {
72   // Normally deleting this object will delete the loader which will implicitly
73   // cancel the load. But this won't happen for the main document loader. So it
74   // would be nice to issue a Close() here.
75   //
76   // However, the PDF plugin will cancel the document load and then close the
77   // resource (which is reasonable). It then makes a second request to load the
78   // document so it can set the "want progress" flags (which is unreasonable --
79   // we should probably provide download progress on document loads).
80   //
81   // But a Close() on the main document (even if the request is already
82   // canceled) will cancel all pending subresources, of which the second
83   // request is one, and the load will fail. Even if we fixed the PDF reader to
84   // change the timing or to send progress events to avoid the second request,
85   // we don't want to cancel other loads when the main one is closed.
86   //
87   // "Leaking" the main document load here by not closing it will only affect
88   // plugins handling main document loads (which are very few, mostly only PDF)
89   // that dereference without explicitly closing the main document load (which
90   // PDF doesn't do -- it explicitly closes it before issuing the second
91   // request). And the worst thing that will happen is that any remaining data
92   // will get queued inside WebKit.
93   if (main_document_loader_) {
94     // The PluginInstance has a non-owning pointer to us.
95     PepperPluginInstanceImpl* instance_object =
96         renderer_ppapi_host_->GetPluginInstanceImpl(pp_instance());
97     if (instance_object) {
98       DCHECK(instance_object->document_loader() == this);
99       instance_object->set_document_loader(nullptr);
100     }
101   }
102
103   // There is a path whereby the destructor for the loader_ member can
104   // invoke InstanceWasDeleted() upon this URLLoaderResource, thereby
105   // re-entering the scoped_ptr destructor with the same scoped_ptr object
106   // via loader_.reset(). Be sure that loader_ is first NULL then destroy
107   // the scoped_ptr. See http://crbug.com/159429.
108   std::unique_ptr<WebAssociatedURLLoader> for_destruction_only(
109       loader_.release());
110 }
111
112 int32_t PepperURLLoaderHost::OnResourceMessageReceived(
113     const IPC::Message& msg,
114     ppapi::host::HostMessageContext* context) {
115   PPAPI_BEGIN_MESSAGE_MAP(PepperURLLoaderHost, msg)
116     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_URLLoader_Open,
117                                       OnHostMsgOpen)
118     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_URLLoader_SetDeferLoading,
119                                       OnHostMsgSetDeferLoading)
120     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_URLLoader_Close,
121                                         OnHostMsgClose);
122     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
123         PpapiHostMsg_URLLoader_GrantUniversalAccess,
124         OnHostMsgGrantUniversalAccess)
125   PPAPI_END_MESSAGE_MAP()
126   LOG(ERROR) << "Resource message unresolved";
127   return PP_ERROR_FAILED;
128 }
129
130 bool PepperURLLoaderHost::WillFollowRedirect(
131     const WebURL& new_url,
132     const WebURLResponse& redirect_response) {
133   DCHECK(out_of_order_replies_.empty());
134   if (base::FeatureList::IsEnabled(
135           features::kPepperCrossOriginRedirectRestriction)) {
136     // Follows the Firefox approach
137     // (https://bugzilla.mozilla.org/show_bug.cgi?id=1436241) to disallow
138     // cross-origin 307/308 POST redirects for requests from plugins. But we try
139     // allowing only GET and HEAD methods rather than disallowing POST.
140     // See http://crbug.com/332023 for details.
141     int status = redirect_response.HttpStatusCode();
142     if ((status == 307 || status == 308)) {
143       std::string method = base::ToUpperASCII(request_data_.method);
144       // method can be an empty string for default behavior, GET.
145       if (!method.empty() && method != net::HttpRequestHeaders::kGetMethod &&
146           method != net::HttpRequestHeaders::kHeadMethod) {
147         return false;
148       }
149     }
150   }
151
152   if (!request_data_.follow_redirects) {
153     SaveResponse(redirect_response);
154     SetDefersLoading(true);
155     // Defer the request and wait the plugin to audit the redirect. We
156     // shouldn't return false here as decision has been delegated to the
157     // plugin.
158   }
159   return true;
160 }
161
162 void PepperURLLoaderHost::DidSendData(uint64_t bytes_sent,
163                                       uint64_t total_bytes_to_be_sent) {
164   // TODO(darin): Bounds check input?
165   bytes_sent_ = static_cast<int64_t>(bytes_sent);
166   total_bytes_to_be_sent_ = static_cast<int64_t>(total_bytes_to_be_sent);
167   UpdateProgress();
168 }
169
170 void PepperURLLoaderHost::DidReceiveResponse(const WebURLResponse& response) {
171   // Sets -1 if the content length is unknown. Send before issuing callback.
172   total_bytes_to_be_received_ = response.ExpectedContentLength();
173   UpdateProgress();
174
175   SaveResponse(response);
176 }
177
178 void PepperURLLoaderHost::DidDownloadData(uint64_t data_length) {
179   bytes_received_ += data_length;
180   UpdateProgress();
181 }
182
183 void PepperURLLoaderHost::DidReceiveData(const char* data, int data_length) {
184   // Note that |loader| will be NULL for document loads.
185   bytes_received_ += data_length;
186   UpdateProgress();
187
188   auto message = std::make_unique<PpapiPluginMsg_URLLoader_SendData>();
189   message->WriteData(data, base::checked_cast<size_t>(data_length));
190   SendUpdateToPlugin(std::move(message));
191 }
192
193 void PepperURLLoaderHost::DidFinishLoading() {
194   // Note that |loader| will be NULL for document loads.
195   SendUpdateToPlugin(
196       std::make_unique<PpapiPluginMsg_URLLoader_FinishedLoading>(PP_OK));
197 }
198
199 void PepperURLLoaderHost::DidFail(const WebURLError& error) {
200   // Note that |loader| will be NULL for document loads.
201   int32_t pp_error = PP_ERROR_FAILED;
202   // TODO(bbudge): Extend pp_errors.h to cover interesting network errors
203   // from the net error domain.
204   switch (error.reason()) {
205     case net::ERR_ACCESS_DENIED:
206     case net::ERR_NETWORK_ACCESS_DENIED:
207       pp_error = PP_ERROR_NOACCESS;
208       break;
209   }
210
211   if (error.is_web_security_violation())
212     pp_error = PP_ERROR_NOACCESS;
213   SendUpdateToPlugin(
214       std::make_unique<PpapiPluginMsg_URLLoader_FinishedLoading>(pp_error));
215 }
216
217 void PepperURLLoaderHost::DidConnectPendingHostToResource() {
218   for (const auto& reply : pending_replies_)
219     host()->SendUnsolicitedReply(pp_resource(), *reply);
220   pending_replies_.clear();
221 }
222
223 int32_t PepperURLLoaderHost::OnHostMsgOpen(
224     ppapi::host::HostMessageContext* context,
225     const ppapi::URLRequestInfoData& request_data) {
226   // An "Open" isn't a resource Call so has no reply, but failure to open
227   // implies a load failure. To make it harder to forget to send the load
228   // failed reply from the open handler, we instead catch errors and convert
229   // them to load failed messages.
230   int32_t ret = InternalOnHostMsgOpen(context, request_data);
231   DCHECK(ret != PP_OK_COMPLETIONPENDING);
232
233   if (ret != PP_OK)
234     SendUpdateToPlugin(
235         std::make_unique<PpapiPluginMsg_URLLoader_FinishedLoading>(ret));
236   return PP_OK;
237 }
238
239 // Since this is wrapped by OnHostMsgOpen, we can return errors here and they
240 // will be translated into a FinishedLoading call automatically.
241 int32_t PepperURLLoaderHost::InternalOnHostMsgOpen(
242     ppapi::host::HostMessageContext* context,
243     const ppapi::URLRequestInfoData& request_data) {
244   // Main document loads are already open, so don't allow people to open them
245   // again.
246   if (main_document_loader_) {
247     LOG(ERROR) << "Main document already opened - not allow to multiple opens";
248     return PP_ERROR_INPROGRESS;
249   }
250
251   // Create a copy of the request data since CreateWebURLRequest will populate
252   // the file refs.
253   ppapi::URLRequestInfoData filled_in_request_data = request_data;
254
255   if (URLRequestRequiresUniversalAccess(filled_in_request_data) &&
256       !has_universal_access_) {
257     ppapi::PpapiGlobals::Get()->LogWithSource(
258         pp_instance(),
259         PP_LOGLEVEL_ERROR,
260         std::string(),
261         "PPB_URLLoader.Open: The URL you're requesting is "
262         " on a different security origin than your plugin. To request "
263         " cross-origin resources, see "
264         " PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS.");
265     LOG(ERROR) << "The URL you're requesting is on a different security"
266                << " origin than your plugin";
267     return PP_ERROR_NOACCESS;
268   }
269
270   if (loader_.get()) {
271     LOG(ERROR) << "Open already in progress";
272     return PP_ERROR_INPROGRESS;
273   }
274
275   WebLocalFrame* frame = GetFrame();
276   if (!frame) {
277     LOG(ERROR) << "Frame is NULL";
278     return PP_ERROR_FAILED;
279   }
280
281   WebURLRequest web_request;
282   if (!CreateWebURLRequest(
283           pp_instance(), &filled_in_request_data, frame, &web_request)) {
284     LOG(ERROR) << "Cannot create WebURLRequest";
285     return PP_ERROR_FAILED;
286   }
287
288   // Requests from plug-ins must be marked as PLUGIN, and must skip service
289   // workers, see the comment in CreateWebURLRequest.
290   DCHECK_EQ(blink::mojom::RequestContextType::PLUGIN,
291             web_request.GetRequestContext());
292   DCHECK(web_request.GetSkipServiceWorker());
293
294   WebAssociatedURLLoaderOptions options;
295   if (has_universal_access_) {
296     options.grant_universal_access = true;
297   } else {
298     // All other HTTP requests are untrusted.
299     options.untrusted_http = true;
300     if (filled_in_request_data.allow_cross_origin_requests) {
301       // Allow cross-origin requests with access control. The request specifies
302       // if credentials are to be sent.
303       web_request.SetMode(network::mojom::RequestMode::kCors);
304       web_request.SetCredentialsMode(
305           filled_in_request_data.allow_credentials
306               ? network::mojom::CredentialsMode::kInclude
307               : network::mojom::CredentialsMode::kOmit);
308     } else {
309       web_request.SetMode(network::mojom::RequestMode::kSameOrigin);
310       // Same-origin requests can always send credentials. Use the default
311       // credentials mode "include".
312     }
313   }
314
315   loader_ = frame->CreateAssociatedURLLoader(options);
316   if (!loader_.get()) {
317     LOG(ERROR) << "Unsuccessful creation of AssociatedURLLoader";
318     return PP_ERROR_FAILED;
319   }
320
321   // Don't actually save the request until we know we're going to load.
322   request_data_ = filled_in_request_data;
323   loader_->LoadAsynchronously(web_request, this);
324
325   // Although the request is technically pending, this is not a "Call" message
326   // so we don't return COMPLETIONPENDING.
327   return PP_OK;
328 }
329
330 int32_t PepperURLLoaderHost::OnHostMsgSetDeferLoading(
331     ppapi::host::HostMessageContext* context,
332     bool defers_loading) {
333   SetDefersLoading(defers_loading);
334   return PP_OK;
335 }
336
337 int32_t PepperURLLoaderHost::OnHostMsgClose(
338     ppapi::host::HostMessageContext* context) {
339   Close();
340   return PP_OK;
341 }
342
343 int32_t PepperURLLoaderHost::OnHostMsgGrantUniversalAccess(
344     ppapi::host::HostMessageContext* context) {
345   // Only plugins with permission can bypass same origin.
346   if (host()->permissions().HasPermission(ppapi::PERMISSION_PDF)) {
347     has_universal_access_ = true;
348     return PP_OK;
349   }
350   LOG(ERROR) << "Only plugins with permission can bypass same origin";
351   return PP_ERROR_FAILED;
352 }
353
354 void PepperURLLoaderHost::SendUpdateToPlugin(
355     std::unique_ptr<IPC::Message> message) {
356   // We must send messages to the plugin in the order that the responses are
357   // received from webkit, even when the host isn't ready to send messages or
358   // when the host performs an asynchronous operation.
359   //
360   // Only {FinishedLoading, ReceivedResponse, SendData} have ordering
361   // contraints; all other messages are immediately added to pending_replies_.
362   //
363   // Accepted orderings for {FinishedLoading, ReceivedResponse, SendData} are:
364   //   - {ReceivedResponse, SendData (zero or more times), FinishedLoading}
365   //   - {FinishedLoading (when status != PP_OK)}
366   if (message->type() == PpapiPluginMsg_URLLoader_SendData::ID ||
367       message->type() == PpapiPluginMsg_URLLoader_FinishedLoading::ID) {
368     // Messages that must be sent after ReceivedResponse.
369     if (pending_response_) {
370       out_of_order_replies_.push_back(std::move(message));
371     } else {
372       SendOrderedUpdateToPlugin(std::move(message));
373     }
374   } else if (message->type() == PpapiPluginMsg_URLLoader_ReceivedResponse::ID) {
375     // Allow SendData and FinishedLoading into the ordered queue.
376     DCHECK(pending_response_);
377     SendOrderedUpdateToPlugin(std::move(message));
378     for (auto& reply : out_of_order_replies_)
379       SendOrderedUpdateToPlugin(std::move(reply));
380     out_of_order_replies_.clear();
381     pending_response_ = false;
382   } else {
383     // Messages without ordering constraints.
384     SendOrderedUpdateToPlugin(std::move(message));
385   }
386 }
387
388 void PepperURLLoaderHost::SendOrderedUpdateToPlugin(
389     std::unique_ptr<IPC::Message> message) {
390   if (pp_resource() == 0) {
391     pending_replies_.push_back(std::move(message));
392   } else {
393     host()->SendUnsolicitedReply(pp_resource(), *message);
394   }
395 }
396
397 void PepperURLLoaderHost::Close() {
398   if (loader_.get()) {
399     loader_->Cancel();
400   } else if (main_document_loader_) {
401     // TODO(raymes): Calling WebLocalFrame::stopLoading here is incorrect as it
402     // cancels all URL loaders associated with the frame. If a client has opened
403     // other URLLoaders and then closes the main one, the others should still
404     // remain connected. Work out how to only cancel the main request:
405     // crbug.com/384197.
406     WebLocalFrame* frame = GetFrame();
407     if (frame)
408       frame->DeprecatedStopLoading();
409   }
410 }
411
412 WebLocalFrame* PepperURLLoaderHost::GetFrame() {
413   PepperPluginInstanceImpl* instance_object =
414       static_cast<PepperPluginInstanceImpl*>(
415           renderer_ppapi_host_->GetPluginInstance(pp_instance()));
416   if (!instance_object || instance_object->is_deleted())
417     return nullptr;
418   return instance_object->GetContainer()->GetDocument().GetFrame();
419 }
420
421 void PepperURLLoaderHost::SetDefersLoading(bool defers_loading) {
422   if (loader_.get())
423     loader_->SetDefersLoading(defers_loading);
424
425   // TODO(brettw) bug 96770: We need a way to set the defers loading flag on
426   // main document loads (when the loader_ is null).
427 }
428
429 void PepperURLLoaderHost::SaveResponse(const WebURLResponse& response) {
430   // When we're the main document loader, we send the response data up front,
431   // so we don't want to trigger any callbacks in the plugin which aren't
432   // expected. We should not be getting redirects so the response sent
433   // up-front should be valid (plugin document loads happen after all
434   // redirects are processed since WebKit has to know the MIME type).
435   if (!main_document_loader_) {
436     // We note when there's a callback in flight for a response to ensure that
437     // messages we send to the plugin are not sent out of order. See
438     // SendUpdateToPlugin() for more details.
439     DCHECK(!pending_response_);
440     pending_response_ = true;
441
442     SendUpdateToPlugin(
443         std::make_unique<PpapiPluginMsg_URLLoader_ReceivedResponse>(
444             DataFromWebURLResponse(response)));
445   }
446 }
447
448 void PepperURLLoaderHost::UpdateProgress() {
449   bool record_download = request_data_.record_download_progress;
450   bool record_upload = request_data_.record_upload_progress;
451   if (record_download || record_upload) {
452     // Here we go through some effort to only send the exact information that
453     // the requestor wanted in the request flags. It would be just as
454     // efficient to send all of it, but we don't want people to rely on
455     // getting download progress when they happen to set the upload progress
456     // flag.
457     ppapi::proxy::ResourceMessageReplyParams params;
458     SendUpdateToPlugin(
459         std::make_unique<PpapiPluginMsg_URLLoader_UpdateProgress>(
460             record_upload ? bytes_sent_ : -1,
461             record_upload ? total_bytes_to_be_sent_ : -1,
462             record_download ? bytes_received_ : -1,
463             record_download ? total_bytes_to_be_received_ : -1));
464   }
465 }
466
467 }  // namespace content