static void ppb_device_ref_destroy(void *ptr) { struct pp_device_ref_s *dr = ptr; ppb_var_release(dr->name); ppb_var_release(dr->longname); }
static void ppb_input_event_destroy(void *p) { struct pp_input_event_s *ie = p; ppb_var_release(ie->code); ppb_var_release(ie->text); free_and_nullify(ie->segment_offsets); }
static void p2n_has_method_comt(void *user_data, int32_t result) { struct has_method_param_s *p = user_data; struct np_proxy_object_s *obj = (void *)p->npobj; struct PP_Var exception = PP_MakeUndefined(); struct PP_Var method_name = ppb_var_var_from_utf8_z(p->name); p->result = ppb_var_has_method(obj->ppobj, method_name, &exception); ppb_var_release(method_name); ppb_var_release(exception); ppb_message_loop_post_quit_depth(p->m_loop, PP_FALSE, p->depth); }
struct PP_Var ppb_instance_private_execute_script(PP_Instance instance, struct PP_Var script, struct PP_Var *exception) { if (script.type != PP_VARTYPE_STRING) { trace_error("%s, 'script' is not a string\n", __func__); // TODO: fill exception return PP_MakeUndefined(); } struct pp_instance_s *pp_i = tables_get_pp_instance(instance); if (!pp_i) { trace_error("%s, bad instance\n", __func__); return PP_MakeUndefined(); } struct execute_script_param_s p; p.script = script; p.pp_i = pp_i; p.m_loop = ppb_message_loop_get_current(); p.depth = ppb_message_loop_get_depth(p.m_loop) + 1; ppb_var_add_ref(script); ppb_message_loop_post_work(p.m_loop, PP_MakeCompletionCallback(_execute_script_comt, &p), 0); ppb_message_loop_run_int(p.m_loop, 1); ppb_var_release(script); return p.result; }
struct PP_Var ppb_url_util_dev_get_document_url(PP_Instance instance, struct PP_URLComponents_Dev *components) { struct pp_instance_s *pp_i = tables_get_pp_instance(instance); struct PP_Var result = PP_MakeString(""); NPIdentifier location_id = npn.getstringidentifier("location"); NPIdentifier href_id = npn.getstringidentifier("href"); NPObject *np_window_obj, *np_location_obj; NPVariant location_var, href_var; if (npn.getvalue(pp_i->npp, NPNVWindowNPObject, &np_window_obj) != NPERR_NO_ERROR) goto err_1; if (!npn.getproperty(pp_i->npp, np_window_obj, location_id, &location_var)) goto err_2; if (location_var.type != NPVariantType_Object) goto err_3; np_location_obj = location_var.value.objectValue; if (!npn.getproperty(pp_i->npp, np_location_obj, href_id, &href_var)) goto err_3; struct PP_Var var = np_variant_to_pp_var(href_var); if (var.type != PP_VARTYPE_STRING) { ppb_var_release(var); goto err_4; } ppb_var_release(result); result = var; if (components) parse_url_string(ppb_var_var_to_utf8(result, NULL), components); err_4: npn.releasevariantvalue(&href_var); err_3: npn.releasevariantvalue(&location_var); err_2: npn.releaseobject(np_window_obj); err_1: return result; }
static void p2n_get_property_comt(void *user_data, int32_t result) { struct get_property_param_s *p = user_data; struct np_proxy_object_s *obj = (void *)p->npobj; struct PP_Var exception = PP_MakeUndefined(); struct PP_Var property_name = ppb_var_var_from_utf8_z(p->name); struct PP_Var res = ppb_var_get_property(obj->ppobj, property_name, &exception); p->result = true; *p->np_result = pp_var_to_np_variant(res); ppb_var_release(res); ppb_var_release(exception); ppb_message_loop_post_quit_depth(p->m_loop, PP_FALSE, p->depth); }
TEST(ppb_net_address_private, ipv4_describe) { const uint8_t ip[4] = {192, 168, 1, 2}; const uint16_t port = 1234; struct PP_NetAddress_Private addr; struct PP_Var s; ppb_net_address_private_create_from_ipv4_address(ip, port, &addr); s = ppb_net_address_private_describe(0, &addr, PP_TRUE); ASSERT_STREQ(ppb_var_var_to_utf8(s, NULL), "192.168.1.2:1234"); ppb_var_release(s); s = ppb_net_address_private_describe(0, &addr, PP_FALSE); ASSERT_STREQ(ppb_var_var_to_utf8(s, NULL), "192.168.1.2"); ppb_var_release(s); }
void p2n_deallocate(NPObject *npobj) { struct np_proxy_object_s *obj = (void *)npobj; if (--obj->npobj.referenceCount <= 0) { ppb_var_release(obj->ppobj); npn.memfree(npobj); } }
TEST(ppb_net_address_private, ipv6_describe) { const uint8_t ip[16] = { 0x01, 0x23, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xef, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; const uint16_t port = 1234; struct PP_NetAddress_Private addr; struct PP_Var s; ppb_net_address_private_create_from_ipv6_address(ip, 0, port, &addr); s = ppb_net_address_private_describe(0, &addr, PP_TRUE); ASSERT_STREQ(ppb_var_var_to_utf8(s, NULL), "[123:3456:789a:bcde:ef11:2233:4455:6677]:1234"); ppb_var_release(s); s = ppb_net_address_private_describe(0, &addr, PP_FALSE); ASSERT_STREQ(ppb_var_var_to_utf8(s, NULL), "123:3456:789a:bcde:ef11:2233:4455:6677"); ppb_var_release(s); }
static void _get_document_url_ptac(void *user_data) { struct get_document_url_param_s *p = user_data; p->result = ppb_var_var_from_utf8("", 0); NPIdentifier location_id = npn.getstringidentifier("location"); NPIdentifier href_id = npn.getstringidentifier("href"); NPObject *np_location_obj; NPVariant location_var, href_var; if (!npn.getproperty(p->npp, p->np_window_obj, location_id, &location_var)) goto err_2; if (location_var.type != NPVariantType_Object) goto err_3; np_location_obj = location_var.value.objectValue; if (!npn.getproperty(p->npp, np_location_obj, href_id, &href_var)) goto err_3; struct PP_Var var = np_variant_to_pp_var(href_var); if (var.type != PP_VARTYPE_STRING) { ppb_var_release(var); goto err_4; } ppb_var_release(p->result); p->result = var; err_4: npn.releasevariantvalue(&href_var); err_3: npn.releasevariantvalue(&location_var); err_2: ppb_message_loop_post_quit_depth(p->m_loop, PP_FALSE, p->depth); return; }
static void test_default_charset(void) { // silly test actually. Both test and implementation contains almost the same code setlocale(LC_ALL, ""); char *current_charset = nl_langinfo(CODESET); struct PP_Var cs = ppb_char_set_get_default_char_set(instance); assert(strcmp(ppb_var_var_to_utf8(cs, NULL), current_charset) == 0); printf("default charset = %s\n", current_charset); ppb_var_release(cs); }
static void p2n_invoke_comt(void *user_data, int32_t result) { struct invoke_param_s *p = user_data; unsigned int k; p->result = true; struct np_proxy_object_s *obj = (void *)p->npobj; struct PP_Var exception = PP_MakeUndefined(); struct PP_Var method_name = ppb_var_var_from_utf8_z(p->name); struct PP_Var res; struct PP_Var *pp_args = malloc(p->argCount * sizeof(*pp_args)); for (k = 0; k < p->argCount; k ++) { pp_args[k] = np_variant_to_pp_var(p->args[k]); } res = ppb_var_call(obj->ppobj, method_name, p->argCount, pp_args, &exception); for (k = 0; k < p->argCount; k ++) ppb_var_release(pp_args[k]); free(pp_args); if (p->np_result) { *p->np_result = pp_var_to_np_variant(res); if (p->np_result->type == NPVariantType_Object) { NPP npp = tables_get_npobj_npp_mapping(p->npobj); tables_add_npobj_npp_mapping(p->np_result->value.objectValue, npp); } } ppb_var_release(res); ppb_var_release(method_name); ppb_var_release(exception); ppb_message_loop_post_quit_depth(p->m_loop, PP_FALSE, p->depth); }
TEST(ppb_net_address, ipv6_compose_decompose) { PP_Instance instance = create_instance(); const struct PP_NetAddress_IPv6 ipv6 = { .addr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, .port = htons(3456) }; struct PP_NetAddress_IPv6 ipv6_a = {}; struct PP_NetAddress_IPv4 ipv4 = {}; PP_Resource addr = ppb_net_address_create_from_ipv6_address(instance, &ipv6); ASSERT_EQ(ppb_net_address_is_net_address(addr), PP_TRUE); ASSERT_EQ(ppb_net_address_get_family(addr), PP_NETADDRESS_FAMILY_IPV6); ASSERT_EQ(ppb_net_address_describe_as_ipv4_address(addr, &ipv4), PP_FALSE); ASSERT_EQ(ppb_net_address_describe_as_ipv6_address(addr, &ipv6_a), PP_TRUE); ASSERT_EQ(memcmp(&ipv6, &ipv6_a, sizeof(ipv6_a)), 0); struct PP_Var s = ppb_net_address_describe_as_string(addr, PP_TRUE); ASSERT_STREQ(ppb_var_var_to_utf8(s, NULL), "[102:304:506:708:90a:b0c:d0e:f10]:3456"); ppb_var_release(s); ppb_core_release_resource(addr); destroy_instance(instance); }
TEST(ppb_net_address, ipv4_compose_decompose) { PP_Instance instance = create_instance(); const struct PP_NetAddress_IPv4 ipv4 = { .addr = {192, 168, 1, 2}, .port = htons(3456) }; struct PP_NetAddress_IPv4 ipv4_a = {}; struct PP_NetAddress_IPv6 ipv6 = {}; PP_Resource addr = ppb_net_address_create_from_ipv4_address(instance, &ipv4); ASSERT_EQ(ppb_net_address_is_net_address(addr), PP_TRUE); ASSERT_EQ(ppb_net_address_get_family(addr), PP_NETADDRESS_FAMILY_IPV4); ASSERT_EQ(ppb_net_address_describe_as_ipv6_address(addr, &ipv6), PP_FALSE); ASSERT_EQ(ppb_net_address_describe_as_ipv4_address(addr, &ipv4_a), PP_TRUE); ASSERT_EQ(memcmp(&ipv4, &ipv4_a, sizeof(ipv4_a)), 0); struct PP_Var s = ppb_net_address_describe_as_string(addr, PP_TRUE); ASSERT_STREQ(ppb_var_var_to_utf8(s, NULL), "192.168.1.2:3456"); ppb_var_release(s); ppb_core_release_resource(addr); destroy_instance(instance); }
int32_t ppb_url_loader_open_target(PP_Resource loader, PP_Resource request_info, struct PP_CompletionCallback callback, const char *target) { struct pp_url_loader_s *ul = pp_resource_acquire(loader, PP_RESOURCE_URL_LOADER); if (!ul) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } struct pp_url_request_info_s *ri = pp_resource_acquire(request_info, PP_RESOURCE_URL_REQUEST_INFO); if (!ri) { trace_error("%s, bad resource\n", __func__); pp_resource_release(loader); return PP_ERROR_BADRESOURCE; } struct PP_Var full_url; if (ri->is_immediate_javascript) { full_url = ppb_var_var_from_utf8_z(ri->url); } else { struct PP_Var rel_url = ppb_var_var_from_utf8_z(ri->url); full_url = ppb_url_util_dev_resolve_relative_to_document(ul->instance->id, rel_url, NULL); ppb_var_release(rel_url); } ul->url = nullsafe_strdup(ppb_var_var_to_utf8(full_url, NULL)); ul->method = ri->method; ul->read_pos = 0; ul->request_headers = nullsafe_strdup(ri->headers); ul->follow_redirects = ri->follow_redirects; ul->stream_to_file = ri->stream_to_file; ul->record_download_progress = ri->record_download_progress; ul->record_upload_progress = ri->record_upload_progress; ul->custom_referrer_url = nullsafe_strdup(ri->custom_referrer_url); ul->allow_cross_origin_requests = ri->allow_cross_origin_requests; ul->allow_credentials = ri->allow_credentials; ul->custom_content_transfer_encoding = nullsafe_strdup(ri->custom_content_transfer_encoding); ul->custom_user_agent = nullsafe_strdup(ri->custom_user_agent); ul->target = nullsafe_strdup(target); #define TRIM_NEWLINE(s) s = trim_nl(s) TRIM_NEWLINE(ul->request_headers); TRIM_NEWLINE(ul->custom_referrer_url); TRIM_NEWLINE(ul->custom_content_transfer_encoding); TRIM_NEWLINE(ul->custom_user_agent); ul->post_len = ri->post_len; if (ri->post_len > 0) { ul->post_data = malloc(ri->post_len); memcpy(ul->post_data, ri->post_data, ri->post_len); } ul->fd = open_temporary_file(); ul->ccb = callback; ppb_var_release(full_url); pp_resource_release(request_info); struct url_loader_open_param_s *p = g_slice_alloc(sizeof(*p)); p->url = ul->url; p->loader = loader; p->instance_id = ul->instance->id; p->method = ul->method; p->request_headers = ul->request_headers; p->custom_referrer_url = ul->custom_referrer_url; p->custom_content_transfer_encoding = ul->custom_content_transfer_encoding; p->custom_user_agent = ul->custom_user_agent; p->target = ul->target; p->post_len = ul->post_len; p->post_data = ul->post_data; p->m_loop = ppb_message_loop_get_current(); p->depth = ppb_message_loop_get_depth(p->m_loop) + 1; ppb_core_add_ref_resource(loader); // add ref to ensure data in ul remain accessible pp_resource_release(loader); ppb_message_loop_post_work(p->m_loop, PP_MakeCCB(_url_loader_open_comt, p), 0); ppb_message_loop_run_nested(p->m_loop); ppb_core_release_resource(loader); int retval = p->retval; g_slice_free1(sizeof(*p), p); if (retval != NPERR_NO_ERROR) return PP_ERROR_FAILED; if (callback.func == NULL) { int done = 0; while (!done) { ul = pp_resource_acquire(loader, PP_RESOURCE_URL_LOADER); if (ul) { done = ul->finished_loading; pp_resource_release(loader); } else { break; } printf("waitin'\n"); usleep(10000); } return PP_OK; } return PP_OK_COMPLETIONPENDING; }
int32_t ppb_video_capture_open(PP_Resource video_capture, PP_Resource device_ref, const struct PP_VideoCaptureDeviceInfo_Dev *requested_info, uint32_t buffer_count, struct PP_CompletionCallback callback) { int32_t result; struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } const char *capture_device = default_capture_device; struct PP_Var longname = ppb_device_ref_get_longname(device_ref); if (longname.type == PP_VARTYPE_STRING) capture_device = ppb_var_var_to_utf8(longname, NULL); vc->fd = v4l2_open(capture_device, O_RDWR); ppb_var_release(longname); if (vc->fd < 0) { result = PP_ERROR_NOACCESS; goto point_1; } struct v4l2_capability caps; if (v4l2_ioctl(vc->fd, VIDIOC_QUERYCAP, &caps) != 0) { result = PP_ERROR_FAILED; goto point_2; } #ifdef V4L2_CAP_DEVICE_CAPS const uint32_t device_caps = (caps.capabilities & V4L2_CAP_DEVICE_CAPS) ? caps.device_caps : caps.capabilities; #else const uint32_t device_caps = caps.capabilities; #endif // V4L2_CAP_DEVICE_CAPS if (!(device_caps & V4L2_CAP_VIDEO_CAPTURE)) { trace_error("%s, device can't capture\n", __func__); result = PP_ERROR_FAILED; goto point_2; } if (!(device_caps & V4L2_CAP_READWRITE)) { trace_error("%s, device doesn't support read/write interface\n", __func__); result = PP_ERROR_FAILED; goto point_2; } if (requested_info) { vc->width = requested_info->width; vc->height = requested_info->height; vc->fps = requested_info->frames_per_second; } else { vc->width = 640; vc->height = 480; vc->fps = 15; } struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .fmt.pix.width = vc->width, .fmt.pix.height = vc->height, .fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420, // PPAPI hardcodes format to YUV420 .fmt.pix.field = V4L2_FIELD_INTERLACED, }; if (v4l2_ioctl(vc->fd, VIDIOC_S_FMT, &fmt) != 0) { trace_error("%s, failed to set resolution\n", __func__); result = PP_ERROR_FAILED; goto point_2; } vc->width = fmt.fmt.pix.width; vc->height = fmt.fmt.pix.height; vc->buffer_size = fmt.fmt.pix.sizeimage; // buffer size in bytes vc->buffer_count = MAX(buffer_count, 5); // limit lowest number of buffers, just in case vc->buffers = calloc(sizeof(*vc->buffers), vc->buffer_count); if (!vc->buffers) { trace_error("%s, memory allocation failure (1)\n", __func__); result = PP_ERROR_FAILED; goto point_2; } vc->buffer_is_free = malloc(sizeof(*vc->buffer_is_free) * vc->buffer_count); if (!vc->buffer_is_free) { trace_error("%s, memory allocation failure (2)\n", __func__); result = PP_ERROR_FAILED; goto point_3; } for (unsigned int k = 0; k < vc->buffer_count; k ++) { vc->buffer_is_free[k] = 1; vc->buffers[k] = ppb_buffer_create(vc->instance->id, vc->buffer_size); if (vc->buffers[k] == 0) goto point_4; } struct PP_VideoCaptureDeviceInfo_Dev info = { .width = vc->width, .height = vc->height, .frames_per_second = vc->fps, }; vc->ppp_video_capture_dev->OnDeviceInfo(vc->instance->id, video_capture, &info, vc->buffer_count, vc->buffers); result = PP_OK; goto point_1; point_4: for (unsigned int k = 0; k < vc->buffer_count; k ++) ppb_core_release_resource(vc->buffers[k]); free_and_nullify(vc->buffer_is_free); point_3: free_and_nullify(vc->buffers); point_2: v4l2_close(vc->fd); vc->fd = -1; point_1: pp_resource_release(video_capture); ppb_core_call_on_main_thread2(0, callback, result, __func__); return PP_OK_COMPLETIONPENDING; } struct on_buffer_ready_param_s { PP_Instance instance; PP_Resource video_capture; uint32_t buf_idx; const struct PPP_VideoCapture_Dev_0_1 *ppp_video_capture_dev; }; static void on_buffer_ready_comt(void *user_data, int32_t result) { struct on_buffer_ready_param_s *p = user_data; struct pp_instance_s *pp_i = tables_get_pp_instance(p->instance); if (!pp_i) return; p->ppp_video_capture_dev->OnBufferReady(p->instance, p->video_capture, p->buf_idx); g_slice_free1(sizeof(*p), p); } static void * video_capture_thread(void *param) { struct pp_video_capture_s *vc = param; PP_Resource video_capture = vc->self_id; PP_Instance instance = vc->instance->id; const int fd = vc->fd; const size_t buffer_size = vc->buffer_size; vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) goto gone; while (!vc->terminate_thread) { // find free buffer uint32_t buf_idx = (uint32_t)-1; for (uint32_t k = 0; k < vc->buffer_count; k ++) { if (vc->buffer_is_free[k]) { buf_idx = k; vc->buffer_is_free[k] = 0; break; } } if (buf_idx == (uint32_t)-1) { // all buffers are busy, wait for some to free, with resource unlocked pp_resource_release(video_capture); usleep(10); vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) goto gone; continue; } PP_Resource buffer = vc->buffers[buf_idx]; pp_resource_release(video_capture); // wait on v4l2_read() with resource unlocked void *ptr = ppb_buffer_map(buffer); RETRY_ON_EINTR(v4l2_read(fd, ptr, buffer_size)); ppb_buffer_unmap(buffer); vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) goto gone; struct on_buffer_ready_param_s *p = g_slice_alloc(sizeof(*p)); p->instance = instance; p->video_capture = video_capture; p->buf_idx = buf_idx; p->ppp_video_capture_dev = vc->ppp_video_capture_dev; ppb_core_call_on_main_thread2(0, PP_MakeCCB(on_buffer_ready_comt, p), PP_OK, __func__); } pp_resource_release(video_capture); return NULL; gone: trace_error("%s, resource gone\n", __func__); return NULL; } int32_t ppb_video_capture_start_capture(PP_Resource video_capture) { struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } if (vc->thread_started) goto done; if (vc->fd < 0) { trace_error("%s, device is closed\n", __func__); pp_resource_release(video_capture); return PP_ERROR_FAILED; } vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture, PP_VIDEO_CAPTURE_STATUS_STARTING); pp_resource_ref(video_capture); // prevents freeing while thread is still running pthread_create(&vc->thread, NULL, video_capture_thread, vc); vc->thread_started = 1; vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture, PP_VIDEO_CAPTURE_STATUS_STARTED); done: pp_resource_release(video_capture); return PP_OK; } int32_t ppb_video_capture_reuse_buffer(PP_Resource video_capture, uint32_t buffer) { struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } if (buffer < vc->buffer_count) vc->buffer_is_free[buffer] = 1; pp_resource_release(video_capture); return PP_OK; } int32_t ppb_video_capture_stop_capture(PP_Resource video_capture) { struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } if (!vc->thread_started) goto done; vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture, PP_VIDEO_CAPTURE_STATUS_STOPPING); vc->terminate_thread = 1; pthread_t thread = vc->thread; pp_resource_release(video_capture); pthread_join(thread, NULL); vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, resource gone\n", __func__); return PP_ERROR_BADRESOURCE; } vc->thread_started = 0; vc->terminate_thread = 0; vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture, PP_VIDEO_CAPTURE_STATUS_STOPPED); pp_resource_unref(video_capture); // remove reference made in start_capture() done: pp_resource_release(video_capture); return PP_OK; } void ppb_video_capture_close(PP_Resource video_capture) { ppb_video_capture_stop_capture(video_capture); struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return; } ppb_video_capture_destroy(vc); pp_resource_release(video_capture); return; } // trace wrappers TRACE_WRAPPER PP_Resource trace_ppb_video_capture_create(PP_Instance instance) { trace_info("[PPB] {full} %s instance=%d\n", __func__+6, instance); return ppb_video_capture_create(instance); }