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