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.
6 #include "nacl_io_demo.h"
12 #include <sys/mount.h>
15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/c/pp_module.h"
17 #include "ppapi/c/ppb.h"
18 #include "ppapi/c/ppb_instance.h"
19 #include "ppapi/c/ppb_messaging.h"
20 #include "ppapi/c/ppb_var.h"
21 #include "ppapi/c/ppp.h"
22 #include "ppapi/c/ppp_instance.h"
23 #include "ppapi/c/ppp_messaging.h"
24 #include "nacl_io/nacl_io.h"
29 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
32 #define va_copy(d, s) ((d) = (s))
40 static PP_Instance g_instance = 0;
41 static PPB_GetInterface get_browser_interface = NULL;
42 static PPB_Messaging* ppb_messaging_interface = NULL;
43 static PPB_Var* ppb_var_interface = NULL;
45 static FuncNameMapping g_function_map[] = {
46 { "fopen", HandleFopen },
47 { "fwrite", HandleFwrite },
48 { "fread", HandleFread },
49 { "fseek", HandleFseek },
50 { "fclose", HandleFclose },
51 { "fflush", HandleFflush },
52 { "stat", HandleStat },
53 { "opendir", HandleOpendir },
54 { "readdir", HandleReaddir },
55 { "closedir", HandleClosedir },
56 { "mkdir", HandleMkdir },
57 { "rmdir", HandleRmdir },
58 { "chdir", HandleChdir },
59 { "getcwd", HandleGetcwd },
60 { "getaddrinfo", HandleGetaddrinfo },
61 { "gethostbyname", HandleGethostbyname },
62 { "connect", HandleConnect },
63 { "send", HandleSend },
64 { "recv", HandleRecv },
65 { "close", HandleClose },
69 /** A handle to the thread the handles messages. */
70 static pthread_t g_handle_message_thread;
73 * Create a new PP_Var from a C string.
74 * @param[in] str The string to convert.
75 * @return A new PP_Var with the contents of |str|.
77 struct PP_Var CStrToVar(const char* str) {
78 if (ppb_var_interface != NULL) {
79 return ppb_var_interface->VarFromUtf8(str, strlen(str));
81 return PP_MakeUndefined();
85 * Printf to a newly allocated C string.
86 * @param[in] format A printf format string.
87 * @param[in] args The printf arguments.
88 * @return The newly constructed string. Caller takes ownership. */
89 char* VprintfToNewString(const char* format, va_list args) {
95 va_copy(args_copy, args);
96 length = vsnprintf(NULL, 0, format, args);
97 buffer = (char*)malloc(length + 1); /* +1 for NULL-terminator. */
98 result = vsnprintf(&buffer[0], length + 1, format, args_copy);
99 if (result != length) {
107 * Printf to a newly allocated C string.
108 * @param[in] format A print format string.
109 * @param[in] ... The printf arguments.
110 * @return The newly constructed string. Caller takes ownership.
112 char* PrintfToNewString(const char* format, ...) {
115 va_start(args, format);
116 result = VprintfToNewString(format, args);
122 * Printf to a new PP_Var.
123 * @param[in] format A print format string.
124 * @param[in] ... The printf arguments.
125 * @return A new PP_Var.
127 struct PP_Var PrintfToVar(const char* format, ...) {
128 if (ppb_var_interface != NULL) {
133 va_start(args, format);
134 string = VprintfToNewString(format, args);
137 var = ppb_var_interface->VarFromUtf8(string, strlen(string));
143 return PP_MakeUndefined();
147 * Convert a PP_Var to a C string, given a buffer.
148 * @param[in] var The PP_Var to convert.
149 * @param[out] buffer The buffer to write to.
150 * @param[in] length The length of |buffer|.
151 * @return The number of characters written.
153 uint32_t VarToCStr(struct PP_Var var, char* buffer, uint32_t length) {
154 if (ppb_var_interface != NULL) {
156 const char* str = ppb_var_interface->VarToUtf8(var, &var_length);
157 /* str is NOT NULL-terminated. Copy using memcpy. */
158 uint32_t min_length = MIN(var_length, length - 1);
159 memcpy(buffer, str, min_length);
160 buffer[min_length] = 0;
169 * Given a message from JavaScript, parse it for functions and parameters.
171 * The format of the message is:
172 * function, param1, param2, param3, etc.
173 * where each element is separated by the \1 character.
176 * "function\1first parameter\1second parameter"
181 * int num_params = ParseMessage(msg, &function, ¶ms, 4);
183 * @param[in, out] message The message to parse. This string is modified
185 * @param[out] out_function The function name.
186 * @param[out] out_params An array of strings, one for each parameter parsed.
187 * @param[in] max_params The maximum number of parameters to parse.
188 * @return The number of parameters parsed.
190 static size_t ParseMessage(char* message,
196 size_t num_params = 0;
198 /* Parse the message: function\1param1\1param2\1param3,... */
199 *out_function = &message[0];
201 separator = strchr(message, 1);
206 *separator = 0; /* NULL-terminate function. */
208 while (separator && num_params < max_params) {
209 param_start = separator + 1;
210 separator = strchr(param_start, 1);
213 out_params[num_params++] = param_start;
217 out_params[num_params++] = param_start;
223 * Given a function name, look up its handler function.
224 * @param[in] function_name The function name to look up.
225 * @return The handler function mapped to |function_name|.
227 static HandleFunc GetFunctionByName(const char* function_name) {
228 FuncNameMapping* map_iter = g_function_map;
229 for (; map_iter->name; ++map_iter) {
230 if (strcmp(map_iter->name, function_name) == 0) {
231 return map_iter->function;
239 * Handle as message from JavaScript on the worker thread.
241 * @param[in] message The message to parse and handle.
243 static void HandleMessage(char* message) {
245 char* params[MAX_PARAMS];
251 num_params = ParseMessage(message, &function_name, ¶ms[0], MAX_PARAMS);
253 function = GetFunctionByName(function_name);
255 /* Function name wasn't found. Error. */
256 ppb_messaging_interface->PostMessage(
258 PrintfToVar("Error: Unknown function \"%s\"", function_name));
262 /* Function name was found, call it. */
263 result = (*function)(num_params, ¶ms[0], &output);
267 if (output != NULL) {
268 var = PrintfToVar("Error: \"%s\" failed: %d: %s.", function_name,
273 "Error: \"%s\" failed: %d.", function_name, result);
276 /* Post the error to JavaScript, so the user can see it. */
277 ppb_messaging_interface->PostMessage(g_instance, var);
281 if (output != NULL) {
282 /* Function returned an output string. Send it to JavaScript. */
283 ppb_messaging_interface->PostMessage(g_instance, CStrToVar(output));
289 * A worker thread that handles messages from JavaScript.
290 * @param[in] user_data Unused.
293 void* HandleMessageThread(void* user_data) {
295 char* message = DequeueMessage();
296 HandleMessage(message);
301 static PP_Bool Instance_DidCreate(PP_Instance instance,
304 const char* argv[]) {
305 g_instance = instance;
306 nacl_io_init_ppapi(instance, get_browser_interface);
308 // By default, nacl_io mounts / to pass through to the original NaCl
309 // filesystem (which doesn't do much). Let's remount it to a memfs
312 mount("", "/", "memfs", 0, "");
314 mount("", /* source */
315 "/persistent", /* target */
316 "html5fs", /* filesystemtype */
318 "type=PERSISTENT,expected_size=1048576"); /* data */
320 mount("", /* source. Use relative URL */
321 "/http", /* target */
322 "httpfs", /* filesystemtype */
326 pthread_create(&g_handle_message_thread, NULL, &HandleMessageThread, NULL);
327 InitializeMessageQueue();
332 static void Instance_DidDestroy(PP_Instance instance) {}
334 static void Instance_DidChangeView(PP_Instance instance,
335 PP_Resource view_resource) {}
337 static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) {}
339 static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
340 PP_Resource url_loader) {
341 /* NaCl modules do not need to handle the document load function. */
345 static void Messaging_HandleMessage(PP_Instance instance,
346 struct PP_Var message) {
348 VarToCStr(message, &buffer[0], 1024);
349 if (!EnqueueMessage(strdup(buffer))) {
352 "Warning: dropped message \"%s\" because the queue was full.", message);
353 ppb_messaging_interface->PostMessage(g_instance, var);
357 PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id,
358 PPB_GetInterface get_browser) {
359 get_browser_interface = get_browser;
360 ppb_messaging_interface =
361 (PPB_Messaging*)(get_browser(PPB_MESSAGING_INTERFACE));
362 ppb_var_interface = (PPB_Var*)(get_browser(PPB_VAR_INTERFACE));
366 PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
367 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
368 static PPP_Instance instance_interface = {
370 &Instance_DidDestroy,
371 &Instance_DidChangeView,
372 &Instance_DidChangeFocus,
373 &Instance_HandleDocumentLoad,
375 return &instance_interface;
376 } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
377 static PPP_Messaging messaging_interface = {
378 &Messaging_HandleMessage,
380 return &messaging_interface;
385 PP_EXPORT void PPP_ShutdownModule() {}