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"
26 static NPNetscapeFuncs g_funcs
;
27 static NPIdentifier ident_launch
;
28 static NPIdentifier ident_get_file
;
29 static NPIdentifier ident_send
;
30 static NPIdentifier ident_length
;
32 #define NPN_CreateObject (g_funcs.createobject)
33 #define NPN_RetainObject (g_funcs.retainobject)
34 #define NPN_ReleaseObject (g_funcs.releaseobject)
35 #define NPN_ReleaseVariantValue (g_funcs.releasevariantvalue)
36 #define NPN_InvokeDefault (g_funcs.invokeDefault)
37 #define NPN_GetProperty (g_funcs.getproperty)
38 #define NPN_UTF8FromIdentifier (g_funcs.utf8fromidentifier)
39 #define NPN_GetStringIdentifier (g_funcs.getstringidentifier)
40 #define NPN_GetIntIdentifier (g_funcs.getintidentifier)
41 #define NPN_GetURLNotify (g_funcs.geturlnotify)
42 #define NPN_PluginThreadAsyncCall (g_funcs.pluginthreadasynccall)
45 static void PrintIdentifier(NPIdentifier ident
)
47 char *str
= NPN_UTF8FromIdentifier(ident
);
48 print((" ident = \"%s\"\n", str
));
53 IsType(NPObject
*npobj
, NPClass
*npclass
)
55 /* The underscore in "_class" suggests this member is not part of
56 the public API, but it could just be to prevent a clash with the
58 return npobj
->_class
== npclass
;
62 /* Mozilla does not provide default behaviour for these NoOp_* methods
63 if NULL is provided instead in the NPClass. */
66 NoOp_HasMethod(NPObject
* obj
, NPIdentifier methodName
)
72 NoOp_HasProperty(NPObject
*obj
, NPIdentifier propertyName
)
74 print(("Obj_HasProperty\n"));
75 PrintIdentifier(propertyName
);
80 NoOp_GetProperty(NPObject
*obj
, NPIdentifier propertyName
, NPVariant
*result
)
82 print(("Obj_GetProperty\n"));
88 struct NPObject header
;
89 struct NaClDesc
*desc
;
90 /* We keep a filename to pass to sel_ldr, but there's a chance the
91 file could be deleted from the cache.
92 TODO: pass FDs to sel_ldr instead. */
97 FileObj_Allocate(NPP npp
, NPClass
*npclass
)
99 print(("Obj_Allocate\n"));
100 struct FileObj
*obj
= malloc(sizeof(struct FileObj
));
105 FileObj_Deallocate(NPObject
*npobj
)
107 struct FileObj
*obj
= (struct FileObj
*) npobj
;
108 NaClDescUnref(obj
->desc
);
111 print(("FileObj_Deallocate\n"));
114 static NPClass FileObj_Vtable
= {
115 .structVersion
= NP_CLASS_STRUCT_VERSION
,
116 .allocate
= FileObj_Allocate
,
117 .deallocate
= FileObj_Deallocate
,
119 .hasMethod
= NoOp_HasMethod
,
121 .invokeDefault
= NULL
,
122 .hasProperty
= NoOp_HasProperty
,
123 .getProperty
= NoOp_GetProperty
,
125 .removeProperty
= NULL
,
130 struct NPObject header
;
132 struct NaClDesc
*desc
;
136 SendObj_Allocate(NPP npp
, NPClass
*npclass
)
138 print(("Obj_Allocate\n"));
139 struct SendObj
*obj
= malloc(sizeof(struct SendObj
));
145 SendObj_Deallocate(NPObject
*npobj
)
147 struct SendObj
*obj
= (struct SendObj
*) npobj
;
148 NaClDescUnref(obj
->desc
);
150 print(("SendObj_Deallocate\n"));
154 SendObj_HasMethod(NPObject
* npobj
, NPIdentifier methodName
)
156 return methodName
== ident_send
;
160 ConvertDescArray(NPP plugin
, NPObject
*array
, struct NaClImcTypedMsgHdr
*header
)
163 if(!NPN_GetProperty(plugin
, array
, ident_length
, &length_np
) ||
164 !NPVARIANT_IS_INT32(length_np
)) {
165 NPN_ReleaseVariantValue(&length_np
);
168 int length
= NPVARIANT_TO_INT32(length_np
);
169 NPN_ReleaseVariantValue(&length_np
);
171 /* Firstly, there will be a limit to the number of capabilities we
172 can send in a message. Secondly, the array is not necessarily a
173 primitive Javascript array; its .length property could be much
174 larger than the amount of space Javascript is normally allowed to
179 struct NaClDesc
**descs
= malloc(sizeof(struct NaClDesc
*) * length
);
183 for(i
= 0; i
< length
; i
++) {
184 NPIdentifier index_id
= NPN_GetIntIdentifier(i
);
186 if(!NPN_GetProperty(plugin
, array
, index_id
, &element
) ||
187 !NPVARIANT_IS_OBJECT(element
))
188 return false; /* TODO: clear up */
189 NPObject
*file_npobj
= NPVARIANT_TO_OBJECT(element
);
190 if(!IsType(file_npobj
, &FileObj_Vtable
))
191 return false; /* TODO: clear up */
192 struct FileObj
*file_obj
= (struct FileObj
*) file_npobj
;
193 NaClDescRef(file_obj
->desc
);
194 descs
[i
] = file_obj
->desc
;
195 NPN_ReleaseVariantValue(&element
);
197 header
->ndescv
= descs
;
198 header
->ndesc_length
= length
;
203 SendObj_Invoke(NPObject
*npobj
, NPIdentifier methodName
,
204 const NPVariant
*args
, uint32_t argCount
, NPVariant
*result
)
206 struct SendObj
*obj
= (struct SendObj
*) npobj
;
207 print(("Obj_Invoke %i\n", argCount
));
208 PrintIdentifier(methodName
);
209 if(methodName
== ident_send
) {
211 NPVARIANT_IS_STRING(args
[0]) &&
212 NPVARIANT_IS_OBJECT(args
[1])) {
213 NPString message_data
= NPVARIANT_TO_STRING(args
[0]);
214 NPObject
*caps_array
= NPVARIANT_TO_OBJECT(args
[1]);
215 struct NaClNrdXferEffector effector
;
216 if(!NaClNrdXferEffectorCtor(&effector
, obj
->desc
))
218 struct NaClImcMsgIoVec iovec
;
219 struct NaClImcTypedMsgHdr header
;
220 if(!ConvertDescArray(obj
->plugin
, caps_array
, &header
))
222 /* TODO: allow sending any data, not just UTF-8 */
223 iovec
.base
= (char *) message_data
.utf8characters
;
224 iovec
.length
= message_data
.utf8length
;
226 header
.iov_length
= 1;
228 NaClImcSendTypedMessage(obj
->desc
, (struct NaClDescEffector
*) &effector
,
231 for(i
= 0; i
< header
.ndesc_length
; i
++)
232 NaClDescUnref(header
.ndescv
[i
]);
234 effector
.base
.vtbl
->Dtor(&effector
.base
);
236 VOID_TO_NPVARIANT(*result
);
242 static NPClass SendObj_Vtable
= {
243 .structVersion
= NP_CLASS_STRUCT_VERSION
,
244 .allocate
= SendObj_Allocate
,
245 .deallocate
= SendObj_Deallocate
,
247 .hasMethod
= SendObj_HasMethod
,
248 .invoke
= SendObj_Invoke
,
249 .invokeDefault
= NULL
,
250 .hasProperty
= NoOp_HasProperty
,
251 .getProperty
= NoOp_GetProperty
,
253 .removeProperty
= NULL
,
258 struct NPObject header
;
263 LauncherObj_Allocate(NPP npp
, NPClass
*npclass
)
265 print(("Obj_Allocate\n"));
266 struct LauncherObj
*obj
= malloc(sizeof(struct LauncherObj
));
272 LauncherObj_HasMethod(NPObject
* obj
, NPIdentifier methodName
)
274 print(("Obj_HasMethod %i\n", methodName
== ident_launch
));
275 PrintIdentifier(methodName
);
276 return (methodName
== ident_launch
||
277 methodName
== ident_get_file
);
280 struct ReceiveCallbackArgs
{
288 DoReceiveCallback(void *handle
)
290 struct ReceiveCallbackArgs
*call
= handle
;
293 /* TODO: don't assume this data is UTF-8 */
294 STRINGN_TO_NPVARIANT(call
->buffer
, call
->size
, args
[0]);
295 NPN_InvokeDefault(call
->plugin
, call
->callback
, args
, 1, &result
);
296 NPN_ReleaseVariantValue(&result
);
298 /* We don't do NPN_ReleaseObject() on the callback here because we
299 couldn't do NPN_RetainObject() earlier. */
302 struct ReceiveThreadArgs
{
305 struct NaClDesc
*desc
;
309 ReceiveThread(void *handle
)
311 /* We create one thread for every socket we listen on. It would be
312 more efficient to have one thread using poll(). NPAPI doesn't
313 provide a way for us to share the browser's event loop. */
314 struct ReceiveThreadArgs
*args
= handle
;
315 struct NaClNrdXferEffector effector
;
316 if(!NaClNrdXferEffectorCtor(&effector
, args
->desc
))
319 struct ReceiveCallbackArgs
*call
=
320 malloc(sizeof(struct ReceiveCallbackArgs
));
323 struct NaClImcMsgIoVec iovec
;
324 struct NaClImcTypedMsgHdr header
;
325 iovec
.base
= call
->buffer
;
326 iovec
.length
= sizeof(call
->buffer
);
328 header
.iov_length
= 1;
329 header
.ndescv
= NULL
;
330 header
.ndesc_length
= 0;
333 NaClImcRecvTypedMessage(args
->desc
, (struct NaClDescEffector
*) &effector
,
339 /* We can't call NPN_RetainObject() here because it is not safe to
340 call it from this thread. We assume that
341 NPN_PluginThreadAsyncCall() schedules functions to be called in
342 order, so that the function is not freed until later. */
343 call
->plugin
= args
->plugin
;
344 call
->callback
= args
->callback
;
345 call
->size
= got_bytes
;
346 NPN_PluginThreadAsyncCall(args
->plugin
, DoReceiveCallback
, call
);
348 effector
.base
.vtbl
->Dtor(&effector
.base
);
349 NaClDescUnref(args
->desc
); /* Supposed to be thread safe */
350 NPN_PluginThreadAsyncCall(args
->plugin
, (void (*)(void *)) NPN_ReleaseObject
,
357 ConvertArgvArray(NPP plugin
, NPObject
*array
,
358 char ***result_argv
, int *result_size
)
361 if(!NPN_GetProperty(plugin
, array
, ident_length
, &length_np
) ||
362 !NPVARIANT_IS_INT32(length_np
)) {
363 NPN_ReleaseVariantValue(&length_np
);
366 int length
= NPVARIANT_TO_INT32(length_np
);
367 NPN_ReleaseVariantValue(&length_np
);
369 /* Arbitrary limit */
373 char **argv
= malloc(sizeof(char *) * length
);
377 for(i
= 0; i
< length
; i
++) {
378 NPIdentifier index_id
= NPN_GetIntIdentifier(i
);
380 if(!NPN_GetProperty(plugin
, array
, index_id
, &element
) ||
381 !NPVARIANT_IS_STRING(element
))
382 return false; /* TODO: clear up */
383 NPString string
= NPVARIANT_TO_STRING(element
);
384 char *copy
= malloc(string
.utf8length
+ 1);
386 return false; /* TODO: clear up */
387 /* TODO: allow sending any data, not just UTF-8 */
388 memcpy(copy
, string
.utf8characters
, string
.utf8length
);
389 copy
[string
.utf8length
] = 0;
391 NPN_ReleaseVariantValue(&element
);
394 *result_size
= length
;
398 static struct NaClDesc
*
399 LaunchProcess(NPP plugin
, const char *executable
, char **argv
, int argv_size
,
400 NPObject
*receive_callback
)
403 if(NaClSocketPair(socks
) < 0) {
404 perror("socketpair");
412 return NULL
; /* TODO: report error */
417 snprintf(fd_buffer
, sizeof(fd_buffer
), "%i:%i", 3, socks
[0]);
419 int args_len
= 9 + argv_size
;
420 const char *args
[args_len
];
422 args
[i
++] = "sel_ldr";
424 args
[i
++] = fd_buffer
;
426 args
[i
++] = "NACL_FD=3";
428 args
[i
++] = executable
;
431 for(j
= 0; j
< argv_size
; j
++)
434 assert(i
== args_len
);
436 execvp("sel_ldr", (char **) args
);
441 if(fcntl(socks
[1], F_SETFD
, FD_CLOEXEC
) < 0) {
446 struct NaClDesc
*desc
= malloc(sizeof(struct NaClDescImcDesc
));
447 if(!NaClDescImcDescCtor((struct NaClDescImcDesc
*) desc
, socks
[1]))
450 struct ReceiveThreadArgs
*args
= malloc(sizeof(struct ReceiveThreadArgs
));
454 NPN_RetainObject(receive_callback
);
455 args
->plugin
= plugin
;
457 args
->callback
= receive_callback
;
460 pthread_attr_init(&attr
);
461 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
462 if(pthread_create(&thread_id
, &attr
, ReceiveThread
, args
) != 0)
469 LauncherObj_Invoke(NPObject
*npobj
, NPIdentifier methodName
,
470 const NPVariant
*args
, uint32_t argCount
, NPVariant
*result
)
472 struct LauncherObj
*obj
= (struct LauncherObj
*) npobj
;
473 print(("Obj_Invoke %i\n", argCount
));
474 PrintIdentifier(methodName
);
475 if(methodName
== ident_launch
) {
477 NPVARIANT_IS_OBJECT(args
[0]) &&
478 NPVARIANT_IS_OBJECT(args
[1]) &&
479 NPVARIANT_IS_OBJECT(args
[2])) {
480 NPObject
*file_npobj
= NPVARIANT_TO_OBJECT(args
[0]);
481 NPObject
*argv_obj
= NPVARIANT_TO_OBJECT(args
[1]);
482 NPObject
*receive_callback
= NPVARIANT_TO_OBJECT(args
[2]);
485 if(IsType(file_npobj
, &FileObj_Vtable
) &&
486 ConvertArgvArray(obj
->plugin
, argv_obj
, &argv
, &argv_size
)) {
487 struct FileObj
*file_obj
= (struct FileObj
*) file_npobj
;
488 struct NaClDesc
*desc
= LaunchProcess(obj
->plugin
, file_obj
->filename
,
492 for(i
= 0; i
< argv_size
; i
++)
497 NPObject
*send_npobj
= NPN_CreateObject(obj
->plugin
, &SendObj_Vtable
);
498 struct SendObj
*send_obj
= (struct SendObj
*) send_npobj
;
499 send_obj
->desc
= desc
;
500 OBJECT_TO_NPVARIANT(send_npobj
, *result
);
505 VOID_TO_NPVARIANT(*result
);
508 if(methodName
== ident_get_file
) {
510 NPVARIANT_IS_STRING(args
[0]) &&
511 NPVARIANT_IS_OBJECT(args
[1])) {
512 /* TODO: do same-origin check (assuming they are useful). */
513 NPString url
= NPVARIANT_TO_STRING(args
[0]);
514 NPObject
*callback
= NPVARIANT_TO_OBJECT(args
[1]);
515 NPN_RetainObject(callback
);
516 /* TODO: I don't think utf8characters is guaranteed to be
518 NPError err
= NPN_GetURLNotify(obj
->plugin
, url
.utf8characters
,
520 print(("NPN_GetUrlNotify = %i\n", err
));
522 /* TODO: handle errors */
523 VOID_TO_NPVARIANT(*result
);
529 static NPClass LauncherObj_Vtable
= {
530 .structVersion
= NP_CLASS_STRUCT_VERSION
,
531 .allocate
= LauncherObj_Allocate
,
534 .hasMethod
= LauncherObj_HasMethod
,
535 .invoke
= LauncherObj_Invoke
,
536 .invokeDefault
= NULL
,
537 .hasProperty
= NoOp_HasProperty
,
538 .getProperty
= NoOp_GetProperty
,
540 .removeProperty
= NULL
,
545 NPP_New(NPMIMEType pluginType
, NPP instance
, uint16 mode
,
546 int16 argc
, char **argn
, char **argv
, NPSavedData
*saved
)
548 return NPERR_NO_ERROR
;
552 NPP_Destroy(NPP instance
, NPSavedData
**save
)
554 /* TODO: kill sel_ldr subprocesses, and do waitpid() to avoid a zombie */
555 print(("NPP_Destroy\n"));
556 return NPERR_NO_ERROR
;
560 NPP_GetValue(NPP instance
, NPPVariable variable
, void *result
)
563 case NPPVpluginScriptableNPObject
:
565 NPObject
*obj
= NPN_CreateObject(instance
, &LauncherObj_Vtable
);
566 *(NPObject
**) result
= obj
;
570 return NPERR_GENERIC_ERROR
;
572 return NPERR_NO_ERROR
;
576 NPP_NewStream(NPP instance
, NPMIMEType type
, NPStream
*stream
,
577 NPBool seekable
, uint16
*stype
)
579 print(("NPP_NewStream type=%s seekable=%i data=%p url=%s\n", type
, seekable
,
580 stream
->notifyData
, stream
->url
));
581 *stype
= NP_ASFILEONLY
;
582 return NPERR_NO_ERROR
;
586 NPP_StreamAsFile(NPP instance
, NPStream
*stream
, const char *filename
)
588 print(("NPP_StreamAsFile \"%s\"\n", filename
));
590 /* TODO: this function calls LOG_FATAL on error; not good */
591 struct NaClDesc
*desc
= (struct NaClDesc
*)
592 NaClDescIoDescOpen((char *) filename
, NACL_ABI_O_RDONLY
, 0);
596 NPObject
*callback
= stream
->notifyData
;
597 NPObject
*file_npobj
= NPN_CreateObject(instance
, &FileObj_Vtable
);
598 struct FileObj
*file_obj
= (struct FileObj
*) file_npobj
;
599 file_obj
->desc
= desc
;
600 file_obj
->filename
= strdup(filename
);
601 if(file_obj
->filename
== NULL
)
605 OBJECT_TO_NPVARIANT(file_npobj
, args
[0]);
606 bool success
= NPN_InvokeDefault(instance
, callback
, args
, 1, &result
);
607 print(("NPN_InvokeDefault %i\n", success
));
608 NPN_ReleaseVariantValue(&result
);
609 NPN_ReleaseObject(callback
);
610 NPN_ReleaseObject(file_npobj
);
614 NPP_URLNotify(NPP instance
, const char *url
, NPReason reason
, void *notifyData
)
616 /* TODO: handle file not found errors */
617 print(("NPP_URLNotify \"%s\"\n", url
));
622 NP_Initialize(NPNetscapeFuncs
*funcs
, NPPluginFuncs
*callbacks
)
624 print(("NP_Initialize\n"));
626 callbacks
->newp
= NPP_New
;
627 callbacks
->destroy
= NPP_Destroy
;
628 callbacks
->getvalue
= NPP_GetValue
;
629 callbacks
->newstream
= NPP_NewStream
;
630 callbacks
->asfile
= NPP_StreamAsFile
;
631 callbacks
->urlnotify
= NPP_URLNotify
;
633 ident_launch
= NPN_GetStringIdentifier("launch");
634 ident_get_file
= NPN_GetStringIdentifier("get_file");
635 ident_send
= NPN_GetStringIdentifier("send");
636 ident_length
= NPN_GetStringIdentifier("length");
638 NaClNrdAllModulesInit();
640 return NPERR_NO_ERROR
;
644 NP_GetMIMEDescription(void)
646 return "application/x-nacl-imc::Native Client IMC plugin";