1/****************************************************************************** 2 Copyright 2012 Google Inc. All Rights Reserved. 3 Author: yugui@google.com (Yugui Sonoda) 4 ******************************************************************************/ 5 6#include <stdlib.h> 7#include <stdio.h> 8#include <string.h> 9#include <pthread.h> 10#include <sys/stat.h> 11#include <fcntl.h> 12#include <pthread.h> 13#include "ppapi/c/pp_errors.h" 14#include "ppapi/c/pp_module.h" 15#include "ppapi/c/pp_var.h" 16#include "ppapi/c/ppb.h" 17#include "ppapi/c/ppb_core.h" 18#include "ppapi/c/ppb_file_ref.h" 19#include "ppapi/c/ppb_instance.h" 20#include "ppapi/c/ppb_messaging.h" 21#include "ppapi/c/ppb_url_loader.h" 22#include "ppapi/c/ppb_url_request_info.h" 23#include "ppapi/c/ppb_url_response_info.h" 24#include "ppapi/c/ppb_var.h" 25#include "ppapi/c/ppp.h" 26#include "ppapi/c/ppp_instance.h" 27#include "ppapi/c/ppp_messaging.h" 28 29#include "verconf.h" 30#include "ruby/ruby.h" 31#include "version.h" 32#include "gc.h" 33 34#ifdef HAVE_STRUCT_PPB_CORE 35typedef struct PPB_Core PPB_Core; 36#endif 37#ifdef HAVE_STRUCT_PPB_MESSAGING 38typedef struct PPB_Messaging PPB_Messaging; 39#endif 40#ifdef HAVE_STRUCT_PPB_VAR 41typedef struct PPB_Var PPB_Var; 42#endif 43#ifdef HAVE_STRUCT_PPB_URLLOADER 44typedef struct PPB_URLLoader PPB_URLLoader; 45#endif 46#ifdef HAVE_STRUCT_PPB_URLREQUESTINFO 47typedef struct PPB_URLRequestInfo PPB_URLRequestInfo; 48#endif 49#ifdef HAVE_STRUCT_PPB_URLRESPONSEINFO 50typedef struct PPB_URLResponseInfo PPB_URLResponseInfo; 51#endif 52#ifdef HAVE_STRUCT_PPP_INSTANCE 53typedef struct PPP_Instance PPP_Instance; 54#endif 55 56static PP_Module module_id = 0; 57static PPB_Core* core_interface = NULL; 58static PPB_Messaging* messaging_interface = NULL; 59static PPB_Var* var_interface = NULL; 60static PPB_URLLoader* loader_interface = NULL; 61static PPB_URLRequestInfo* request_interface = NULL; 62static PPB_URLResponseInfo* response_interface = NULL; 63static PPB_FileRef* fileref_interface = NULL; 64static struct st_table* instance_data = NULL; 65 66static VALUE instance_table = Qundef; 67 68static PP_Instance current_instance = 0; 69 70/****************************************************************************** 71 * State of instance 72 ******************************************************************************/ 73 74static void inst_mark(void *const ptr); 75static void inst_free(void *const ptr); 76static size_t inst_memsize(void *const ptr); 77static const rb_data_type_t pepper_instance_data_type = { 78 "PepperInstance", 79 { inst_mark, inst_free, inst_memsize } 80}; 81 82struct PepperInstance { 83 PP_Instance instance; 84 PP_Resource url_loader; 85 VALUE self; 86 void* async_call_args; 87 union { 88 int32_t as_int; 89 const char* as_str; 90 VALUE as_value; 91 } async_call_result; 92 char buf[1000]; 93 94 pthread_t th; 95 pthread_mutex_t mutex; 96 pthread_cond_t cond; 97}; 98 99struct PepperInstance* 100pruby_get_instance(PP_Instance instance) 101{ 102 VALUE self = rb_hash_aref(instance_table, INT2FIX(instance)); 103 if (RTEST(self)) { 104 struct PepperInstance *inst; 105 TypedData_Get_Struct(self, struct PepperInstance, &pepper_instance_data_type, inst); 106 return inst; 107 } 108 else { 109 return NULL; 110 } 111} 112 113#define GET_PEPPER_INSTANCE() (pruby_get_instance(current_instance)) 114 115struct PepperInstance* 116pruby_register_instance(PP_Instance instance) 117{ 118 VALUE obj; 119 struct PepperInstance *data; 120 obj = TypedData_Make_Struct(rb_cData, struct PepperInstance, &pepper_instance_data_type, data); 121 data->self = obj; 122 data->instance = instance; 123 data->url_loader = 0; 124 125 pthread_mutex_init(&data->mutex, NULL); 126 pthread_cond_init(&data->cond, NULL); 127 128 rb_hash_aset(instance_table, INT2FIX(instance), obj); 129 return data; 130} 131 132int 133pruby_unregister_instance(PP_Instance instance) 134{ 135 VALUE inst = rb_hash_delete(instance_table, INT2FIX(instance)); 136 return RTEST(inst); 137} 138 139static void 140inst_mark(void *const ptr) 141{ 142 RUBY_MARK_ENTER("PepperInstance"0); 143 if (ptr) { 144 const struct PepperInstance* inst = (struct PepperInstance*)ptr; 145 RUBY_MARK_UNLESS_NULL(inst->async_call_result.as_value); 146 } 147 RUBY_MARK_LEAVE("PepperInstance"0); 148} 149 150static void 151inst_free(void *const ptr) 152{ 153 ruby_xfree(ptr); 154} 155 156static size_t 157inst_memsize(void *const ptr) 158{ 159 if (ptr) { 160 const struct PepperInstance* inst = (struct PepperInstance*)ptr; 161 return sizeof(*inst); 162 } else { 163 return 0; 164 } 165} 166 167void 168pruby_async_return_int(void* data, int32_t result) 169{ 170 /* PPAPI main thread */ 171 struct PepperInstance* const instance = (struct PepperInstance*)data; 172 instance->async_call_result.as_int = result; 173 if (pthread_cond_signal(&instance->cond)) { 174 perror("pepper-ruby:pthread_cond_signal"); 175 } 176} 177 178void 179pruby_async_return_str(void* data, const char *result) 180{ 181 /* PPAPI main thread */ 182 struct PepperInstance* const instance = (struct PepperInstance*)data; 183 instance->async_call_result.as_str = result; 184 if (pthread_cond_signal(&instance->cond)) { 185 perror("pepper-ruby:pthread_cond_signal"); 186 } 187} 188 189void 190pruby_async_return_value(void* data, VALUE value) 191{ 192 /* PPAPI main thread */ 193 struct PepperInstance* const instance = (struct PepperInstance*)data; 194 instance->async_call_result.as_value = value; 195 if (pthread_cond_signal(&instance->cond)) { 196 perror("pepper-ruby:pthread_cond_signal"); 197 } 198} 199/****************************************************************************** 200 * Conversion between Ruby's VALUE, Pepper's Var and C string 201 ******************************************************************************/ 202 203/** 204 * Creates a new string PP_Var from C string. The resulting object will be a 205 * refcounted string object. It will be AddRef()ed for the caller. When the 206 * caller is done with it, it should be Release()d. 207 * @param[in] str C string to be converted to PP_Var 208 * @return PP_Var containing string. 209 */ 210static struct PP_Var 211pruby_cstr_to_var(const char* str) 212{ 213#ifdef PPB_VAR_INTERFACE_1_0 214 if (var_interface != NULL) 215 return var_interface->VarFromUtf8(module_id, str, strlen(str)); 216 return PP_MakeUndefined(); 217#else 218 return var_interface->VarFromUtf8(str, strlen(str)); 219#endif 220} 221 222/** 223 * Returns a mutable C string contained in the @a var or NULL if @a var is not 224 * string. This makes a copy of the string in the @a var and adds a NULL 225 * terminator. Note that VarToUtf8() does not guarantee the NULL terminator on 226 * the returned string. See the comments for VarToUtf8() in ppapi/c/ppb_var.h 227 * for more info. The caller is responsible for freeing the returned memory. 228 * @param[in] var PP_Var containing string. 229 * @return a mutable C string representation of @a var. 230 * @note The caller is responsible for freeing the returned string. 231 */ 232static char* 233pruby_var_to_cstr(struct PP_Var var) 234{ 235 uint32_t len = 0; 236 if (var_interface != NULL) { 237 const char* var_c_str = var_interface->VarToUtf8(var, &len); 238 if (len > 0) { 239 char* c_str = (char*)malloc(len + 1); 240 memcpy(c_str, var_c_str, len); 241 c_str[len] = '\0'; 242 return c_str; 243 } 244 } 245 return NULL; 246} 247 248static struct PP_Var 249pruby_str_to_var(volatile VALUE str) 250{ 251 if (!RB_TYPE_P(str, T_STRING)) { 252 fprintf(stderr, "[BUG] Unexpected object type: %x\n", TYPE(str)); 253 exit(EXIT_FAILURE); 254 } 255#ifdef PPB_VAR_INTERFACE_1_0 256 if (var_interface != NULL) { 257 return var_interface->VarFromUtf8(module_id, RSTRING_PTR(str), RSTRING_LEN(str)); 258 } 259#else 260 return var_interface->VarFromUtf8(RSTRING_PTR(str), RSTRING_LEN(str)); 261#endif 262 return PP_MakeUndefined(); 263} 264 265static struct PP_Var 266pruby_obj_to_var(volatile VALUE obj) 267{ 268 static const char* const error = 269 "throw 'Failed to convert the result to a JavaScript object';"; 270 int state; 271 obj = rb_protect(&rb_obj_as_string, obj, &state); 272 if (!state) { 273 return pruby_str_to_var(obj); 274 } 275 else { 276 return pruby_cstr_to_var(error); 277 } 278} 279 280int 281pruby_var_equal_to_cstr_p(struct PP_Var lhs, const char* rhs) 282{ 283 uint32_t len = 0; 284 if (var_interface == NULL) { 285 return 0; 286 } 287 else { 288 const char* const cstr = var_interface->VarToUtf8(lhs, &len); 289 return strncmp(cstr, rhs, len) == 0; 290 } 291} 292 293int 294pruby_var_prefixed_p(struct PP_Var var, const char* prefix) 295{ 296 uint32_t len = 0; 297 if (var_interface == NULL) { 298 return 0; 299 } 300 else { 301 const char* const cstr = var_interface->VarToUtf8(var, &len); 302 const size_t prefix_len = strlen(prefix); 303 return len >= prefix_len && memcmp(cstr, prefix, len) == 0; 304 } 305} 306 307 308/****************************************************************************** 309 * Messaging 310 ******************************************************************************/ 311 312/* Posts the given C string as a message. 313 * @param data pointer to a NULL-terminated string */ 314void 315pruby_post_cstr(void* data) 316{ 317 /* PPAPI main thread */ 318 struct PepperInstance* const instance = (struct PepperInstance*)data; 319 const char* const msg = (const char*)instance->async_call_args; 320 messaging_interface->PostMessage(instance->instance, 321 pruby_cstr_to_var(msg)); 322} 323 324/* Posts the given Ruby VALUE as a message. 325 * @param data a VALUE casted to void* */ 326void 327pruby_post_value(void* data) 328{ 329 /* PPAPI main thread */ 330 struct PepperInstance* const instance = (struct PepperInstance*)data; 331 volatile VALUE value = (VALUE)instance->async_call_args; 332 messaging_interface->PostMessage(instance->instance, pruby_obj_to_var(value)); 333} 334 335 336 337/****************************************************************************** 338 * Ruby initialization 339 ******************************************************************************/ 340 341static void 342init_loadpath(void) 343{ 344 ruby_incpush("lib/ruby/"RUBY_LIB_VERSION); 345 ruby_incpush("lib/ruby/"RUBY_LIB_VERSION"/"RUBY_PLATFORM); 346 ruby_incpush("."); 347} 348 349static VALUE 350init_libraries_internal(VALUE unused) 351{ 352 extern void Init_enc(); 353 extern void Init_ext(); 354 355 init_loadpath(); 356 Init_enc(); 357 Init_ext(); 358 return Qnil; 359} 360 361static void* 362init_libraries(void* data) 363{ 364 int state; 365 struct PepperInstance* const instance = (struct PepperInstance*)data; 366 current_instance = instance->instance; 367 368 if (pthread_mutex_lock(&instance->mutex)) { 369 perror("pepper-ruby:pthread_mutex_lock"); 370 return 0; 371 } 372 rb_protect(&init_libraries_internal, Qnil, &state); 373 pthread_mutex_unlock(&instance->mutex); 374 375 if (state) { 376 volatile VALUE err = rb_errinfo(); 377 err = rb_obj_as_string(err); 378 } else { 379 instance->async_call_args = (void*)"rubyReady"; 380 core_interface->CallOnMainThread( 381 0, PP_MakeCompletionCallback(pruby_post_cstr, instance), 0); 382 } 383 return NULL; 384} 385 386static int 387init_libraries_if_necessary(void) 388{ 389 static int initialized = 0; 390 if (!initialized) { 391 struct PepperInstance* const instance = GET_PEPPER_INSTANCE(); 392 int err; 393 initialized = 1; 394 err = pthread_create(&instance->th, NULL, &init_libraries, instance); 395 if (err) { 396 fprintf(stderr, "pepper_ruby:pthread_create: %s\n", strerror(err)); 397 exit(EXIT_FAILURE); 398 } 399 pthread_detach(instance->th); 400 } 401 return 0; 402} 403 404static int 405pruby_init(void) 406{ 407 RUBY_INIT_STACK; 408 ruby_init(); 409 410 instance_table = rb_hash_new(); 411 rb_gc_register_mark_object(instance_table); 412 413 return 0; 414} 415 416 417/****************************************************************************** 418 * Ruby evaluation 419 ******************************************************************************/ 420 421static void* 422pruby_eval(void* data) 423{ 424 extern VALUE ruby_eval_string_from_file_protect(const char* src, const char* path, int* state); 425 struct PepperInstance* const instance = (struct PepperInstance*)data; 426 volatile VALUE src = (VALUE)instance->async_call_args; 427 volatile VALUE result = Qnil; 428 volatile int state; 429 430 RUBY_INIT_STACK; 431 432 if (pthread_mutex_lock(&instance->mutex)) { 433 perror("pepper-ruby:pthread_mutex_lock"); 434 return 0; 435 } 436 result = ruby_eval_string_from_file_protect( 437 RSTRING_PTR(src), "(pepper-ruby)", &state); 438 pthread_mutex_unlock(&instance->mutex); 439 440 if (!state) { 441 instance->async_call_args = 442 rb_str_concat(rb_usascii_str_new_cstr("return:"), 443 rb_obj_as_string(result)); 444 core_interface->CallOnMainThread( 445 0, PP_MakeCompletionCallback(pruby_post_value, instance), 0); 446 return NULL; 447 } 448 else { 449 rb_set_errinfo(Qnil); 450 instance->async_call_args = 451 rb_str_concat(rb_usascii_str_new_cstr("error:"), 452 rb_obj_as_string(result)); 453 core_interface->CallOnMainThread( 454 0, PP_MakeCompletionCallback(pruby_post_value, instance), 0); 455 return NULL; 456 } 457} 458 459 460/****************************************************************************** 461 * Pepper Module callbacks 462 ******************************************************************************/ 463 464/** 465 * Called when the NaCl module is instantiated on the web page. The identifier 466 * of the new instance will be passed in as the first argument (this value is 467 * generated by the browser and is an opaque handle). This is called for each 468 * instantiation of the NaCl module, which is each time the <embed> tag for 469 * this module is encountered. 470 * 471 * If this function reports a failure (by returning @a PP_FALSE), the NaCl 472 * module will be deleted and DidDestroy will be called. 473 * @param[in] instance The identifier of the new instance representing this 474 * NaCl module. 475 * @param[in] argc The number of arguments contained in @a argn and @a argv. 476 * @param[in] argn An array of argument names. These argument names are 477 * supplied in the <embed> tag, for example: 478 * <embed id="nacl_module" dimensions="2"> 479 * will produce two arguments, one named "id" and one named "dimensions". 480 * @param[in] argv An array of argument values. These are the values of the 481 * arguments listed in the <embed> tag. In the above example, there will 482 * be two elements in this array, "nacl_module" and "2". The indices of 483 * these values match the indices of the corresponding names in @a argn. 484 * @return @a PP_TRUE on success. 485 */ 486static PP_Bool 487Instance_DidCreate(PP_Instance instance, 488 uint32_t argc, const char* argn[], const char* argv[]) 489{ 490 struct PepperInstance* data = pruby_register_instance(instance); 491 current_instance = instance; 492 return init_libraries_if_necessary() ? PP_FALSE : PP_TRUE; 493} 494 495/** 496 * Called when the NaCl module is destroyed. This will always be called, 497 * even if DidCreate returned failure. This routine should deallocate any data 498 * associated with the instance. 499 * @param[in] instance The identifier of the instance representing this NaCl 500 * module. 501 */ 502static void Instance_DidDestroy(PP_Instance instance) { 503 struct PepperInstance* data = pruby_get_instance(instance); 504 core_interface->ReleaseResource(data->url_loader); 505 pruby_unregister_instance(instance); 506} 507 508/** 509 * Called when the position, the size, or the clip rect of the element in the 510 * browser that corresponds to this NaCl module has changed. 511 * @param[in] instance The identifier of the instance representing this NaCl 512 * module. 513 * @param[in] position The location on the page of this NaCl module. This is 514 * relative to the top left corner of the viewport, which changes as the 515 * page is scrolled. 516 * @param[in] clip The visible region of the NaCl module. This is relative to 517 * the top left of the plugin's coordinate system (not the page). If the 518 * plugin is invisible, @a clip will be (0, 0, 0, 0). 519 */ 520#ifdef PPP_INSTANCE_INTERFACE_1_0 521static void 522Instance_DidChangeView(PP_Instance instance, 523 const struct PP_Rect* position, 524 const struct PP_Rect* clip) 525{ 526} 527#else 528static void 529Instance_DidChangeView(PP_Instance instance, PP_Resource view_resource) 530{ 531} 532#endif 533 534/** 535 * Notification that the given NaCl module has gained or lost focus. 536 * Having focus means that keyboard events will be sent to the NaCl module 537 * represented by @a instance. A NaCl module's default condition is that it 538 * will not have focus. 539 * 540 * Note: clicks on NaCl modules will give focus only if you handle the 541 * click event. You signal if you handled it by returning @a true from 542 * HandleInputEvent. Otherwise the browser will bubble the event and give 543 * focus to the element on the page that actually did end up consuming it. 544 * If you're not getting focus, check to make sure you're returning true from 545 * the mouse click in HandleInputEvent. 546 * @param[in] instance The identifier of the instance representing this NaCl 547 * module. 548 * @param[in] has_focus Indicates whether this NaCl module gained or lost 549 * event focus. 550 */ 551static void 552Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) 553{ 554} 555 556/** 557 * Handler that gets called after a full-frame module is instantiated based on 558 * registered MIME types. This function is not called on NaCl modules. This 559 * function is essentially a place-holder for the required function pointer in 560 * the PPP_Instance structure. 561 * @param[in] instance The identifier of the instance representing this NaCl 562 * module. 563 * @param[in] url_loader A PP_Resource an open PPB_URLLoader instance. 564 * @return PP_FALSE. 565 */ 566static PP_Bool 567Instance_HandleDocumentLoad(PP_Instance instance, PP_Resource url_loader) 568{ 569 /* NaCl modules do not need to handle the document load function. */ 570 return PP_FALSE; 571} 572 573 574/** 575 * Handler for messages coming in from the browser via postMessage. The 576 * @a var_message can contain anything: a JSON string; a string that encodes 577 * method names and arguments; etc. For example, you could use JSON.stringify 578 * in the browser to create a message that contains a method name and some 579 * parameters, something like this: 580 * var json_message = JSON.stringify({ "myMethod" : "3.14159" }); 581 * nacl_module.postMessage(json_message); 582 * On receipt of this message in @a var_message, you could parse the JSON to 583 * retrieve the method name, match it to a function call, and then call it with 584 * the parameter. 585 * @param[in] instance The instance ID. 586 * @param[in] message The contents, copied by value, of the message sent from 587 * browser via postMessage. 588 */ 589void 590Messaging_HandleMessage(PP_Instance instance, struct PP_Var var_message) 591{ 592 char* const message = pruby_var_to_cstr(var_message); 593 size_t message_len = strlen(message); 594 current_instance = instance; 595 596 if (strstr(message, "eval:") != NULL) { 597 volatile VALUE src; 598 struct PepperInstance* const instance_data = GET_PEPPER_INSTANCE(); 599 int err; 600#define EVAL_PREFIX_LEN 5 601 src = rb_str_new(message + EVAL_PREFIX_LEN, message_len - EVAL_PREFIX_LEN); 602 instance_data->async_call_args = (void*)src; 603 err = pthread_create(&instance_data->th, NULL, &pruby_eval, instance_data); 604 if (err) { 605 fprintf(stderr, "pepper_ruby:pthread_create: %s\n", strerror(err)); 606 exit(EXIT_FAILURE); 607 } 608 pthread_detach(instance_data->th); 609 } 610 free(message); 611} 612 613/** 614 * Entry points for the module. 615 * Initialize instance interface and scriptable object class. 616 * @param[in] a_module_id Module ID 617 * @param[in] get_browser_interface Pointer to PPB_GetInterface 618 * @return PP_OK on success, any other value on failure. 619 */ 620PP_EXPORT int32_t 621PPP_InitializeModule(PP_Module a_module_id, PPB_GetInterface get_browser_interface) 622{ 623 module_id = a_module_id; 624 core_interface = (PPB_Core*)(get_browser_interface(PPB_CORE_INTERFACE)); 625 if (core_interface == NULL) return PP_ERROR_NOINTERFACE; 626 627 var_interface = (PPB_Var*)(get_browser_interface(PPB_VAR_INTERFACE)); 628 if (var_interface == NULL) return PP_ERROR_NOINTERFACE; 629 630 messaging_interface = (PPB_Messaging*)(get_browser_interface(PPB_MESSAGING_INTERFACE)); 631 if (messaging_interface == NULL) return PP_ERROR_NOINTERFACE; 632 633 loader_interface = (PPB_URLLoader*)(get_browser_interface(PPB_URLLOADER_INTERFACE)); 634 if (loader_interface == NULL) return PP_ERROR_NOINTERFACE; 635 636 request_interface = (PPB_URLRequestInfo*)(get_browser_interface(PPB_URLREQUESTINFO_INTERFACE)); 637 if (request_interface == NULL) return PP_ERROR_NOINTERFACE; 638 639 response_interface = (PPB_URLResponseInfo*)(get_browser_interface(PPB_URLRESPONSEINFO_INTERFACE)); 640 if (response_interface == NULL) return PP_ERROR_NOINTERFACE; 641 642 fileref_interface = (PPB_FileRef*)(get_browser_interface(PPB_FILEREF_INTERFACE)); 643 if (fileref_interface == NULL) return PP_ERROR_NOINTERFACE; 644 645 return pruby_init() ? PP_ERROR_FAILED : PP_OK; 646} 647 648/** 649 * Returns an interface pointer for the interface of the given name, or NULL 650 * if the interface is not supported. 651 * @param[in] interface_name name of the interface 652 * @return pointer to the interface 653 */ 654PP_EXPORT const void* 655PPP_GetInterface(const char* interface_name) 656{ 657 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) { 658 static PPP_Instance instance_interface = { 659 &Instance_DidCreate, 660 &Instance_DidDestroy, 661 &Instance_DidChangeView, 662 &Instance_DidChangeFocus, 663 &Instance_HandleDocumentLoad 664 }; 665 return &instance_interface; 666 } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) { 667 static PPP_Messaging messaging_interface = { 668 &Messaging_HandleMessage 669 }; 670 return &messaging_interface; 671 } 672 return NULL; 673} 674 675/** 676 * Called before the plugin module is unloaded. 677 */ 678PP_EXPORT void 679PPP_ShutdownModule() 680{ 681 ruby_cleanup(0); 682} 683 684/****************************************************************************** 685 * Overwrites rb_file_load_ok 686 ******************************************************************************/ 687 688static void 689load_ok_internal(void* data, int32_t unused) 690{ 691 /* PPAPI main thread */ 692 struct PepperInstance* const instance = (struct PepperInstance*)data; 693 const char *const path = (const char*)instance->async_call_args; 694 PP_Resource req; 695 int result; 696 697 instance->url_loader = loader_interface->Create(instance->instance); 698 req = request_interface->Create(instance->instance); 699 request_interface->SetProperty( 700 req, PP_URLREQUESTPROPERTY_METHOD, pruby_cstr_to_var("HEAD")); 701 request_interface->SetProperty( 702 req, PP_URLREQUESTPROPERTY_URL, pruby_cstr_to_var(path)); 703 704 result = loader_interface->Open( 705 instance->url_loader, req, 706 PP_MakeCompletionCallback(pruby_async_return_int, instance)); 707 if (result != PP_OK_COMPLETIONPENDING) { 708 pruby_async_return_int(instance, result); 709 } 710} 711 712static void 713pruby_file_fetch_check_response(void* data, int32_t unused) 714{ 715 /* PPAPI main thread */ 716 PP_Resource res; 717 struct PepperInstance* const instance = (struct PepperInstance*)data; 718 719 res = loader_interface->GetResponseInfo(instance->url_loader); 720 if (res) { 721 struct PP_Var status = 722 response_interface->GetProperty(res, PP_URLRESPONSEPROPERTY_STATUSCODE); 723 if (status.type == PP_VARTYPE_INT32) { 724 pruby_async_return_int(instance, status.value.as_int / 100 == 2 ? PP_OK : PP_ERROR_FAILED); 725 return; 726 } 727 else { 728 messaging_interface->PostMessage( 729 instance->instance, pruby_cstr_to_var("Unexpected type: ResponseInfoInterface::GetProperty")); 730 } 731 } 732 else { 733 messaging_interface->PostMessage( 734 instance->instance, pruby_cstr_to_var("Failed to open URL: URLLoaderInterface::GetResponseInfo")); 735 } 736 pruby_async_return_int(instance, PP_ERROR_FAILED); 737} 738 739 740int 741rb_file_load_ok(const char *path) 742{ 743 struct PepperInstance* const instance = GET_PEPPER_INSTANCE(); 744 if (path[0] == '.' && path[1] == '/') path += 2; 745 746 instance->async_call_args = (void*)path; 747 core_interface->CallOnMainThread( 748 0, PP_MakeCompletionCallback(load_ok_internal, instance), 0); 749 if (pthread_cond_wait(&instance->cond, &instance->mutex)) { 750 perror("pepper-ruby:pthread_cond_wait"); 751 return 0; 752 } 753 if (instance->async_call_result.as_int != PP_OK) { 754 fprintf(stderr, "Failed to open URL: %d: %s\n", 755 instance->async_call_result.as_int, path); 756 return 0; 757 } 758 759 core_interface->CallOnMainThread( 760 0, PP_MakeCompletionCallback(pruby_file_fetch_check_response, instance), 0); 761 if (pthread_cond_wait(&instance->cond, &instance->mutex)) { 762 perror("pepper-ruby:pthread_cond_wait"); 763 return 0; 764 } 765 return instance->async_call_result.as_int == PP_OK; 766} 767 768/****************************************************************************** 769 * Overwrites rb_load_file 770 ******************************************************************************/ 771 772static void 773load_file_internal(void* data, int32_t unused) 774{ 775 /* PPAPI main thread */ 776 struct PepperInstance* const instance = (struct PepperInstance*)data; 777 const char *const path = (const char*)instance->async_call_args; 778 PP_Resource req; 779 int result; 780 781 instance->url_loader = loader_interface->Create(instance->instance); 782 req = request_interface->Create(instance->instance); 783 request_interface->SetProperty( 784 req, PP_URLREQUESTPROPERTY_METHOD, pruby_cstr_to_var("GET")); 785 request_interface->SetProperty( 786 req, PP_URLREQUESTPROPERTY_URL, pruby_cstr_to_var(path)); 787 788 result = loader_interface->Open( 789 instance->url_loader, req, 790 PP_MakeCompletionCallback(pruby_async_return_int, instance)); 791 if (result != PP_OK_COMPLETIONPENDING) { 792 pruby_async_return_int(instance, result); 793 } 794} 795 796static void 797load_file_read_contents_callback(void *data, int result) 798{ 799 struct PepperInstance* const instance = (struct PepperInstance*)data; 800 if (result > 0) { 801 rb_str_buf_cat(instance->async_call_result.as_value, 802 instance->buf, result); 803 loader_interface->ReadResponseBody( 804 instance->url_loader, instance->buf, 1000, PP_MakeCompletionCallback(load_file_read_contents_callback, instance)); 805 } 806 else if (result == 0) { 807 pruby_async_return_value(data, instance->async_call_result.as_value); 808 } 809 else { 810 pruby_async_return_value(data, INT2FIX(result)); 811 } 812} 813 814static void 815load_file_read_contents(void *data, int result) 816{ 817 struct PepperInstance* const instance = (struct PepperInstance*)data; 818 instance->async_call_result.as_value = rb_str_new(0, 0); 819 loader_interface->ReadResponseBody( 820 instance->url_loader, instance->buf, 1000, PP_MakeCompletionCallback(load_file_read_contents_callback, instance)); 821} 822 823void* 824rb_load_file(const char *path) 825{ 826 const char *real_path; 827 struct PepperInstance* instance; 828 if (path[0] != '.' || path[1] != '/') path += 2; 829 830 instance = GET_PEPPER_INSTANCE(); 831 832 instance->async_call_args = (void*)path; 833 core_interface->CallOnMainThread( 834 0, PP_MakeCompletionCallback(load_file_internal, instance), 0); 835 if (pthread_cond_wait(&instance->cond, &instance->mutex)) { 836 perror("pepper-ruby:pthread_cond_wait"); 837 return 0; 838 } 839 if (instance->async_call_result.as_int != PP_OK) { 840 fprintf(stderr, "Failed to open URL: %d: %s\n", 841 instance->async_call_result.as_int, path); 842 return 0; 843 } 844 845 core_interface->CallOnMainThread( 846 0, PP_MakeCompletionCallback(pruby_file_fetch_check_response, instance), 0); 847 if (pthread_cond_wait(&instance->cond, &instance->mutex)) { 848 perror("pepper-ruby:pthread_cond_wait"); 849 return 0; 850 } 851 if (instance->async_call_result.as_int != PP_OK) return 0; 852 853 core_interface->CallOnMainThread( 854 0, PP_MakeCompletionCallback(load_file_read_contents, instance), 0); 855 if (pthread_cond_wait(&instance->cond, &instance->mutex)) { 856 perror("pepper-ruby:pthread_cond_wait"); 857 return 0; 858 } 859 if (FIXNUM_P(instance->async_call_result.as_value)) { 860 return 0; 861 } 862 else if (RB_TYPE_P(instance->async_call_result.as_value, T_STRING)) { 863 VALUE str = instance->async_call_result.as_value; 864 extern void* rb_compile_cstr(const char *f, const char *s, int len, int line); 865 return rb_compile_cstr(path, RSTRING_PTR(str), RSTRING_LEN(str), 0); 866 } 867 else { 868 return 0; 869 } 870} 871