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