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