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
21namespace content {
22
23namespace {
24
25base::SyncSocket::Handle DuplicateHandle(base::SyncSocket::Handle handle) {
26  base::SyncSocket::Handle out_handle = base::SyncSocket::kInvalidHandle;
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::SyncSocket::kInvalidHandle;
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
50PepperBrokerDispatcherWrapper::PepperBrokerDispatcherWrapper() {}
51
52PepperBrokerDispatcherWrapper::~PepperBrokerDispatcherWrapper() {}
53
54bool PepperBrokerDispatcherWrapper::Init(
55    base::ProcessId broker_pid,
56    const IPC::ChannelHandle& channel_handle) {
57  if (channel_handle.name.empty())
58    return false;
59
60#if defined(OS_POSIX)
61  DCHECK_NE(-1, channel_handle.socket.fd);
62  if (channel_handle.socket.fd == -1)
63    return false;
64#endif
65
66  dispatcher_delegate_.reset(new PepperProxyChannelDelegateImpl);
67  dispatcher_.reset(new ppapi::proxy::BrokerHostDispatcher());
68
69  if (!dispatcher_->InitBrokerWithChannel(dispatcher_delegate_.get(),
70                                          broker_pid,
71                                          channel_handle,
72                                          true)) {  // Client.
73    dispatcher_.reset();
74    dispatcher_delegate_.reset();
75    return false;
76  }
77  dispatcher_->channel()->SetRestrictDispatchChannelGroup(
78      kRendererRestrictDispatchGroup_Pepper);
79  return true;
80}
81
82// Does not take ownership of the local pipe.
83int32_t PepperBrokerDispatcherWrapper::SendHandleToBroker(
84    PP_Instance instance,
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;
90
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;
101  }
102
103  return result;
104}
105
106PepperBroker::PepperBroker(PluginModule* plugin_module)
107    : plugin_module_(plugin_module) {
108  DCHECK(plugin_module_);
109
110  plugin_module_->SetBroker(this);
111}
112
113PepperBroker::~PepperBroker() {
114  ReportFailureToClients(PP_ERROR_ABORTED);
115  plugin_module_->SetBroker(NULL);
116  plugin_module_ = NULL;
117}
118
119// If the channel is not ready, queue the connection.
120void PepperBroker::AddPendingConnect(PPB_Broker_Impl* client) {
121  DCHECK(pending_connects_.find(client) == pending_connects_.end())
122      << "Connect was already called for this client";
123
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
130  // longer using it.
131  AddRef();
132
133  pending_connects_[client].client = client->AsWeakPtr();
134}
135
136void PepperBroker::Disconnect(PPB_Broker_Impl* client) {
137  // Remove the pending connect if one exists. This class will not call client's
138  // callback.
139  pending_connects_.erase(client);
140
141  // TODO(ddorwin): Send message disconnect message using dispatcher_.
142
143  // Release the reference added in Connect().
144  // This must be the last statement because it may delete this object.
145  Release();
146}
147
148void 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);
155    return;
156  }
157
158  dispatcher_.reset(dispatcher.release());
159
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) {
165      ++i;
166      continue;
167    }
168
169    if (weak_ptr.get())
170      ConnectPluginToBroker(weak_ptr.get());
171
172    pending_connects_.erase(i++);
173  }
174}
175
176void PepperBroker::OnBrokerPermissionResult(PPB_Broker_Impl* client,
177                                            bool result) {
178  ClientMap::iterator entry = pending_connects_.find(client);
179  if (entry == pending_connects_.end())
180    return;
181
182  if (!entry->second.client.get()) {
183    // Client has gone away.
184    pending_connects_.erase(entry);
185    return;
186  }
187
188  if (!result) {
189    // Report failure.
190    client->BrokerConnected(
191        ppapi::PlatformFileToInt(base::SyncSocket::kInvalidHandle),
192        PP_ERROR_NOACCESS);
193    pending_connects_.erase(entry);
194    return;
195  }
196
197  if (dispatcher_) {
198    ConnectPluginToBroker(client);
199    pending_connects_.erase(entry);
200    return;
201  }
202
203  // Mark the request as authorized, continue waiting for the broker
204  // connection.
205  DCHECK(!entry->second.is_authorized);
206  entry->second.is_authorized = true;
207}
208
209PepperBroker::PendingConnection::PendingConnection() : is_authorized(false) {}
210
211PepperBroker::PendingConnection::~PendingConnection() {}
212
213void 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();
217       ++i) {
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),
222          error_code);
223    }
224  }
225  pending_connects_.clear();
226}
227
228void PepperBroker::ConnectPluginToBroker(PPB_Broker_Impl* client) {
229  base::SyncSocket::Handle plugin_handle = base::SyncSocket::kInvalidHandle;
230  int32_t result = PP_OK;
231
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());
239
240    // If the broker has its pipe handle, duplicate the plugin's handle.
241    // Otherwise, the plugin's handle will be automatically closed.
242    if (result == PP_OK)
243      plugin_handle = DuplicateHandle(plugin_socket->handle());
244  } else {
245    result = PP_ERROR_FAILED;
246  }
247
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);
254}
255
256}  // namespace content
257