Home | History | Annotate | Download | only in pepper
      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   // Note that |loader| will be NULL for document loads.
    178   SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(PP_OK));
    179 }
    180 
    181 void PepperURLLoaderHost::didFail(WebURLLoader* loader,
    182                                  const WebURLError& error) {
    183   // Note that |loader| will be NULL for document loads.
    184   int32_t pp_error = PP_ERROR_FAILED;
    185   if (error.domain.equals(WebString::fromUTF8(net::kErrorDomain))) {
    186     // TODO(bbudge): Extend pp_errors.h to cover interesting network errors
    187     // from the net error domain.
    188     switch (error.reason) {
    189       case net::ERR_ACCESS_DENIED:
    190       case net::ERR_NETWORK_ACCESS_DENIED:
    191         pp_error = PP_ERROR_NOACCESS;
    192         break;
    193     }
    194   } else {
    195     // It's a WebKit error.
    196     pp_error = PP_ERROR_NOACCESS;
    197   }
    198   SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(pp_error));
    199 }
    200 
    201 void PepperURLLoaderHost::DidConnectPendingHostToResource() {
    202   for (size_t i = 0; i < pending_replies_.size(); i++)
    203     host()->SendUnsolicitedReply(pp_resource(), *pending_replies_[i]);
    204   pending_replies_.clear();
    205 }
    206 
    207 int32_t PepperURLLoaderHost::OnHostMsgOpen(
    208     ppapi::host::HostMessageContext* context,
    209     const ppapi::URLRequestInfoData& request_data) {
    210   // An "Open" isn't a resource Call so has no reply, but failure to open
    211   // implies a load failure. To make it harder to forget to send the load
    212   // failed reply from the open handler, we instead catch errors and convert
    213   // them to load failed messages.
    214   int32_t ret = InternalOnHostMsgOpen(context, request_data);
    215   DCHECK(ret != PP_OK_COMPLETIONPENDING);
    216 
    217   if (ret != PP_OK)
    218     SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(ret));
    219   return PP_OK;
    220 }
    221 
    222 // Since this is wrapped by OnHostMsgOpen, we can return errors here and they
    223 // will be translated into a FinishedLoading call automatically.
    224 int32_t PepperURLLoaderHost::InternalOnHostMsgOpen(
    225     ppapi::host::HostMessageContext* context,
    226     const ppapi::URLRequestInfoData& request_data) {
    227   // Main document loads are already open, so don't allow people to open them
    228   // again.
    229   if (main_document_loader_)
    230     return PP_ERROR_INPROGRESS;
    231 
    232   // Create a copy of the request data since CreateWebURLRequest will populate
    233   // the file refs.
    234   ppapi::URLRequestInfoData filled_in_request_data = request_data;
    235 
    236   if (URLRequestRequiresUniversalAccess(filled_in_request_data) &&
    237       !has_universal_access_) {
    238     ppapi::PpapiGlobals::Get()->LogWithSource(
    239         pp_instance(), PP_LOGLEVEL_ERROR, 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   WebFrame* frame = GetFrame();
    251   if (!frame)
    252     return PP_ERROR_FAILED;
    253 
    254   WebURLRequest web_request;
    255   if (!CreateWebURLRequest(pp_instance(),
    256                            &filled_in_request_data,
    257                            frame,
    258                            &web_request)) {
    259     return PP_ERROR_FAILED;
    260   }
    261 
    262   web_request.setTargetType(WebURLRequest::TargetIsObject);
    263   web_request.setRequestorProcessID(renderer_ppapi_host_->GetPluginPID());
    264 
    265   WebURLLoaderOptions options;
    266   if (has_universal_access_) {
    267     options.allowCredentials = true;
    268     options.crossOriginRequestPolicy =
    269         WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
    270   } else {
    271     // All other HTTP requests are untrusted.
    272     options.untrustedHTTP = true;
    273     if (filled_in_request_data.allow_cross_origin_requests) {
    274       // Allow cross-origin requests with access control. The request specifies
    275       // if credentials are to be sent.
    276       options.allowCredentials = filled_in_request_data.allow_credentials;
    277       options.crossOriginRequestPolicy =
    278           WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
    279     } else {
    280       // Same-origin requests can always send credentials.
    281       options.allowCredentials = true;
    282     }
    283   }
    284 
    285   loader_.reset(frame->createAssociatedURLLoader(options));
    286   if (!loader_.get())
    287     return PP_ERROR_FAILED;
    288 
    289   // Don't actually save the request until we know we're going to load.
    290   request_data_ = filled_in_request_data;
    291   loader_->loadAsynchronously(web_request, this);
    292 
    293   // Although the request is technically pending, this is not a "Call" message
    294   // so we don't return COMPLETIONPENDING.
    295   return PP_OK;
    296 }
    297 
    298 int32_t PepperURLLoaderHost::OnHostMsgSetDeferLoading(
    299     ppapi::host::HostMessageContext* context,
    300     bool defers_loading) {
    301   SetDefersLoading(defers_loading);
    302   return PP_OK;
    303 }
    304 
    305 int32_t PepperURLLoaderHost::OnHostMsgClose(
    306     ppapi::host::HostMessageContext* context) {
    307   Close();
    308   return PP_OK;
    309 }
    310 
    311 int32_t PepperURLLoaderHost::OnHostMsgGrantUniversalAccess(
    312     ppapi::host::HostMessageContext* context) {
    313   // Only plugins with private permission can bypass same origin.
    314   if (!host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE))
    315     return PP_ERROR_FAILED;
    316   has_universal_access_ = true;
    317   return PP_OK;
    318 }
    319 
    320 void PepperURLLoaderHost::SendUpdateToPlugin(IPC::Message* message) {
    321   // We must send messages to the plugin in the order that the responses are
    322   // received from webkit, even when the host isn't ready to send messages or
    323   // when the host performs an asynchronous operation.
    324   //
    325   // Only {FinishedLoading, ReceivedResponse, SendData} have ordering
    326   // contraints; all other messages are immediately added to pending_replies_.
    327   //
    328   // Accepted orderings for {FinishedLoading, ReceivedResponse, SendData} are:
    329   //   - {ReceivedResponse, SendData (zero or more times), FinishedLoading}
    330   //   - {FinishedLoading (when status != PP_OK)}
    331   if (message->type() == PpapiPluginMsg_URLLoader_SendData::ID ||
    332       message->type() == PpapiPluginMsg_URLLoader_FinishedLoading::ID) {
    333     // Messages that must be sent after ReceivedResponse.
    334     if (pending_response_) {
    335       out_of_order_replies_.push_back(message);
    336     } else {
    337       SendOrderedUpdateToPlugin(message);
    338     }
    339   } else if (message->type() == PpapiPluginMsg_URLLoader_ReceivedResponse::ID) {
    340     // Allow SendData and FinishedLoading into the ordered queue.
    341     DCHECK(pending_response_);
    342     SendOrderedUpdateToPlugin(message);
    343     for (size_t i = 0; i < out_of_order_replies_.size(); i++)
    344       SendOrderedUpdateToPlugin(out_of_order_replies_[i]);
    345     // SendOrderedUpdateToPlugin destroys the messages for us.
    346     out_of_order_replies_.weak_clear();
    347     pending_response_ = false;
    348   } else {
    349     // Messages without ordering constraints.
    350     SendOrderedUpdateToPlugin(message);
    351   }
    352 }
    353 
    354 void PepperURLLoaderHost::SendOrderedUpdateToPlugin(IPC::Message* message) {
    355   if (pp_resource() == 0) {
    356     pending_replies_.push_back(message);
    357   } else {
    358     host()->SendUnsolicitedReply(pp_resource(), *message);
    359     delete message;
    360   }
    361 }
    362 
    363 void PepperURLLoaderHost::Close() {
    364   if (loader_.get())
    365     loader_->cancel();
    366   else if (main_document_loader_)
    367     GetFrame()->stopLoading();
    368 }
    369 
    370 blink::WebFrame* PepperURLLoaderHost::GetFrame() {
    371   PepperPluginInstance* instance_object =
    372       renderer_ppapi_host_->GetPluginInstance(pp_instance());
    373   if (!instance_object)
    374     return NULL;
    375   return instance_object->GetContainer()->element().document().frame();
    376 }
    377 
    378 void PepperURLLoaderHost::SetDefersLoading(bool defers_loading) {
    379   if (loader_.get())
    380     loader_->setDefersLoading(defers_loading);
    381 
    382   // TODO(brettw) bug 96770: We need a way to set the defers loading flag on
    383   // main document loads (when the loader_ is null).
    384 }
    385 
    386 void PepperURLLoaderHost::SaveResponse(const WebURLResponse& response) {
    387   // When we're the main document loader, we send the response data up front,
    388   // so we don't want to trigger any callbacks in the plugin which aren't
    389   // expected. We should not be getting redirects so the response sent
    390   // up-front should be valid (plugin document loads happen after all
    391   // redirects are processed since WebKit has to know the MIME type).
    392   if (!main_document_loader_) {
    393     // We note when there's a callback in flight for a response to ensure that
    394     // messages we send to the plugin are not sent out of order. See
    395     // SendUpdateToPlugin() for more details.
    396     DCHECK(!pending_response_);
    397     pending_response_ = true;
    398 
    399     DataFromWebURLResponse(
    400         renderer_ppapi_host_,
    401         pp_instance(),
    402         response,
    403         base::Bind(&PepperURLLoaderHost::DidDataFromWebURLResponse,
    404             weak_factory_.GetWeakPtr()));
    405   }
    406 }
    407 
    408 void PepperURLLoaderHost::DidDataFromWebURLResponse(
    409     const ppapi::URLResponseInfoData& data) {
    410   SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_ReceivedResponse(data));
    411 }
    412 
    413 void PepperURLLoaderHost::UpdateProgress() {
    414   bool record_download = request_data_.record_download_progress;
    415   bool record_upload = request_data_.record_upload_progress;
    416   if (record_download || record_upload) {
    417     // Here we go through some effort to only send the exact information that
    418     // the requestor wanted in the request flags. It would be just as
    419     // efficient to send all of it, but we don't want people to rely on
    420     // getting download progress when they happen to set the upload progress
    421     // flag.
    422     ppapi::proxy::ResourceMessageReplyParams params;
    423     SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_UpdateProgress(
    424             record_upload ? bytes_sent_ : -1,
    425             record_upload ? total_bytes_to_be_sent_ : -1,
    426             record_download ? bytes_received_ : -1,
    427             record_download ? total_bytes_to_be_received_ : -1));
    428   }
    429 }
    430 
    431 }  // namespace content
    432