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 "ppapi/proxy/ppb_broker_proxy.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"
20 using ppapi::IntToPlatformFile
;
21 using ppapi::PlatformFileToInt
;
22 using ppapi::thunk::PPB_Broker_API
;
27 class Broker
: public PPB_Broker_API
, public Resource
{
29 explicit Broker(const HostResource
& resource
);
32 // Resource overrides.
33 virtual PPB_Broker_API
* AsPPB_Broker_API() OVERRIDE
;
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
;
40 // Called by the proxy when the host side has completed the request.
41 void ConnectComplete(IPC::PlatformFileForTransit socket_handle
,
46 scoped_refptr
<TrackedCallback
> current_connect_callback_
;
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_
;
54 DISALLOW_COPY_AND_ASSIGN(Broker
);
57 Broker::Broker(const HostResource
& resource
)
58 : Resource(OBJECT_IS_PROXY
, resource
),
59 called_connect_(false),
60 socket_handle_(base::kInvalidPlatformFileValue
) {
64 socket_handle_
= base::kInvalidPlatformFileValue
;
67 PPB_Broker_API
* Broker::AsPPB_Broker_API() {
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
;
77 current_connect_callback_
= connect_callback
;
78 called_connect_
= true;
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
;
86 int32_t Broker::GetHandle(int32_t* handle
) {
87 if (socket_handle_
== base::kInvalidPlatformFileValue
)
88 return PP_ERROR_FAILED
;
89 *handle
= PlatformFileToInt(socket_handle_
);
93 void Broker::ConnectComplete(IPC::PlatformFileForTransit socket_handle
,
95 if (result
== PP_OK
) {
96 DCHECK(socket_handle_
== base::kInvalidPlatformFileValue
);
97 socket_handle_
= IPC::PlatformFileForTransitToPlatformFile(socket_handle
);
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
));
106 if (!TrackedCallback::IsPending(current_connect_callback_
)) {
107 // The handle might leak if the plugin never calls GetHandle().
111 current_connect_callback_
->Run(result
);
114 PPB_Broker_Proxy::PPB_Broker_Proxy(Dispatcher
* dispatcher
)
115 : InterfaceProxy(dispatcher
),
116 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)){
119 PPB_Broker_Proxy::~PPB_Broker_Proxy() {
123 PP_Resource
PPB_Broker_Proxy::CreateProxyResource(PP_Instance instance
) {
124 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForInstance(instance
);
129 dispatcher
->Send(new PpapiHostMsg_PPBBroker_Create(
130 API_ID_PPB_BROKER
, instance
, &result
));
131 if (result
.is_null())
133 return (new Broker(result
))->GetReference();
136 bool PPB_Broker_Proxy::OnMessageReceived(const IPC::Message
& msg
) {
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()
148 void PPB_Broker_Proxy::OnMsgCreate(PP_Instance instance
,
149 HostResource
* result_resource
) {
150 thunk::EnterResourceCreation
enter(instance
);
151 if (enter
.succeeded()) {
152 result_resource
->SetHostResource(
154 enter
.functions()->CreateBroker(instance
));
158 void PPB_Broker_Proxy::OnMsgConnect(const HostResource
& broker
) {
159 EnterHostFromHostResourceForceCallback
<PPB_Broker_API
> enter(
160 broker
, callback_factory_
,
161 &PPB_Broker_Proxy::ConnectCompleteInHost
, broker
);
162 if (enter
.succeeded())
163 enter
.SetResult(enter
.object()->Connect(enter
.callback()));
166 // Called in the plugin to handle the connect callback.
167 // The proxy owns the handle and transfers it to the Broker. At that point,
168 // the plugin owns the handle and is responsible for closing it.
169 // The caller guarantees that socket_handle is not valid if result is not PP_OK.
170 void PPB_Broker_Proxy::OnMsgConnectComplete(
171 const HostResource
& resource
,
172 IPC::PlatformFileForTransit socket_handle
,
174 DCHECK(result
== PP_OK
||
175 socket_handle
== IPC::InvalidPlatformFileForTransit());
177 EnterPluginFromHostResource
<PPB_Broker_API
> enter(resource
);
178 if (enter
.failed()) {
179 // As in Broker::ConnectComplete, we need to close the resource on error.
180 base::SyncSocket
temp_socket(
181 IPC::PlatformFileForTransitToPlatformFile(socket_handle
));
183 static_cast<Broker
*>(enter
.object())->ConnectComplete(socket_handle
,
188 // Callback on the host side.
189 // Transfers ownership of the handle to the plugin side. This function must
190 // either successfully call the callback or close the handle.
191 void PPB_Broker_Proxy::ConnectCompleteInHost(int32_t result
,
192 const HostResource
& broker
) {
193 IPC::PlatformFileForTransit foreign_socket_handle
=
194 IPC::InvalidPlatformFileForTransit();
195 if (result
== PP_OK
) {
196 int32_t socket_handle
= PlatformFileToInt(base::kInvalidPlatformFileValue
);
197 EnterHostFromHostResource
<PPB_Broker_API
> enter(broker
);
198 if (enter
.succeeded())
199 result
= enter
.object()->GetHandle(&socket_handle
);
200 DCHECK(result
== PP_OK
||
201 socket_handle
== PlatformFileToInt(base::kInvalidPlatformFileValue
));
203 if (result
== PP_OK
) {
204 foreign_socket_handle
=
205 dispatcher()->ShareHandleWithRemote(IntToPlatformFile(socket_handle
),
207 if (foreign_socket_handle
== IPC::InvalidPlatformFileForTransit()) {
208 result
= PP_ERROR_FAILED
;
209 // Assume the local handle was closed even if the foreign handle could
214 DCHECK(result
== PP_OK
||
215 foreign_socket_handle
== IPC::InvalidPlatformFileForTransit());
217 bool success
= dispatcher()->Send(new PpapiMsg_PPBBroker_ConnectComplete(
218 API_ID_PPB_BROKER
, broker
, foreign_socket_handle
, result
));
220 if (!success
|| result
!= PP_OK
) {
221 // The plugin did not receive the handle, so it must be closed.
222 // The easiest way to clean it up is to just put it in an object
223 // and then close it. This failure case is not performance critical.
224 // The handle could still leak if Send succeeded but the IPC later failed.
225 base::SyncSocket
temp_socket(
226 IPC::PlatformFileForTransitToPlatformFile(foreign_socket_handle
));