Upstream version 9.38.198.0
[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::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
50 PepperBrokerDispatcherWrapper::PepperBrokerDispatcherWrapper() {}
51
52 PepperBrokerDispatcherWrapper::~PepperBrokerDispatcherWrapper() {}
53
54 bool 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.
83 int32_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
106 PepperBroker::PepperBroker(PluginModule* plugin_module)
107     : plugin_module_(plugin_module) {
108   DCHECK(plugin_module_);
109
110   plugin_module_->SetBroker(this);
111 }
112
113 PepperBroker::~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.
120 void 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
136 void 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
148 void 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
176 void 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
209 PepperBroker::PendingConnection::PendingConnection() : is_authorized(false) {}
210
211 PepperBroker::PendingConnection::~PendingConnection() {}
212
213 void 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
228 void 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