Home | History | Annotate | Download | only in pepper
      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 "content/renderer/pepper/pepper_broker.h"
      6 
      7 #include "build/build_config.h"
      8 #include "content/renderer/pepper/pepper_proxy_channel_delegate_impl.h"
      9 #include "content/renderer/pepper/plugin_module.h"
     10 #include "content/renderer/pepper/ppb_broker_impl.h"
     11 #include "content/renderer/pepper/renderer_restrict_dispatch_group.h"
     12 #include "ipc/ipc_channel_handle.h"
     13 #include "ppapi/proxy/broker_dispatcher.h"
     14 #include "ppapi/proxy/ppapi_messages.h"
     15 #include "ppapi/shared_impl/platform_file.h"
     16 
     17 #if defined(OS_WIN)
     18 #include <windows.h>
     19 #endif
     20 
     21 namespace content {
     22 
     23 namespace {
     24 
     25 base::SyncSocket::Handle DuplicateHandle(base::SyncSocket::Handle handle) {
     26   base::SyncSocket::Handle out_handle = base::kInvalidPlatformFileValue;
     27 #if defined(OS_WIN)
     28   DWORD options = DUPLICATE_SAME_ACCESS;
     29   if (!::DuplicateHandle(::GetCurrentProcess(),
     30                          handle,
     31                          ::GetCurrentProcess(),
     32                          &out_handle,
     33                          0,
     34                          FALSE,
     35                          options)) {
     36     out_handle = base::kInvalidPlatformFileValue;
     37   }
     38 #elif defined(OS_POSIX)
     39   // If asked to close the source, we can simply re-use the source fd instead of
     40   // dup()ing and close()ing.
     41   out_handle = ::dup(handle);
     42 #else
     43   #error Not implemented.
     44 #endif
     45   return out_handle;
     46 }
     47 
     48 }  // namespace
     49 
     50 PepperBrokerDispatcherWrapper::PepperBrokerDispatcherWrapper() {
     51 }
     52 
     53 PepperBrokerDispatcherWrapper::~PepperBrokerDispatcherWrapper() {
     54 }
     55 
     56 bool PepperBrokerDispatcherWrapper::Init(
     57     base::ProcessId broker_pid,
     58     const IPC::ChannelHandle& channel_handle) {
     59   if (channel_handle.name.empty())
     60     return false;
     61 
     62 #if defined(OS_POSIX)
     63   DCHECK_NE(-1, channel_handle.socket.fd);
     64   if (channel_handle.socket.fd == -1)
     65     return false;
     66 #endif
     67 
     68   dispatcher_delegate_.reset(new PepperProxyChannelDelegateImpl);
     69   dispatcher_.reset(
     70       new ppapi::proxy::BrokerHostDispatcher());
     71 
     72   if (!dispatcher_->InitBrokerWithChannel(dispatcher_delegate_.get(),
     73                                           broker_pid,
     74                                           channel_handle,
     75                                           true)) {  // Client.
     76     dispatcher_.reset();
     77     dispatcher_delegate_.reset();
     78     return false;
     79   }
     80   dispatcher_->channel()->SetRestrictDispatchChannelGroup(
     81       kRendererRestrictDispatchGroup_Pepper);
     82   return true;
     83 }
     84 
     85 // Does not take ownership of the local pipe.
     86 int32_t PepperBrokerDispatcherWrapper::SendHandleToBroker(
     87     PP_Instance instance,
     88     base::SyncSocket::Handle handle) {
     89   IPC::PlatformFileForTransit foreign_socket_handle =
     90       dispatcher_->ShareHandleWithRemote(handle, false);
     91   if (foreign_socket_handle == IPC::InvalidPlatformFileForTransit())
     92     return PP_ERROR_FAILED;
     93 
     94   int32_t result;
     95   if (!dispatcher_->Send(
     96       new PpapiMsg_ConnectToPlugin(instance, foreign_socket_handle, &result))) {
     97     // The plugin did not receive the handle, so it must be closed.
     98     // The easiest way to clean it up is to just put it in an object
     99     // and then close it. This failure case is not performance critical.
    100     // The handle could still leak if Send succeeded but the IPC later failed.
    101     base::SyncSocket temp_socket(
    102         IPC::PlatformFileForTransitToPlatformFile(foreign_socket_handle));
    103     return PP_ERROR_FAILED;
    104   }
    105 
    106   return result;
    107 }
    108 
    109 PepperBroker::PepperBroker(PluginModule* plugin_module)
    110     : plugin_module_(plugin_module) {
    111   DCHECK(plugin_module_);
    112 
    113   plugin_module_->SetBroker(this);
    114 }
    115 
    116 PepperBroker::~PepperBroker() {
    117   ReportFailureToClients(PP_ERROR_ABORTED);
    118   plugin_module_->SetBroker(NULL);
    119   plugin_module_ = NULL;
    120 }
    121 
    122 // If the channel is not ready, queue the connection.
    123 void PepperBroker::AddPendingConnect(PPB_Broker_Impl* client) {
    124   DCHECK(pending_connects_.find(client) == pending_connects_.end())
    125       << "Connect was already called for this client";
    126 
    127   // Ensure this object and the associated broker exist as long as the
    128   // client exists. There is a corresponding Release() call in Disconnect(),
    129   // which is called when the PPB_Broker_Impl is destroyed. The only other
    130   // possible reference is in pending_connect_broker_, which only holds a
    131   // transient reference. This ensures the broker is available as long as the
    132   // plugin needs it and allows the plugin to release the broker when it is no
    133   // longer using it.
    134   AddRef();
    135 
    136   pending_connects_[client].client = client->AsWeakPtr();
    137 }
    138 
    139 void PepperBroker::Disconnect(PPB_Broker_Impl* client) {
    140   // Remove the pending connect if one exists. This class will not call client's
    141   // callback.
    142   pending_connects_.erase(client);
    143 
    144   // TODO(ddorwin): Send message disconnect message using dispatcher_.
    145 
    146   // Release the reference added in Connect().
    147   // This must be the last statement because it may delete this object.
    148   Release();
    149 }
    150 
    151 void PepperBroker::OnBrokerChannelConnected(
    152     base::ProcessId broker_pid,
    153     const IPC::ChannelHandle& channel_handle) {
    154   scoped_ptr<PepperBrokerDispatcherWrapper> dispatcher(
    155       new PepperBrokerDispatcherWrapper);
    156   if (!dispatcher->Init(broker_pid, channel_handle)) {
    157     ReportFailureToClients(PP_ERROR_FAILED);
    158     return;
    159   }
    160 
    161   dispatcher_.reset(dispatcher.release());
    162 
    163   // Process all pending channel requests from the plugins.
    164   for (ClientMap::iterator i = pending_connects_.begin();
    165        i != pending_connects_.end();) {
    166     base::WeakPtr<PPB_Broker_Impl>& weak_ptr = i->second.client;
    167     if (!i->second.is_authorized) {
    168       ++i;
    169       continue;
    170     }
    171 
    172     if (weak_ptr.get())
    173       ConnectPluginToBroker(weak_ptr.get());
    174 
    175     pending_connects_.erase(i++);
    176   }
    177 }
    178 
    179 void PepperBroker::OnBrokerPermissionResult(PPB_Broker_Impl* client,
    180                                             bool result) {
    181   ClientMap::iterator entry = pending_connects_.find(client);
    182   if (entry == pending_connects_.end())
    183     return;
    184 
    185   if (!entry->second.client.get()) {
    186     // Client has gone away.
    187     pending_connects_.erase(entry);
    188     return;
    189   }
    190 
    191   if (!result) {
    192     // Report failure.
    193     client->BrokerConnected(
    194         ppapi::PlatformFileToInt(base::kInvalidPlatformFileValue),
    195         PP_ERROR_NOACCESS);
    196     pending_connects_.erase(entry);
    197     return;
    198   }
    199 
    200   if (dispatcher_) {
    201     ConnectPluginToBroker(client);
    202     pending_connects_.erase(entry);
    203     return;
    204   }
    205 
    206   // Mark the request as authorized, continue waiting for the broker
    207   // connection.
    208   DCHECK(!entry->second.is_authorized);
    209   entry->second.is_authorized = true;
    210 }
    211 
    212 PepperBroker::PendingConnection::PendingConnection() : is_authorized(false) {
    213 }
    214 
    215 PepperBroker::PendingConnection::~PendingConnection() {
    216 }
    217 
    218 void PepperBroker::ReportFailureToClients(int error_code) {
    219   DCHECK_NE(PP_OK, error_code);
    220   for (ClientMap::iterator i = pending_connects_.begin();
    221        i != pending_connects_.end(); ++i) {
    222     base::WeakPtr<PPB_Broker_Impl>& weak_ptr = i->second.client;
    223     if (weak_ptr.get()) {
    224       weak_ptr->BrokerConnected(
    225           ppapi::PlatformFileToInt(base::kInvalidPlatformFileValue),
    226           error_code);
    227     }
    228   }
    229   pending_connects_.clear();
    230 }
    231 
    232 void PepperBroker::ConnectPluginToBroker(PPB_Broker_Impl* client) {
    233   base::SyncSocket::Handle plugin_handle = base::kInvalidPlatformFileValue;
    234   int32_t result = PP_OK;
    235 
    236   // The socket objects will be deleted when this function exits, closing the
    237   // handles. Any uses of the socket must duplicate them.
    238   scoped_ptr<base::SyncSocket> broker_socket(new base::SyncSocket());
    239   scoped_ptr<base::SyncSocket> plugin_socket(new base::SyncSocket());
    240   if (base::SyncSocket::CreatePair(broker_socket.get(), plugin_socket.get())) {
    241     result = dispatcher_->SendHandleToBroker(client->pp_instance(),
    242                                              broker_socket->handle());
    243 
    244     // If the broker has its pipe handle, duplicate the plugin's handle.
    245     // Otherwise, the plugin's handle will be automatically closed.
    246     if (result == PP_OK)
    247       plugin_handle = DuplicateHandle(plugin_socket->handle());
    248   } else {
    249     result = PP_ERROR_FAILED;
    250   }
    251 
    252   // TOOD(ddorwin): Change the IPC to asynchronous: Queue an object containing
    253   // client and plugin_socket.release(), then return.
    254   // That message handler will then call client->BrokerConnected() with the
    255   // saved pipe handle.
    256   // Temporarily, just call back.
    257   client->BrokerConnected(ppapi::PlatformFileToInt(plugin_handle), result);
    258 }
    259 
    260 }  // namespace content
    261