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 57 Broker::Broker(const HostResource& resource) 58 : Resource(OBJECT_IS_PROXY, resource), 59 called_connect_(false), 60 socket_handle_(base::kInvalidPlatformFileValue) { 61 } 62 63 Broker::~Broker() { 64 socket_handle_ = base::kInvalidPlatformFileValue; 65 } 66 67 PPB_Broker_API* Broker::AsPPB_Broker_API() { 68 return this; 69 } 70 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 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 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 114 PPB_Broker_Proxy::PPB_Broker_Proxy(Dispatcher* dispatcher) 115 : InterfaceProxy(dispatcher), 116 callback_factory_(this){ 117 } 118 119 PPB_Broker_Proxy::~PPB_Broker_Proxy() { 120 } 121 122 // static 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 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 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 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. 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. 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