blob: 9a1babd1ab15cd35a53a34aa672fdeff8180eff9 [file] [log] [blame]
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001/* 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.
4 */
5
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +01006#include "nacl_io_demo.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01007
8#include <assert.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <pthread.h>
13
14#include "ppapi/c/pp_errors.h"
15#include "ppapi/c/pp_module.h"
16#include "ppapi/c/ppb.h"
17#include "ppapi/c/ppb_instance.h"
18#include "ppapi/c/ppb_messaging.h"
19#include "ppapi/c/ppb_var.h"
20#include "ppapi/c/ppp.h"
21#include "ppapi/c/ppp_instance.h"
22#include "ppapi/c/ppp_messaging.h"
23#include "nacl_io/nacl_io.h"
24
25#include "handlers.h"
26#include "queue.h"
27
28#define MIN(a, b) (((a) < (b)) ? (a) : (b))
29
30#if defined(WIN32)
31#define va_copy(d, s) ((d) = (s))
32#endif
33
34typedef struct {
35 const char* name;
36 HandleFunc function;
37} FuncNameMapping;
38
39static PP_Instance g_instance = 0;
40static PPB_GetInterface get_browser_interface = NULL;
41static PPB_Messaging* ppb_messaging_interface = NULL;
42static PPB_Var* ppb_var_interface = NULL;
43
44static FuncNameMapping g_function_map[] = {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010045 {"fopen", HandleFopen},
46 {"fwrite", HandleFwrite},
47 {"fread", HandleFread},
48 {"fseek", HandleFseek},
49 {"fclose", HandleFclose},
50 {"stat", HandleStat},
51 {"opendir", HandleOpendir},
52 {"readdir", HandleReaddir},
53 {"closedir", HandleClosedir},
54 {"mkdir", HandleMkdir},
Ben Murdochbb1529c2013-08-08 10:24:53 +010055 {"gethostbyname", HandleGethostbyname},
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010056 {NULL, NULL},
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010057};
58
59/** A handle to the thread the handles messages. */
60static pthread_t g_handle_message_thread;
61
62/**
63 * Create a new PP_Var from a C string.
64 * @param[in] str The string to convert.
65 * @return A new PP_Var with the contents of |str|. */
66struct PP_Var CStrToVar(const char* str) {
67 if (ppb_var_interface != NULL) {
68 return ppb_var_interface->VarFromUtf8(str, strlen(str));
69 }
70 return PP_MakeUndefined();
71}
72
73/**
74 * Printf to a newly allocated C string.
75 * @param[in] format A printf format string.
76 * @param[in] args The printf arguments.
77 * @return The newly constructed string. Caller takes ownership. */
78char* VprintfToNewString(const char* format, va_list args) {
79 va_list args_copy;
80 int length;
81 char* buffer;
82 int result;
83
84 va_copy(args_copy, args);
85 length = vsnprintf(NULL, 0, format, args);
86 buffer = (char*)malloc(length + 1); /* +1 for NULL-terminator. */
87 result = vsnprintf(&buffer[0], length + 1, format, args_copy);
88 assert(result == length);
89 return buffer;
90}
91
92/**
93 * Printf to a newly allocated C string.
94 * @param[in] format A print format string.
95 * @param[in] ... The printf arguments.
96 * @return The newly constructed string. Caller takes ownership. */
97char* PrintfToNewString(const char* format, ...) {
98 va_list args;
99 char* result;
100 va_start(args, format);
101 result = VprintfToNewString(format, args);
102 va_end(args);
103 return result;
104}
105
106/**
107 * Printf to a new PP_Var.
108 * @param[in] format A print format string.
109 * @param[in] ... The printf arguments.
110 * @return A new PP_Var. */
111struct PP_Var PrintfToVar(const char* format, ...) {
112 if (ppb_var_interface != NULL) {
113 char* string;
114 va_list args;
115 struct PP_Var var;
116
117 va_start(args, format);
118 string = VprintfToNewString(format, args);
119 va_end(args);
120
121 var = ppb_var_interface->VarFromUtf8(string, strlen(string));
122 free(string);
123
124 return var;
125 }
126
127 return PP_MakeUndefined();
128}
129
130/**
131 * Convert a PP_Var to a C string, given a buffer.
132 * @param[in] var The PP_Var to convert.
133 * @param[out] buffer The buffer to write to.
134 * @param[in] length The length of |buffer|.
135 * @return The number of characters written. */
136uint32_t VarToCStr(struct PP_Var var, char* buffer, uint32_t length) {
137 if (ppb_var_interface != NULL) {
138 uint32_t var_length;
139 const char* str = ppb_var_interface->VarToUtf8(var, &var_length);
140 /* str is NOT NULL-terminated. Copy using memcpy. */
141 uint32_t min_length = MIN(var_length, length - 1);
142 memcpy(buffer, str, min_length);
143 buffer[min_length] = 0;
144
145 return min_length;
146 }
147
148 return 0;
149}
150
151/**
152 * Given a message from JavaScript, parse it for functions and parameters.
153 *
154 * The format of the message is:
155 * function, param1, param2, param3, etc.
156 * where each element is separated by the \1 character.
157 *
158 * e.g.
159 * "function\1first parameter\1second parameter"
160 *
161 * How to use:
162 * char* function;
163 * char* params[4];
164 * int num_params = ParseMessage(msg, &function, &params, 4);
165 *
166 * @param[in, out] message The message to parse. This string is modified
167 * in-place.
168 * @param[out] out_function The function name.
169 * @param[out] out_params An array of strings, one for each parameter parsed.
170 * @param[in] max_params The maximum number of parameters to parse.
171 * @return The number of parameters parsed. */
172static size_t ParseMessage(char* message,
173 char** out_function,
174 char** out_params,
175 size_t max_params) {
176 char* separator;
177 char* param_start;
178 size_t num_params = 0;
179
180 /* Parse the message: function\1param1\1param2\1param3,... */
181 *out_function = &message[0];
182
183 separator = strchr(message, 1);
184 if (!separator) {
185 return num_params;
186 }
187
188 *separator = 0; /* NULL-terminate function. */
189
190 while (separator && num_params < max_params) {
191 param_start = separator + 1;
192 separator = strchr(param_start, 1);
193 if (separator) {
194 *separator = 0;
195 out_params[num_params++] = param_start;
196 }
197 }
198
199 out_params[num_params++] = param_start;
200
201 return num_params;
202}
203
204/**
205 * Given a function name, look up its handler function.
206 * @param[in] function_name The function name to look up.
207 * @return The handler function mapped to |function_name|. */
208static HandleFunc GetFunctionByName(const char* function_name) {
209 FuncNameMapping* map_iter = g_function_map;
210 for (; map_iter->name; ++map_iter) {
211 if (strcmp(map_iter->name, function_name) == 0) {
212 return map_iter->function;
213 }
214 }
215
216 return NULL;
217}
218
219/** Handle as message from JavaScript on the worker thread.
220 *
221 * @param[in] message The message to parse and handle. */
222static void HandleMessage(char* message) {
223 char* function_name;
224 char* params[MAX_PARAMS];
225 size_t num_params;
226 char* output = NULL;
227 int result;
228 HandleFunc function;
229
230 num_params = ParseMessage(message, &function_name, &params[0], MAX_PARAMS);
231
232 function = GetFunctionByName(function_name);
233 if (!function) {
234 /* Function name wasn't found. Error. */
235 ppb_messaging_interface->PostMessage(
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100236 g_instance,
237 PrintfToVar("Error: Unknown function \"%s\"", function_name));
238 return;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100239 }
240
241 /* Function name was found, call it. */
242 result = (*function)(num_params, &params[0], &output);
243 if (result != 0) {
244 /* Error. */
245 struct PP_Var var;
246 if (output != NULL) {
247 var = PrintfToVar("Error: Function \"%s\" returned error %d. "
248 "Additional output: %s",
249 function_name,
250 result,
251 output);
252 free(output);
253 } else {
254 var = PrintfToVar(
255 "Error: Function \"%s\" returned error %d.", function_name, result);
256 }
257
258 /* Post the error to JavaScript, so the user can see it. */
259 ppb_messaging_interface->PostMessage(g_instance, var);
260 return;
261 }
262
263 if (output != NULL) {
264 /* Function returned an output string. Send it to JavaScript. */
265 ppb_messaging_interface->PostMessage(g_instance, CStrToVar(output));
266 free(output);
267 }
268}
269
270/** A worker thread that handles messages from JavaScript.
271 * @param[in] user_data Unused.
272 * @return unused. */
273void* HandleMessageThread(void* user_data) {
274 while (1) {
275 char* message = DequeueMessage();
276 HandleMessage(message);
277 free(message);
278 }
279}
280
281static PP_Bool Instance_DidCreate(PP_Instance instance,
282 uint32_t argc,
283 const char* argn[],
284 const char* argv[]) {
285 g_instance = instance;
286 nacl_io_init_ppapi(instance, get_browser_interface);
287
288 // By default, nacl_io mounts / to pass through to the original NaCl
289 // filesystem (which doesn't do much). Let's remount it to a memfs
290 // filesystem.
291 umount("/");
292 mount("", "/", "memfs", 0, "");
293
294 mount("", /* source */
295 "/persistent", /* target */
296 "html5fs", /* filesystemtype */
297 0, /* mountflags */
298 "type=PERSISTENT,expected_size=1048576"); /* data */
299
300 mount("", /* source. Use relative URL */
301 "/http", /* target */
302 "httpfs", /* filesystemtype */
303 0, /* mountflags */
304 ""); /* data */
305
306 pthread_create(&g_handle_message_thread, NULL, &HandleMessageThread, NULL);
307 InitializeMessageQueue();
308
309 return PP_TRUE;
310}
311
312static void Instance_DidDestroy(PP_Instance instance) {}
313
314static void Instance_DidChangeView(PP_Instance instance,
315 PP_Resource view_resource) {}
316
317static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) {}
318
319static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
320 PP_Resource url_loader) {
321 /* NaCl modules do not need to handle the document load function. */
322 return PP_FALSE;
323}
324
325static void Messaging_HandleMessage(PP_Instance instance,
326 struct PP_Var message) {
327 char buffer[1024];
328 VarToCStr(message, &buffer[0], 1024);
329 if (!EnqueueMessage(strdup(buffer))) {
330 struct PP_Var var;
331 var = PrintfToVar(
332 "Warning: dropped message \"%s\" because the queue was full.", message);
333 ppb_messaging_interface->PostMessage(g_instance, var);
334 }
335}
336
337PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id,
338 PPB_GetInterface get_browser) {
339 get_browser_interface = get_browser;
340 ppb_messaging_interface =
341 (PPB_Messaging*)(get_browser(PPB_MESSAGING_INTERFACE));
342 ppb_var_interface = (PPB_Var*)(get_browser(PPB_VAR_INTERFACE));
343 return PP_OK;
344}
345
346PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
347 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
348 static PPP_Instance instance_interface = {
349 &Instance_DidCreate,
350 &Instance_DidDestroy,
351 &Instance_DidChangeView,
352 &Instance_DidChangeFocus,
353 &Instance_HandleDocumentLoad,
354 };
355 return &instance_interface;
356 } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
357 static PPP_Messaging messaging_interface = {
358 &Messaging_HandleMessage,
359 };
360 return &messaging_interface;
361 }
362 return NULL;
363}
364
365PP_EXPORT void PPP_ShutdownModule() {}