Make ncval-annotate and ncval-stubout executable
[nativeclient.git] / npapi_plugin / srpc / connected_socket.cc
blob94aed22e19a0ec0b522eed74b18898c5c72f882a
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
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
14 * distribution.
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.
33 #include <signal.h>
34 #include <string.h>
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"
45 namespace nacl_srpc {
47 class GlobalSocketMutex {
48 private:
49 NaClMutex mutex_;
50 public:
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)) {
75 return true;
77 return socket->srpc_client_->HasMethod(name);
80 bool ConnectedSocket::Invoke(NPObject *obj,
81 NPIdentifier name,
82 const NPVariant *args,
83 uint32_t arg_count,
84 NPVariant *result) {
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) {
96 return false;
98 memcpy(buffer, NPVARIANT_TO_STRING(args[0]).utf8characters, length);
99 buffer[length] = '\0';
100 name = NPN_GetStringIdentifier(buffer);
101 args = &args[1];
102 --arg_count;
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])) {
109 return false;
111 size_t length = NPVARIANT_TO_STRING(args[0]).utf8length;
112 NPUTF8* file_name = new NPUTF8[length + 1];
113 if (NULL == file_name) {
114 return false;
115 } else {
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);
120 return true;
124 ScopedCatchSignals sigcatcher(
125 (ScopedCatchSignals::SigHandlerType) SignalHandler);
126 if (PLUGIN_SETJMP(socket_env, 1)) {
127 STRINGZ_TO_NPVARIANT("Method invocation raised signal.", *result);
128 return false;
131 // Invoke the specified method of the service.
132 bool rv = socket->srpc_client_->Invoke(name, args, arg_count, result);
133 return rv;
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) {
148 return true;
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.
153 return false;
156 bool ConnectedSocket::GetProperty(NPObject *obj,
157 NPIdentifier name,
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_);
165 } else {
166 // Firefox wants this to return null.
167 NULL_TO_NPVARIANT(*variant);
169 return true;
172 bool ConnectedSocket::SetProperty(NPObject *obj,
173 NPIdentifier name,
174 const NPVariant *variant) {
175 dprintf(("ConnectedSocket::SetProperty(%p, %s, %p)\n",
176 obj, IdentToString(name), variant));
178 return false;
181 ConnectedSocket* ConnectedSocket::New(Plugin* plugin,
182 struct NaClDesc* desc,
183 bool is_srpc_client,
184 ServiceRuntimeInterface* serv_rtm_info,
185 bool is_command_channel) {
186 static NPClass socketClass = {
187 NP_CLASS_STRUCT_VERSION,
188 Allocate,
189 Deallocate,
190 Invalidate,
191 HasMethod,
192 Invoke,
194 HasProperty,
195 GetProperty,
196 SetProperty,
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(),
204 &socketClass));
206 socket->plugin_ = plugin;
207 socket->is_command_channel_ = is_command_channel;
208 NaClDescRef(desc);
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();
219 g_plugin = plugin;
220 #if NACL_LINUX
221 // Linux will need another x11 display for thread safety
222 g_redraw_platform_specific = XOpenDisplay(NULL);
223 #endif
224 g_plugin_mutex.Unlock();
226 plugin->Print("ConnectedSocket: SRPC client on 0x%x\n", (unsigned) desc);
227 } else {
228 socket->srpc_client_ = NULL;
229 socket->signatures_ = NULL;
230 plugin->Print("ConnectedSocket: non-SRPC client on 0x%x\n",
231 (unsigned) desc);
234 return socket;
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();
266 g_plugin = NULL;
267 #if NACL_LINUX
268 if (NULL != g_redraw_platform_specific) {
269 XCloseDisplay(static_cast<Display *>(g_redraw_platform_specific));
271 #endif
272 g_redraw_platform_specific = NULL;
273 g_plugin_mutex.Unlock();
275 // And free the memory for this object.
276 delete socket;
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.
305 delete srpc_client_;
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,
316 NaClSrpcArg** ins,
317 NaClSrpcArg** outs) {
318 int ret;
319 g_plugin_mutex.Lock();
320 if (g_plugin) {
321 #if NACL_OSX
322 g_plugin->VideoRequestRedraw();
323 #else
324 g_plugin->VideoRedrawAsync(g_redraw_platform_specific);
325 #endif // NACL_OSX
326 ret = NACL_SRPC_RESULT_OK;
327 } else {
328 ret = NACL_SRPC_RESULT_BREAK;
330 g_plugin_mutex.Unlock();
331 return ret;
334 static void WINAPI UpcallThread(void *arg) {
335 NaClHandle handle = reinterpret_cast<NaClHandle>(arg);
336 NaClSrpcHandlerDesc handlers[] = {
337 { "upcall::", handleUpcall },
338 { NULL, NULL }
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"));
355 return false;
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"));
361 return false;
363 // Create a socket pair.
364 NaClHandle nh[2];
365 if (0 != NaClSocketPair(nh)) {
366 return false;
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)) {
372 return false;
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.
376 NPVariant args[2];
377 NPVariant result;
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),
383 false,
384 NULL,
385 false);
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,
390 args,
392 &result);
395 } // namespace nacl_srpc