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