- add sources.
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / pepper_broker.cc
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
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
50 PepperBrokerDispatcherWrapper::PepperBrokerDispatcherWrapper() {
51 }
52
53 PepperBrokerDispatcherWrapper::~PepperBrokerDispatcherWrapper() {
54 }
55
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.
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
109 PepperBroker::PepperBroker(PluginModule* plugin_module)
110     : plugin_module_(plugin_module) {
111   DCHECK(plugin_module_);
112
113   plugin_module_->SetBroker(this);
114 }
115
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.
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
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
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
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
212 PepperBroker::PendingConnection::PendingConnection() : is_authorized(false) {
213 }
214
215 PepperBroker::PendingConnection::~PendingConnection() {
216 }
217
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
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