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.
5 #include "content/renderer/pepper/pepper_broker.h"
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"
25 base::SyncSocket::Handle
DuplicateHandle(base::SyncSocket::Handle handle
) {
26 base::SyncSocket::Handle out_handle
= base::SyncSocket::kInvalidHandle
;
28 DWORD options
= DUPLICATE_SAME_ACCESS
;
29 if (!::DuplicateHandle(::GetCurrentProcess(),
31 ::GetCurrentProcess(),
36 out_handle
= base::SyncSocket::kInvalidHandle
;
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
);
43 #error Not implemented.
50 PepperBrokerDispatcherWrapper::PepperBrokerDispatcherWrapper() {}
52 PepperBrokerDispatcherWrapper::~PepperBrokerDispatcherWrapper() {}
54 bool PepperBrokerDispatcherWrapper::Init(
55 base::ProcessId broker_pid
,
56 const IPC::ChannelHandle
& channel_handle
) {
57 if (channel_handle
.name
.empty())
61 DCHECK_NE(-1, channel_handle
.socket
.fd
);
62 if (channel_handle
.socket
.fd
== -1)
66 dispatcher_delegate_
.reset(new PepperProxyChannelDelegateImpl
);
67 dispatcher_
.reset(new ppapi::proxy::BrokerHostDispatcher());
69 if (!dispatcher_
->InitBrokerWithChannel(dispatcher_delegate_
.get(),
74 dispatcher_delegate_
.reset();
77 dispatcher_
->channel()->SetRestrictDispatchChannelGroup(
78 kRendererRestrictDispatchGroup_Pepper
);
82 // Does not take ownership of the local pipe.
83 int32_t PepperBrokerDispatcherWrapper::SendHandleToBroker(
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
;
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
;
106 PepperBroker::PepperBroker(PluginModule
* plugin_module
)
107 : plugin_module_(plugin_module
) {
108 DCHECK(plugin_module_
);
110 plugin_module_
->SetBroker(this);
113 PepperBroker::~PepperBroker() {
114 ReportFailureToClients(PP_ERROR_ABORTED
);
115 plugin_module_
->SetBroker(NULL
);
116 plugin_module_
= NULL
;
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";
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
133 pending_connects_
[client
].client
= client
->AsWeakPtr();
136 void PepperBroker::Disconnect(PPB_Broker_Impl
* client
) {
137 // Remove the pending connect if one exists. This class will not call client's
139 pending_connects_
.erase(client
);
141 // TODO(ddorwin): Send message disconnect message using dispatcher_.
143 // Release the reference added in Connect().
144 // This must be the last statement because it may delete this object.
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
);
158 dispatcher_
.reset(dispatcher
.release());
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
) {
170 ConnectPluginToBroker(weak_ptr
.get());
172 pending_connects_
.erase(i
++);
176 void PepperBroker::OnBrokerPermissionResult(PPB_Broker_Impl
* client
,
178 ClientMap::iterator entry
= pending_connects_
.find(client
);
179 if (entry
== pending_connects_
.end())
182 if (!entry
->second
.client
.get()) {
183 // Client has gone away.
184 pending_connects_
.erase(entry
);
190 client
->BrokerConnected(
191 ppapi::PlatformFileToInt(base::SyncSocket::kInvalidHandle
),
193 pending_connects_
.erase(entry
);
198 ConnectPluginToBroker(client
);
199 pending_connects_
.erase(entry
);
203 // Mark the request as authorized, continue waiting for the broker
205 DCHECK(!entry
->second
.is_authorized
);
206 entry
->second
.is_authorized
= true;
209 PepperBroker::PendingConnection::PendingConnection() : is_authorized(false) {}
211 PepperBroker::PendingConnection::~PendingConnection() {}
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();
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
),
225 pending_connects_
.clear();
228 void PepperBroker::ConnectPluginToBroker(PPB_Broker_Impl
* client
) {
229 base::SyncSocket::Handle plugin_handle
= base::SyncSocket::kInvalidHandle
;
230 int32_t result
= PP_OK
;
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());
240 // If the broker has its pipe handle, duplicate the plugin's handle.
241 // Otherwise, the plugin's handle will be automatically closed.
243 plugin_handle
= DuplicateHandle(plugin_socket
->handle());
245 result
= PP_ERROR_FAILED
;
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
);
256 } // namespace content