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
DuplicateHandle(base::SyncSocket::Handle handle)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
PepperBrokerDispatcherWrapper()50 PepperBrokerDispatcherWrapper::PepperBrokerDispatcherWrapper() {
51 }
52
~PepperBrokerDispatcherWrapper()53 PepperBrokerDispatcherWrapper::~PepperBrokerDispatcherWrapper() {
54 }
55
Init(base::ProcessId broker_pid,const IPC::ChannelHandle & channel_handle)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.
SendHandleToBroker(PP_Instance instance,base::SyncSocket::Handle handle)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
PepperBroker(PluginModule * plugin_module)109 PepperBroker::PepperBroker(PluginModule* plugin_module)
110 : plugin_module_(plugin_module) {
111 DCHECK(plugin_module_);
112
113 plugin_module_->SetBroker(this);
114 }
115
~PepperBroker()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.
AddPendingConnect(PPB_Broker_Impl * client)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
Disconnect(PPB_Broker_Impl * client)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
OnBrokerChannelConnected(base::ProcessId broker_pid,const IPC::ChannelHandle & channel_handle)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
OnBrokerPermissionResult(PPB_Broker_Impl * client,bool result)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
PendingConnection()212 PepperBroker::PendingConnection::PendingConnection() : is_authorized(false) {
213 }
214
~PendingConnection()215 PepperBroker::PendingConnection::~PendingConnection() {
216 }
217
ReportFailureToClients(int error_code)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
ConnectPluginToBroker(PPB_Broker_Impl * client)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