11 #include <npruntime.h>
13 #include "native_client/intermodule_comm/nacl_imc_c.h"
14 #include "native_client/service_runtime/include/sys/fcntl.h"
15 #include "native_client/service_runtime/nacl_desc_base.h"
16 #include "native_client/service_runtime/nacl_desc_imc.h"
17 #include "native_client/service_runtime/nacl_desc_io.h"
18 #include "native_client/service_runtime/nrd_xfer_lib/nrd_all_modules.h"
19 #include "native_client/service_runtime/nrd_xfer_lib/nrd_xfer.h"
20 #include "native_client/service_runtime/nrd_xfer_lib/nrd_xfer_effector.h"
23 static NPNetscapeFuncs g_funcs
;
24 static NPIdentifier ident_launch
;
25 static NPIdentifier ident_get_file
;
26 static NPIdentifier ident_send
;
27 static NPIdentifier ident_length
;
29 #define NPN_CreateObject (g_funcs.createobject)
30 #define NPN_RetainObject (g_funcs.retainobject)
31 #define NPN_ReleaseObject (g_funcs.releaseobject)
32 #define NPN_ReleaseVariantValue (g_funcs.releasevariantvalue)
33 #define NPN_InvokeDefault (g_funcs.invokeDefault)
34 #define NPN_GetProperty (g_funcs.getproperty)
35 #define NPN_UTF8FromIdentifier (g_funcs.utf8fromidentifier)
36 #define NPN_GetStringIdentifier (g_funcs.getstringidentifier)
37 #define NPN_GetIntIdentifier (g_funcs.getintidentifier)
38 #define NPN_GetURLNotify (g_funcs.geturlnotify)
39 #define NPN_PluginThreadAsyncCall (g_funcs.pluginthreadasynccall)
42 static void PrintIdentifier(NPIdentifier ident
)
44 char *str
= NPN_UTF8FromIdentifier(ident
);
45 printf(" ident = \"%s\"\n", str
);
50 IsType(NPObject
*npobj
, NPClass
*npclass
)
52 /* The underscore in "_class" suggests this member is not part of
53 the public API, but it could just be to prevent a clash with the
55 return npobj
->_class
== npclass
;
59 /* Mozilla does not provide default behaviour for these NoOp_* methods
60 if NULL is provided instead in the NPClass. */
63 NoOp_HasMethod(NPObject
* obj
, NPIdentifier methodName
)
69 NoOp_HasProperty(NPObject
*obj
, NPIdentifier propertyName
)
75 NoOp_GetProperty(NPObject
*obj
, NPIdentifier propertyName
, NPVariant
*result
)
82 struct NPObject header
;
83 struct NaClDesc
*desc
;
84 /* We keep a filename to pass to sel_ldr, but there's a chance the
85 file could be deleted from the cache.
86 TODO: pass FDs to sel_ldr instead. */
91 FileObj_Allocate(NPP npp
, NPClass
*npclass
)
93 struct FileObj
*obj
= malloc(sizeof(struct FileObj
));
98 FileObj_Deallocate(NPObject
*npobj
)
100 struct FileObj
*obj
= (struct FileObj
*) npobj
;
101 NaClDescUnref(obj
->desc
);
106 static NPClass FileObj_Vtable
= {
107 .structVersion
= NP_CLASS_STRUCT_VERSION
,
108 .allocate
= FileObj_Allocate
,
109 .deallocate
= FileObj_Deallocate
,
111 .hasMethod
= NoOp_HasMethod
,
113 .invokeDefault
= NULL
,
114 .hasProperty
= NoOp_HasProperty
,
115 .getProperty
= NoOp_GetProperty
,
117 .removeProperty
= NULL
,
122 struct NPObject header
;
124 struct NaClDesc
*desc
;
128 SendObj_Allocate(NPP npp
, NPClass
*npclass
)
130 struct SendObj
*obj
= malloc(sizeof(struct SendObj
));
136 SendObj_Deallocate(NPObject
*npobj
)
138 struct SendObj
*obj
= (struct SendObj
*) npobj
;
139 NaClDescUnref(obj
->desc
);
144 SendObj_HasMethod(NPObject
* npobj
, NPIdentifier methodName
)
146 return methodName
== ident_send
;
150 ConvertDescArray(NPP plugin
, NPObject
*array
, struct NaClImcTypedMsgHdr
*header
)
153 if(!NPN_GetProperty(plugin
, array
, ident_length
, &length_np
) ||
154 !NPVARIANT_IS_INT32(length_np
)) {
155 NPN_ReleaseVariantValue(&length_np
);
158 int length
= NPVARIANT_TO_INT32(length_np
);
159 NPN_ReleaseVariantValue(&length_np
);
161 /* Firstly, there will be a limit to the number of capabilities we
162 can send in a message. Secondly, the array is not necessarily a
163 primitive Javascript array; its .length property could be much
164 larger than the amount of space Javascript is normally allowed to
169 struct NaClDesc
**descs
= malloc(sizeof(struct NaClDesc
*) * length
);
173 for(i
= 0; i
< length
; i
++) {
174 NPIdentifier index_id
= NPN_GetIntIdentifier(i
);
176 if(!NPN_GetProperty(plugin
, array
, index_id
, &element
) ||
177 !NPVARIANT_IS_OBJECT(element
))
178 return false; /* TODO: clear up */
179 NPObject
*file_npobj
= NPVARIANT_TO_OBJECT(element
);
180 if(!IsType(file_npobj
, &FileObj_Vtable
))
181 return false; /* TODO: clear up */
182 struct FileObj
*file_obj
= (struct FileObj
*) file_npobj
;
183 NaClDescRef(file_obj
->desc
);
184 descs
[i
] = file_obj
->desc
;
185 NPN_ReleaseVariantValue(&element
);
187 header
->ndescv
= descs
;
188 header
->ndesc_length
= length
;
193 SendObj_Invoke(NPObject
*npobj
, NPIdentifier methodName
,
194 const NPVariant
*args
, uint32_t argCount
, NPVariant
*result
)
196 struct SendObj
*obj
= (struct SendObj
*) npobj
;
197 if(methodName
== ident_send
) {
199 NPVARIANT_IS_STRING(args
[0]) &&
200 NPVARIANT_IS_OBJECT(args
[1])) {
201 NPString message_data
= NPVARIANT_TO_STRING(args
[0]);
202 NPObject
*caps_array
= NPVARIANT_TO_OBJECT(args
[1]);
203 struct NaClNrdXferEffector effector
;
204 if(!NaClNrdXferEffectorCtor(&effector
, obj
->desc
))
206 struct NaClImcMsgIoVec iovec
;
207 struct NaClImcTypedMsgHdr header
;
208 if(!ConvertDescArray(obj
->plugin
, caps_array
, &header
))
210 /* TODO: allow sending any data, not just UTF-8 */
211 iovec
.base
= (char *) message_data
.utf8characters
;
212 iovec
.length
= message_data
.utf8length
;
214 header
.iov_length
= 1;
216 NaClImcSendTypedMessage(obj
->desc
, (struct NaClDescEffector
*) &effector
,
219 for(i
= 0; i
< header
.ndesc_length
; i
++)
220 NaClDescUnref(header
.ndescv
[i
]);
222 effector
.base
.vtbl
->Dtor(&effector
.base
);
224 VOID_TO_NPVARIANT(*result
);
230 static NPClass SendObj_Vtable
= {
231 .structVersion
= NP_CLASS_STRUCT_VERSION
,
232 .allocate
= SendObj_Allocate
,
233 .deallocate
= SendObj_Deallocate
,
235 .hasMethod
= SendObj_HasMethod
,
236 .invoke
= SendObj_Invoke
,
237 .invokeDefault
= NULL
,
238 .hasProperty
= NoOp_HasProperty
,
239 .getProperty
= NoOp_GetProperty
,
241 .removeProperty
= NULL
,
246 struct NPObject header
;
251 LauncherObj_Allocate(NPP npp
, NPClass
*npclass
)
253 struct LauncherObj
*obj
= malloc(sizeof(struct LauncherObj
));
259 LauncherObj_HasMethod(NPObject
* obj
, NPIdentifier methodName
)
261 return (methodName
== ident_launch
||
262 methodName
== ident_get_file
);
265 struct ReceiveCallbackArgs
{
273 DoReceiveCallback(void *handle
)
275 struct ReceiveCallbackArgs
*call
= handle
;
278 /* TODO: don't assume this data is UTF-8 */
279 STRINGN_TO_NPVARIANT(call
->buffer
, call
->size
, args
[0]);
280 NPN_InvokeDefault(call
->plugin
, call
->callback
, args
, 1, &result
);
281 NPN_ReleaseVariantValue(&result
);
283 /* We don't do NPN_ReleaseObject() on the callback here because we
284 couldn't do NPN_RetainObject() earlier. */
287 struct ReceiveThreadArgs
{
290 struct NaClDesc
*desc
;
294 ReceiveThread(void *handle
)
296 /* We create one thread for every socket we listen on. It would be
297 more efficient to have one thread using poll(). NPAPI doesn't
298 provide a way for us to share the browser's event loop. */
299 struct ReceiveThreadArgs
*args
= handle
;
300 struct NaClNrdXferEffector effector
;
301 if(!NaClNrdXferEffectorCtor(&effector
, args
->desc
))
304 struct ReceiveCallbackArgs
*call
=
305 malloc(sizeof(struct ReceiveCallbackArgs
));
308 struct NaClImcMsgIoVec iovec
;
309 struct NaClImcTypedMsgHdr header
;
310 iovec
.base
= call
->buffer
;
311 iovec
.length
= sizeof(call
->buffer
);
313 header
.iov_length
= 1;
314 header
.ndescv
= NULL
;
315 header
.ndesc_length
= 0;
318 NaClImcRecvTypedMessage(args
->desc
, (struct NaClDescEffector
*) &effector
,
324 /* We can't call NPN_RetainObject() here because it is not safe to
325 call it from this thread. We assume that
326 NPN_PluginThreadAsyncCall() schedules functions to be called in
327 order, so that the function is not freed until later. */
328 call
->plugin
= args
->plugin
;
329 call
->callback
= args
->callback
;
330 call
->size
= got_bytes
;
331 NPN_PluginThreadAsyncCall(args
->plugin
, DoReceiveCallback
, call
);
333 effector
.base
.vtbl
->Dtor(&effector
.base
);
334 NaClDescUnref(args
->desc
); /* Supposed to be thread safe */
335 NPN_PluginThreadAsyncCall(args
->plugin
, (void (*)(void *)) NPN_ReleaseObject
,
342 ConvertArgvArray(NPP plugin
, NPObject
*array
,
343 char ***result_argv
, int *result_size
)
346 if(!NPN_GetProperty(plugin
, array
, ident_length
, &length_np
) ||
347 !NPVARIANT_IS_INT32(length_np
)) {
348 NPN_ReleaseVariantValue(&length_np
);
351 int length
= NPVARIANT_TO_INT32(length_np
);
352 NPN_ReleaseVariantValue(&length_np
);
354 /* Arbitrary limit */
358 char **argv
= malloc(sizeof(char *) * length
);
362 for(i
= 0; i
< length
; i
++) {
363 NPIdentifier index_id
= NPN_GetIntIdentifier(i
);
365 if(!NPN_GetProperty(plugin
, array
, index_id
, &element
) ||
366 !NPVARIANT_IS_STRING(element
))
367 return false; /* TODO: clear up */
368 NPString string
= NPVARIANT_TO_STRING(element
);
369 char *copy
= malloc(string
.utf8length
+ 1);
371 return false; /* TODO: clear up */
372 /* TODO: allow sending any data, not just UTF-8 */
373 memcpy(copy
, string
.utf8characters
, string
.utf8length
);
374 copy
[string
.utf8length
] = 0;
376 NPN_ReleaseVariantValue(&element
);
379 *result_size
= length
;
383 static struct NaClDesc
*
384 LaunchProcess(NPP plugin
, const char *executable
, char **argv
, int argv_size
,
385 NPObject
*receive_callback
)
388 if(NaClSocketPair(socks
) < 0) {
389 perror("socketpair");
397 return NULL
; /* TODO: report error */
402 snprintf(fd_buffer
, sizeof(fd_buffer
), "%i:%i", 3, socks
[0]);
404 int args_len
= 9 + argv_size
;
405 const char *args
[args_len
];
407 args
[i
++] = "sel_ldr";
409 args
[i
++] = fd_buffer
;
411 args
[i
++] = "NACL_FD=3";
413 args
[i
++] = executable
;
416 for(j
= 0; j
< argv_size
; j
++)
419 assert(i
== args_len
);
421 execvp("sel_ldr", (char **) args
);
426 if(fcntl(socks
[1], F_SETFD
, FD_CLOEXEC
) < 0) {
431 struct NaClDesc
*desc
= malloc(sizeof(struct NaClDescImcDesc
));
432 if(!NaClDescImcDescCtor((struct NaClDescImcDesc
*) desc
, socks
[1]))
435 struct ReceiveThreadArgs
*args
= malloc(sizeof(struct ReceiveThreadArgs
));
439 NPN_RetainObject(receive_callback
);
440 args
->plugin
= plugin
;
442 args
->callback
= receive_callback
;
445 pthread_attr_init(&attr
);
446 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
447 if(pthread_create(&thread_id
, &attr
, ReceiveThread
, args
) != 0)
454 LauncherObj_Invoke(NPObject
*npobj
, NPIdentifier methodName
,
455 const NPVariant
*args
, uint32_t argCount
, NPVariant
*result
)
457 struct LauncherObj
*obj
= (struct LauncherObj
*) npobj
;
458 if(methodName
== ident_launch
) {
460 NPVARIANT_IS_OBJECT(args
[0]) &&
461 NPVARIANT_IS_OBJECT(args
[1]) &&
462 NPVARIANT_IS_OBJECT(args
[2])) {
463 NPObject
*file_npobj
= NPVARIANT_TO_OBJECT(args
[0]);
464 NPObject
*argv_obj
= NPVARIANT_TO_OBJECT(args
[1]);
465 NPObject
*receive_callback
= NPVARIANT_TO_OBJECT(args
[2]);
468 if(IsType(file_npobj
, &FileObj_Vtable
) &&
469 ConvertArgvArray(obj
->plugin
, argv_obj
, &argv
, &argv_size
)) {
470 struct FileObj
*file_obj
= (struct FileObj
*) file_npobj
;
471 struct NaClDesc
*desc
= LaunchProcess(obj
->plugin
, file_obj
->filename
,
475 for(i
= 0; i
< argv_size
; i
++)
480 NPObject
*send_npobj
= NPN_CreateObject(obj
->plugin
, &SendObj_Vtable
);
481 struct SendObj
*send_obj
= (struct SendObj
*) send_npobj
;
482 send_obj
->desc
= desc
;
483 OBJECT_TO_NPVARIANT(send_npobj
, *result
);
488 VOID_TO_NPVARIANT(*result
);
491 if(methodName
== ident_get_file
) {
493 NPVARIANT_IS_STRING(args
[0]) &&
494 NPVARIANT_IS_OBJECT(args
[1])) {
495 /* TODO: do same-origin check (assuming they are useful). */
496 NPString url
= NPVARIANT_TO_STRING(args
[0]);
497 NPObject
*callback
= NPVARIANT_TO_OBJECT(args
[1]);
498 NPN_RetainObject(callback
);
499 /* TODO: I don't think utf8characters is guaranteed to be
501 NPError err
= NPN_GetURLNotify(obj
->plugin
, url
.utf8characters
,
504 /* TODO: handle errors */
505 VOID_TO_NPVARIANT(*result
);
511 static NPClass LauncherObj_Vtable
= {
512 .structVersion
= NP_CLASS_STRUCT_VERSION
,
513 .allocate
= LauncherObj_Allocate
,
516 .hasMethod
= LauncherObj_HasMethod
,
517 .invoke
= LauncherObj_Invoke
,
518 .invokeDefault
= NULL
,
519 .hasProperty
= NoOp_HasProperty
,
520 .getProperty
= NoOp_GetProperty
,
522 .removeProperty
= NULL
,
527 NPP_New(NPMIMEType pluginType
, NPP instance
, uint16 mode
,
528 int16 argc
, char **argn
, char **argv
, NPSavedData
*saved
)
530 return NPERR_NO_ERROR
;
534 NPP_Destroy(NPP instance
, NPSavedData
**save
)
536 /* TODO: kill sel_ldr subprocesses, and do waitpid() to avoid a zombie */
537 return NPERR_NO_ERROR
;
541 NPP_GetValue(NPP instance
, NPPVariable variable
, void *result
)
544 case NPPVpluginScriptableNPObject
:
546 NPObject
*obj
= NPN_CreateObject(instance
, &LauncherObj_Vtable
);
547 *(NPObject
**) result
= obj
;
551 return NPERR_GENERIC_ERROR
;
553 return NPERR_NO_ERROR
;
557 NPP_NewStream(NPP instance
, NPMIMEType type
, NPStream
*stream
,
558 NPBool seekable
, uint16
*stype
)
560 *stype
= NP_ASFILEONLY
;
561 return NPERR_NO_ERROR
;
565 NPP_StreamAsFile(NPP instance
, NPStream
*stream
, const char *filename
)
567 /* TODO: this function calls LOG_FATAL on error; not good */
568 struct NaClDesc
*desc
= (struct NaClDesc
*)
569 NaClDescIoDescOpen((char *) filename
, NACL_ABI_O_RDONLY
, 0);
573 NPObject
*callback
= stream
->notifyData
;
574 NPObject
*file_npobj
= NPN_CreateObject(instance
, &FileObj_Vtable
);
575 struct FileObj
*file_obj
= (struct FileObj
*) file_npobj
;
576 file_obj
->desc
= desc
;
577 file_obj
->filename
= strdup(filename
);
578 if(file_obj
->filename
== NULL
)
582 OBJECT_TO_NPVARIANT(file_npobj
, args
[0]);
583 bool success
= NPN_InvokeDefault(instance
, callback
, args
, 1, &result
);
584 NPN_ReleaseVariantValue(&result
);
585 NPN_ReleaseObject(callback
);
586 NPN_ReleaseObject(file_npobj
);
590 NPP_URLNotify(NPP instance
, const char *url
, NPReason reason
, void *notifyData
)
592 /* TODO: handle file not found errors */
597 NP_Initialize(NPNetscapeFuncs
*funcs
, NPPluginFuncs
*callbacks
)
600 callbacks
->newp
= NPP_New
;
601 callbacks
->destroy
= NPP_Destroy
;
602 callbacks
->getvalue
= NPP_GetValue
;
603 callbacks
->newstream
= NPP_NewStream
;
604 callbacks
->asfile
= NPP_StreamAsFile
;
605 callbacks
->urlnotify
= NPP_URLNotify
;
607 ident_launch
= NPN_GetStringIdentifier("launch");
608 ident_get_file
= NPN_GetStringIdentifier("get_file");
609 ident_send
= NPN_GetStringIdentifier("send");
610 ident_length
= NPN_GetStringIdentifier("length");
612 NaClNrdAllModulesInit();
614 return NPERR_NO_ERROR
;
618 NP_GetMIMEDescription(void)
620 return "application/x-nacl-imc::Native Client IMC plugin";