Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2012 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 "ppapi/proxy/ppb_broker_proxy.h"
      6 
      7 #include "base/bind.h"
      8 #include "ppapi/c/pp_errors.h"
      9 #include "ppapi/c/trusted/ppb_broker_trusted.h"
     10 #include "ppapi/proxy/enter_proxy.h"
     11 #include "ppapi/proxy/plugin_dispatcher.h"
     12 #include "ppapi/proxy/ppapi_messages.h"
     13 #include "ppapi/shared_impl/platform_file.h"
     14 #include "ppapi/shared_impl/tracked_callback.h"
     15 #include "ppapi/thunk/ppb_broker_api.h"
     16 #include "ppapi/thunk/enter.h"
     17 #include "ppapi/thunk/resource_creation_api.h"
     18 #include "ppapi/thunk/thunk.h"
     19 
     20 using ppapi::IntToPlatformFile;
     21 using ppapi::PlatformFileToInt;
     22 using ppapi::thunk::PPB_Broker_API;
     23 
     24 namespace ppapi {
     25 namespace proxy {
     26 
     27 class Broker : public PPB_Broker_API, public Resource {
     28  public:
     29   explicit Broker(const HostResource& resource);
     30   virtual ~Broker();
     31 
     32   // Resource overrides.
     33   virtual PPB_Broker_API* AsPPB_Broker_API() OVERRIDE;
     34 
     35   // PPB_Broker_API implementation.
     36   virtual int32_t Connect(
     37       scoped_refptr<TrackedCallback> connect_callback) OVERRIDE;
     38   virtual int32_t GetHandle(int32_t* handle) OVERRIDE;
     39 
     40   // Called by the proxy when the host side has completed the request.
     41   void ConnectComplete(IPC::PlatformFileForTransit socket_handle,
     42                        int32_t result);
     43 
     44  private:
     45   bool called_connect_;
     46   scoped_refptr<TrackedCallback> current_connect_callback_;
     47 
     48   // The plugin module owns the handle.
     49   // The host side transfers ownership of the handle to the plugin side when it
     50   // sends the IPC. This member holds the handle value for the plugin module
     51   // to read, but the plugin side of the proxy never takes ownership.
     52   base::SyncSocket::Handle socket_handle_;
     53 
     54   DISALLOW_COPY_AND_ASSIGN(Broker);
     55 };
     56 
     57 Broker::Broker(const HostResource& resource)
     58     : Resource(OBJECT_IS_PROXY, resource),
     59       called_connect_(false),
     60       socket_handle_(base::kInvalidPlatformFileValue) {
     61 }
     62 
     63 Broker::~Broker() {
     64   socket_handle_ = base::kInvalidPlatformFileValue;
     65 }
     66 
     67 PPB_Broker_API* Broker::AsPPB_Broker_API() {
     68   return this;
     69 }
     70 
     71 int32_t Broker::Connect(scoped_refptr<TrackedCallback> connect_callback) {
     72   if (TrackedCallback::IsPending(current_connect_callback_))
     73     return PP_ERROR_INPROGRESS;
     74   else if (called_connect_)
     75     return PP_ERROR_FAILED;
     76 
     77   current_connect_callback_ = connect_callback;
     78   called_connect_ = true;
     79 
     80   bool success = PluginDispatcher::GetForResource(this)->Send(
     81       new PpapiHostMsg_PPBBroker_Connect(
     82           API_ID_PPB_BROKER, host_resource()));
     83   return success ?  PP_OK_COMPLETIONPENDING : PP_ERROR_FAILED;
     84 }
     85 
     86 int32_t Broker::GetHandle(int32_t* handle) {
     87   if (socket_handle_ == base::kInvalidPlatformFileValue)
     88     return PP_ERROR_FAILED;
     89   *handle = PlatformFileToInt(socket_handle_);
     90   return PP_OK;
     91 }
     92 
     93 void Broker::ConnectComplete(IPC::PlatformFileForTransit socket_handle,
     94                              int32_t result) {
     95   if (result == PP_OK) {
     96     DCHECK(socket_handle_ == base::kInvalidPlatformFileValue);
     97     socket_handle_ = IPC::PlatformFileForTransitToPlatformFile(socket_handle);
     98   } else {
     99     // The caller may still have given us a handle in the failure case.
    100     // The easiest way to clean it up is to just put it in an object
    101     // and then close them. This failure case is not performance critical.
    102     base::SyncSocket temp_socket(
    103         IPC::PlatformFileForTransitToPlatformFile(socket_handle));
    104   }
    105 
    106   if (!TrackedCallback::IsPending(current_connect_callback_)) {
    107     // The handle might leak if the plugin never calls GetHandle().
    108     return;
    109   }
    110 
    111   current_connect_callback_->Run(result);
    112 }
    113 
    114 PPB_Broker_Proxy::PPB_Broker_Proxy(Dispatcher* dispatcher)
    115     : InterfaceProxy(dispatcher),
    116       callback_factory_(this){
    117 }
    118 
    119 PPB_Broker_Proxy::~PPB_Broker_Proxy() {
    120 }
    121 
    122 // static
    123 PP_Resource PPB_Broker_Proxy::CreateProxyResource(PP_Instance instance) {
    124   PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
    125   if (!dispatcher)
    126     return 0;
    127 
    128   HostResource result;
    129   dispatcher->Send(new PpapiHostMsg_PPBBroker_Create(
    130       API_ID_PPB_BROKER, instance, &result));
    131   if (result.is_null())
    132     return 0;
    133   return (new Broker(result))->GetReference();
    134 }
    135 
    136 bool PPB_Broker_Proxy::OnMessageReceived(const IPC::Message& msg) {
    137   bool handled = true;
    138   IPC_BEGIN_MESSAGE_MAP(PPB_Broker_Proxy, msg)
    139     IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBBroker_Create, OnMsgCreate)
    140     IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBBroker_Connect, OnMsgConnect)
    141     IPC_MESSAGE_HANDLER(PpapiMsg_PPBBroker_ConnectComplete,
    142                         OnMsgConnectComplete)
    143     IPC_MESSAGE_UNHANDLED(handled = false)
    144   IPC_END_MESSAGE_MAP()
    145   return handled;
    146 }
    147 
    148 void PPB_Broker_Proxy::OnMsgCreate(PP_Instance instance,
    149                                    HostResource* result_resource) {
    150   if (!dispatcher()->permissions().HasPermission(PERMISSION_PRIVATE))
    151     return;
    152   thunk::EnterResourceCreation enter(instance);
    153   if (enter.succeeded()) {
    154     result_resource->SetHostResource(
    155         instance,
    156         enter.functions()->CreateBroker(instance));
    157   }
    158 }
    159 
    160 void PPB_Broker_Proxy::OnMsgConnect(const HostResource& broker) {
    161   if (!dispatcher()->permissions().HasPermission(PERMISSION_PRIVATE))
    162     return;
    163   EnterHostFromHostResourceForceCallback<PPB_Broker_API> enter(
    164       broker, callback_factory_,
    165       &PPB_Broker_Proxy::ConnectCompleteInHost, broker);
    166   if (enter.succeeded())
    167     enter.SetResult(enter.object()->Connect(enter.callback()));
    168 }
    169 
    170 // Called in the plugin to handle the connect callback.
    171 // The proxy owns the handle and transfers it to the Broker. At that point,
    172 // the plugin owns the handle and is responsible for closing it.
    173 // The caller guarantees that socket_handle is not valid if result is not PP_OK.
    174 void PPB_Broker_Proxy::OnMsgConnectComplete(
    175     const HostResource& resource,
    176     IPC::PlatformFileForTransit socket_handle,
    177     int32_t result) {
    178   DCHECK(result == PP_OK ||
    179          socket_handle == IPC::InvalidPlatformFileForTransit());
    180 
    181   EnterPluginFromHostResource<PPB_Broker_API> enter(resource);
    182   if (enter.failed()) {
    183     // As in Broker::ConnectComplete, we need to close the resource on error.
    184     base::SyncSocket temp_socket(
    185         IPC::PlatformFileForTransitToPlatformFile(socket_handle));
    186   } else {
    187     static_cast<Broker*>(enter.object())->ConnectComplete(socket_handle,
    188                                                           result);
    189   }
    190 }
    191 
    192 // Callback on the host side.
    193 // Transfers ownership of the handle to the plugin side. This function must
    194 // either successfully call the callback or close the handle.
    195 void PPB_Broker_Proxy::ConnectCompleteInHost(int32_t result,
    196                                              const HostResource& broker) {
    197   IPC::PlatformFileForTransit foreign_socket_handle =
    198       IPC::InvalidPlatformFileForTransit();
    199   if (result == PP_OK) {
    200     int32_t socket_handle = PlatformFileToInt(base::kInvalidPlatformFileValue);
    201     EnterHostFromHostResource<PPB_Broker_API> enter(broker);
    202     if (enter.succeeded())
    203       result = enter.object()->GetHandle(&socket_handle);
    204     DCHECK(result == PP_OK ||
    205            socket_handle == PlatformFileToInt(base::kInvalidPlatformFileValue));
    206 
    207     if (result == PP_OK) {
    208       foreign_socket_handle =
    209           dispatcher()->ShareHandleWithRemote(IntToPlatformFile(socket_handle),
    210                                               true);
    211       if (foreign_socket_handle == IPC::InvalidPlatformFileForTransit()) {
    212         result = PP_ERROR_FAILED;
    213         // Assume the local handle was closed even if the foreign handle could
    214         // not be created.
    215       }
    216     }
    217   }
    218   DCHECK(result == PP_OK ||
    219          foreign_socket_handle == IPC::InvalidPlatformFileForTransit());
    220 
    221   bool success = dispatcher()->Send(new PpapiMsg_PPBBroker_ConnectComplete(
    222       API_ID_PPB_BROKER, broker, foreign_socket_handle, result));
    223 
    224   if (!success || result != PP_OK) {
    225       // The plugin did not receive the handle, so it must be closed.
    226       // The easiest way to clean it up is to just put it in an object
    227       // and then close it. This failure case is not performance critical.
    228       // The handle could still leak if Send succeeded but the IPC later failed.
    229       base::SyncSocket temp_socket(
    230           IPC::PlatformFileForTransitToPlatformFile(foreign_socket_handle));
    231   }
    232 }
    233 
    234 }  // namespace proxy
    235 }  // namespace ppapi
    236