2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include "native_client/npapi_plugin/srpc/connected_socket.h"
36 #include "native_client/npapi_plugin/srpc/plugin.h"
37 #include "native_client/npapi_plugin/srpc/service_runtime_interface.h"
38 #include "native_client/npapi_plugin/srpc/shared_memory.h"
39 #include "native_client/npapi_plugin/srpc/srpc_client.h"
40 #include "native_client/npapi_plugin/srpc/utility.h"
41 #include "native_client/service_runtime/nacl_desc_imc.h"
42 #include "native_client/service_runtime/nacl_sync.h"
43 #include "native_client/service_runtime/nacl_threads.h"
47 class GlobalSocketMutex
{
51 void Lock() { NaClMutexLock(&mutex_
); }
52 void Unlock() { NaClMutexUnlock(&mutex_
); }
53 GlobalSocketMutex() { NaClMutexCtor(&mutex_
); }
54 ~GlobalSocketMutex() { NaClMutexDtor(&mutex_
); }
57 // TODO: make this work for multiple nacl_av's on a page
58 static Plugin
*g_plugin
= NULL
;
59 static GlobalSocketMutex g_plugin_mutex
;
60 static void *g_redraw_platform_specific
= NULL
;
62 // Define all the static variables.
63 int ConnectedSocket::number_alive
= 0;
64 PLUGIN_JMPBUF
ConnectedSocket::socket_env
;
66 // ConnectedSocket implements a method for each method exported from
67 // the NaCl service runtime.
68 bool ConnectedSocket::HasMethod(NPObject
*obj
, NPIdentifier name
) {
69 ConnectedSocket
* socket
= reinterpret_cast<ConnectedSocket
*>(obj
);
71 dprintf(("ConnectedSocket::HasMethod(%p, %s)\n", obj
, IdentToString(name
)));
73 if ((Plugin::kInvokeIdent
== name
) ||
74 (Plugin::kSetCommandLogIdent
== name
)) {
77 return socket
->srpc_client_
->HasMethod(name
);
80 bool ConnectedSocket::Invoke(NPObject
*obj
,
82 const NPVariant
*args
,
85 ConnectedSocket
* socket
= reinterpret_cast<ConnectedSocket
*>(obj
);
87 dprintf(("ConnectedSocket::Invoke(%p, %s, %d)\n",
88 obj
, IdentToString(name
), arg_count
));
90 // __invoke(string, args) invokes the method named by string.
91 if ((Plugin::kInvokeIdent
== name
) && NPVARIANT_IS_STRING(args
[0])) {
92 const int kBufSize
= 1024;
93 char buffer
[kBufSize
];
94 size_t length
= NPVARIANT_TO_STRING(args
[0]).utf8length
;
95 if (length
> kBufSize
-1) {
98 memcpy(buffer
, NPVARIANT_TO_STRING(args
[0]).utf8characters
, length
);
99 buffer
[length
] = '\0';
100 name
= NPN_GetStringIdentifier(buffer
);
103 } else if (Plugin::kSetCommandLogIdent
== name
) {
104 // __setcommandlog takes a string argument and a number.
105 // The string is the name of the file to log to. The number is how many
106 // method invocations to trace (-1 means unlimited).
107 if ((2 != arg_count
) || !NPVARIANT_IS_STRING(args
[0]) ||
108 !NPVARIANT_IS_INT32(args
[1])) {
111 size_t length
= NPVARIANT_TO_STRING(args
[0]).utf8length
;
112 NPUTF8
* file_name
= new NPUTF8
[length
+ 1];
113 if (NULL
== file_name
) {
116 memcpy(file_name
, NPVARIANT_TO_STRING(args
[0]).utf8characters
, length
);
117 file_name
[length
] = '\0';
118 int max_invocation
= NPVARIANT_TO_INT32(args
[1]);
119 socket
->srpc_client_
->set_command_fp(file_name
, max_invocation
);
124 ScopedCatchSignals
sigcatcher(
125 (ScopedCatchSignals::SigHandlerType
) SignalHandler
);
126 if (PLUGIN_SETJMP(socket_env
, 1)) {
127 STRINGZ_TO_NPVARIANT("Method invocation raised signal.", *result
);
131 // Invoke the specified method of the service.
132 bool rv
= socket
->srpc_client_
->Invoke(name
, args
, arg_count
, result
);
136 void ConnectedSocket::SignalHandler(int value
) {
137 dprintf(("ConnectedSocket::SignalHandler()\n"));
139 PLUGIN_LONGJMP(socket_env
, value
);
142 // ConnectedSocket exports the signature property and the
143 // names of the methods exported from the service.
144 bool ConnectedSocket::HasProperty(NPObject
*obj
, NPIdentifier name
) {
145 dprintf(("ConnectedSocket::HasProperty(%p, %s)\n", obj
, IdentToString(name
)));
147 if (Plugin::kSignaturesIdent
== name
) {
150 // Safari apparently wants us to return false here. This means programmers
151 // will have to use a JavaScript function object to wrap NativeClient
152 // methods they intend to invoke indirectly.
156 bool ConnectedSocket::GetProperty(NPObject
*obj
,
158 NPVariant
*variant
) {
159 dprintf(("ConnectedSocket::GetProperty(%p, %s)\n", obj
, IdentToString(name
)));
161 if (Plugin::kSignaturesIdent
== name
) {
162 ConnectedSocket
* socket
= reinterpret_cast<ConnectedSocket
*>(obj
);
163 OBJECT_TO_NPVARIANT(socket
->signatures_
, *variant
);
164 NPN_RetainObject(socket
->signatures_
);
166 // Firefox wants this to return null.
167 NULL_TO_NPVARIANT(*variant
);
172 bool ConnectedSocket::SetProperty(NPObject
*obj
,
174 const NPVariant
*variant
) {
175 dprintf(("ConnectedSocket::SetProperty(%p, %s, %p)\n",
176 obj
, IdentToString(name
), variant
));
181 ConnectedSocket
* ConnectedSocket::New(Plugin
* plugin
,
182 struct NaClDesc
* desc
,
184 ServiceRuntimeInterface
* serv_rtm_info
,
185 bool is_command_channel
) {
186 static NPClass socketClass
= {
187 NP_CLASS_STRUCT_VERSION
,
199 dprintf(("ConnectedSocket::New(%p, %p, %d, %p, %d)\n", plugin
, desc
,
200 is_srpc_client
, serv_rtm_info
, is_command_channel
));
202 ConnectedSocket
* socket
=
203 reinterpret_cast<ConnectedSocket
*>(NPN_CreateObject(plugin
->npp(),
206 socket
->plugin_
= plugin
;
207 socket
->is_command_channel_
= is_command_channel
;
209 socket
->desc_
= desc
;
210 socket
->service_runtime_info_
= serv_rtm_info
;
211 if (is_srpc_client
) {
212 // Get SRPC client interface going over socket.
213 socket
->srpc_client_
= new SrpcClient(plugin
, socket
);
214 // Prefetch the signatures for use by clients.
215 socket
->signatures_
= socket
->srpc_client_
->GetSignatureObject();
216 // only open global x11 display variable on socket with service_runtime_info
217 if (NULL
!= socket
->service_runtime_info_
) {
218 g_plugin_mutex
.Lock();
221 // Linux will need another x11 display for thread safety
222 g_redraw_platform_specific
= XOpenDisplay(NULL
);
224 g_plugin_mutex
.Unlock();
226 plugin
->Print("ConnectedSocket: SRPC client on 0x%x\n", (unsigned) desc
);
228 socket
->srpc_client_
= NULL
;
229 socket
->signatures_
= NULL
;
230 plugin
->Print("ConnectedSocket: non-SRPC client on 0x%x\n",
237 NPObject
*ConnectedSocket::Allocate(NPP npp
, NPClass
*theClass
) {
238 dprintf(("ConnectedSocket::Allocate(%d)\n", ++number_alive
));
240 return new ConnectedSocket(npp
);
243 void ConnectedSocket::Deallocate(NPObject
*obj
) {
244 ConnectedSocket
* socket
= reinterpret_cast<ConnectedSocket
*>(obj
);
246 dprintf(("ConnectedSocket::Deallocate(%p, %d)\n", obj
, --number_alive
));
248 // Release the contained descriptor.
249 if (NULL
!= socket
->desc_
) {
250 NaClDescUnref(socket
->desc_
);
251 socket
->desc_
= NULL
;
253 // Release the other NPAPI objects.
254 if (socket
->signatures_
) {
255 NPN_ReleaseObject(socket
->signatures_
);
256 socket
->signatures_
= NULL
;
258 if (socket
->plugin_
) {
259 socket
->plugin_
= NULL
;
262 // only close global x11 display on socket with service_runtime_info
263 // TODO maybe a cleaner way to do this
264 if (NULL
!= socket
->service_runtime_info_
) {
265 g_plugin_mutex
.Lock();
268 if (NULL
!= g_redraw_platform_specific
) {
269 XCloseDisplay(static_cast<Display
*>(g_redraw_platform_specific
));
272 g_redraw_platform_specific
= NULL
;
273 g_plugin_mutex
.Unlock();
275 // And free the memory for this object.
279 void ConnectedSocket::Invalidate(NPObject
*obj
) {
280 ConnectedSocket
* socket
= reinterpret_cast<ConnectedSocket
*>(obj
);
282 dprintf(("ConnectedSocket::Invalidate(%p)\n", socket
));
284 // After invalidation, the browser does not respect reference counting,
285 // so we shut down here what we can and prevent attempts to shut down
286 // other linked structures in Deallocate.
288 // Unreference the socket object.
289 NaClDescUnref(socket
->desc_
);
290 // Block access to the other contained structures.
291 socket
->desc_
= NULL
;
292 socket
->signatures_
= NULL
;
293 socket
->plugin_
= NULL
;
296 ConnectedSocket::ConnectedSocket(NPP npp
) : UnknownHandle(npp
),
297 is_command_channel_(false) {
298 dprintf(("ConnectedSocket::ConnectedSocket(%p)\n", this));
301 ConnectedSocket::~ConnectedSocket() {
302 dprintf(("ConnectedSocket::~ConnectedSocket(%p)\n", this));
304 // Free the SRPC connection.
306 // Free the rpc method descriptors and terminate the connection to
307 // the service runtime instance.
308 dprintf(("ConnectedSocket(%p): deleting SRI %p\n",
309 this, service_runtime_info_
));
310 if (service_runtime_info_
) {
311 delete service_runtime_info_
;
315 static int handleUpcall(NaClSrpcChannel
* channel
,
317 NaClSrpcArg
** outs
) {
319 g_plugin_mutex
.Lock();
322 g_plugin
->VideoRequestRedraw();
324 g_plugin
->VideoRedrawAsync(g_redraw_platform_specific
);
326 ret
= NACL_SRPC_RESULT_OK
;
328 ret
= NACL_SRPC_RESULT_BREAK
;
330 g_plugin_mutex
.Unlock();
334 static void WINAPI
UpcallThread(void *arg
) {
335 NaClHandle handle
= reinterpret_cast<NaClHandle
>(arg
);
336 NaClSrpcHandlerDesc handlers
[] = {
337 { "upcall::", handleUpcall
},
341 dprintf(("ConnectedSocket::UpcallThread(%p)\n", arg
));
342 // Run the SRPC server.
343 NaClSrpcServerLoop(handle
, handlers
, NULL
);
344 dprintf(("ConnectedSocket::UpcallThread: End\n"));
347 // Support for initializing the NativeClient multimedia system.
348 bool ConnectedSocket::InitializeModuleMultimedia(SharedMemory
* video_shm
) {
349 dprintf(("ConnectedSocket::InitializeModuleMultimedia(%p)\n", this));
351 // If there is no display shared memory region, don't initialize.
352 // TODO: make sure this makes sense with NPAPI call order.
353 if (NULL
== video_shm
) {
354 dprintf(("ConnectedSocket::InitializeModuleMultimedia: No video_shm.\n"));
357 // Determine whether the NaCl module has reported having a method called
358 // "nacl_multimedia_bridge".
359 if (!srpc_client_
->HasMethod(Plugin::kNaClMultimediaBridgeIdent
)) {
360 dprintf(("No nacl_multimedia_bridge method was found.\n"));
363 // Create a socket pair.
365 if (0 != NaClSocketPair(nh
)) {
368 // Start a thread to handle the upcalls.
369 struct NaClThread nacl_thread
;
370 void* handle
= reinterpret_cast<void*>(nh
[0]);
371 if (!NaClThreadCtor(&nacl_thread
, UpcallThread
, handle
, 128 << 10)) {
374 // The arguments to nacl_multimedia_bridge are an object for the video
375 // shared memory and a connected socket to send the plugin's up calls to.
378 struct NaClDescImcDesc sr_desc
;
379 NaClDescImcDescCtor(&sr_desc
, nh
[1]);
380 ConnectedSocket
* sock
=
381 ConnectedSocket::New(plugin_
,
382 reinterpret_cast<struct NaClDesc
*>(&sr_desc
),
386 dprintf(("CS:IMM args %p result %p\n", args
, &result
));
387 OBJECT_TO_NPVARIANT(video_shm
, args
[0]);
388 OBJECT_TO_NPVARIANT(sock
, args
[1]);
389 return srpc_client_
->Invoke(Plugin::kNaClMultimediaBridgeIdent
,
395 } // namespace nacl_srpc