• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ppapi/proxy/ppb_broker_proxy.h"
6 
7 #include "base/bind.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"
19 
20 using ppapi::IntToPlatformFile;
21 using ppapi::PlatformFileToInt;
22 using ppapi::thunk::PPB_Broker_API;
23 
24 namespace ppapi {
25 namespace proxy {
26 
27 class Broker : public PPB_Broker_API, public Resource {
28  public:
29   explicit Broker(const HostResource& resource);
30   virtual ~Broker();
31 
32   // Resource overrides.
33   virtual PPB_Broker_API* AsPPB_Broker_API() OVERRIDE;
34 
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;
39 
40   // Called by the proxy when the host side has completed the request.
41   void ConnectComplete(IPC::PlatformFileForTransit socket_handle,
42                        int32_t result);
43 
44  private:
45   bool called_connect_;
46   scoped_refptr<TrackedCallback> current_connect_callback_;
47 
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_;
53 
54   DISALLOW_COPY_AND_ASSIGN(Broker);
55 };
56 
Broker(const HostResource & resource)57 Broker::Broker(const HostResource& resource)
58     : Resource(OBJECT_IS_PROXY, resource),
59       called_connect_(false),
60       socket_handle_(base::kInvalidPlatformFileValue) {
61 }
62 
~Broker()63 Broker::~Broker() {
64   socket_handle_ = base::kInvalidPlatformFileValue;
65 }
66 
AsPPB_Broker_API()67 PPB_Broker_API* Broker::AsPPB_Broker_API() {
68   return this;
69 }
70 
Connect(scoped_refptr<TrackedCallback> connect_callback)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;
76 
77   current_connect_callback_ = connect_callback;
78   called_connect_ = true;
79 
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;
84 }
85 
GetHandle(int32_t * handle)86 int32_t Broker::GetHandle(int32_t* handle) {
87   if (socket_handle_ == base::kInvalidPlatformFileValue)
88     return PP_ERROR_FAILED;
89   *handle = PlatformFileToInt(socket_handle_);
90   return PP_OK;
91 }
92 
ConnectComplete(IPC::PlatformFileForTransit socket_handle,int32_t result)93 void Broker::ConnectComplete(IPC::PlatformFileForTransit socket_handle,
94                              int32_t result) {
95   if (result == PP_OK) {
96     DCHECK(socket_handle_ == base::kInvalidPlatformFileValue);
97     socket_handle_ = IPC::PlatformFileForTransitToPlatformFile(socket_handle);
98   } else {
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));
104   }
105 
106   if (!TrackedCallback::IsPending(current_connect_callback_)) {
107     // The handle might leak if the plugin never calls GetHandle().
108     return;
109   }
110 
111   current_connect_callback_->Run(result);
112 }
113 
PPB_Broker_Proxy(Dispatcher * dispatcher)114 PPB_Broker_Proxy::PPB_Broker_Proxy(Dispatcher* dispatcher)
115     : InterfaceProxy(dispatcher),
116       callback_factory_(this){
117 }
118 
~PPB_Broker_Proxy()119 PPB_Broker_Proxy::~PPB_Broker_Proxy() {
120 }
121 
122 // static
CreateProxyResource(PP_Instance instance)123 PP_Resource PPB_Broker_Proxy::CreateProxyResource(PP_Instance instance) {
124   PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
125   if (!dispatcher)
126     return 0;
127 
128   HostResource result;
129   dispatcher->Send(new PpapiHostMsg_PPBBroker_Create(
130       API_ID_PPB_BROKER, instance, &result));
131   if (result.is_null())
132     return 0;
133   return (new Broker(result))->GetReference();
134 }
135 
OnMessageReceived(const IPC::Message & msg)136 bool PPB_Broker_Proxy::OnMessageReceived(const IPC::Message& msg) {
137   bool handled = true;
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()
145   return handled;
146 }
147 
OnMsgCreate(PP_Instance instance,HostResource * result_resource)148 void PPB_Broker_Proxy::OnMsgCreate(PP_Instance instance,
149                                    HostResource* result_resource) {
150   if (!dispatcher()->permissions().HasPermission(PERMISSION_PRIVATE))
151     return;
152   thunk::EnterResourceCreation enter(instance);
153   if (enter.succeeded()) {
154     result_resource->SetHostResource(
155         instance,
156         enter.functions()->CreateBroker(instance));
157   }
158 }
159 
OnMsgConnect(const HostResource & broker)160 void PPB_Broker_Proxy::OnMsgConnect(const HostResource& broker) {
161   if (!dispatcher()->permissions().HasPermission(PERMISSION_PRIVATE))
162     return;
163   EnterHostFromHostResourceForceCallback<PPB_Broker_API> enter(
164       broker, callback_factory_,
165       &PPB_Broker_Proxy::ConnectCompleteInHost, broker);
166   if (enter.succeeded())
167     enter.SetResult(enter.object()->Connect(enter.callback()));
168 }
169 
170 // Called in the plugin to handle the connect callback.
171 // The proxy owns the handle and transfers it to the Broker. At that point,
172 // the plugin owns the handle and is responsible for closing it.
173 // The caller guarantees that socket_handle is not valid if result is not PP_OK.
OnMsgConnectComplete(const HostResource & resource,IPC::PlatformFileForTransit socket_handle,int32_t result)174 void PPB_Broker_Proxy::OnMsgConnectComplete(
175     const HostResource& resource,
176     IPC::PlatformFileForTransit socket_handle,
177     int32_t result) {
178   DCHECK(result == PP_OK ||
179          socket_handle == IPC::InvalidPlatformFileForTransit());
180 
181   EnterPluginFromHostResource<PPB_Broker_API> enter(resource);
182   if (enter.failed()) {
183     // As in Broker::ConnectComplete, we need to close the resource on error.
184     base::SyncSocket temp_socket(
185         IPC::PlatformFileForTransitToPlatformFile(socket_handle));
186   } else {
187     static_cast<Broker*>(enter.object())->ConnectComplete(socket_handle,
188                                                           result);
189   }
190 }
191 
192 // Callback on the host side.
193 // Transfers ownership of the handle to the plugin side. This function must
194 // either successfully call the callback or close the handle.
ConnectCompleteInHost(int32_t result,const HostResource & broker)195 void PPB_Broker_Proxy::ConnectCompleteInHost(int32_t result,
196                                              const HostResource& broker) {
197   IPC::PlatformFileForTransit foreign_socket_handle =
198       IPC::InvalidPlatformFileForTransit();
199   if (result == PP_OK) {
200     int32_t socket_handle = PlatformFileToInt(base::kInvalidPlatformFileValue);
201     EnterHostFromHostResource<PPB_Broker_API> enter(broker);
202     if (enter.succeeded())
203       result = enter.object()->GetHandle(&socket_handle);
204     DCHECK(result == PP_OK ||
205            socket_handle == PlatformFileToInt(base::kInvalidPlatformFileValue));
206 
207     if (result == PP_OK) {
208       foreign_socket_handle =
209           dispatcher()->ShareHandleWithRemote(IntToPlatformFile(socket_handle),
210                                               true);
211       if (foreign_socket_handle == IPC::InvalidPlatformFileForTransit()) {
212         result = PP_ERROR_FAILED;
213         // Assume the local handle was closed even if the foreign handle could
214         // not be created.
215       }
216     }
217   }
218   DCHECK(result == PP_OK ||
219          foreign_socket_handle == IPC::InvalidPlatformFileForTransit());
220 
221   bool success = dispatcher()->Send(new PpapiMsg_PPBBroker_ConnectComplete(
222       API_ID_PPB_BROKER, broker, foreign_socket_handle, result));
223 
224   if (!success || result != PP_OK) {
225       // The plugin did not receive the handle, so it must be closed.
226       // The easiest way to clean it up is to just put it in an object
227       // and then close it. This failure case is not performance critical.
228       // The handle could still leak if Send succeeded but the IPC later failed.
229       base::SyncSocket temp_socket(
230           IPC::PlatformFileForTransitToPlatformFile(foreign_socket_handle));
231   }
232 }
233 
234 }  // namespace proxy
235 }  // namespace ppapi
236