PP_Resource
ppb_url_response_info_get_body_as_file_ref(PP_Resource response)
{
    struct pp_url_response_info_s *ri = pp_resource_acquire(response,PP_RESOURCE_URL_RESPONSE_INFO);
    if (!ri) {
        trace_error("%s, bad resource\n", __func__);
        return 0;
    }
    struct pp_url_loader_s *ul = ri->url_loader;

    PP_Resource file_ref = pp_resource_allocate(PP_RESOURCE_FILE_REF, ri->instance);
    struct pp_file_ref_s *fr = pp_resource_acquire(file_ref, PP_RESOURCE_FILE_REF);
    if (!fr) {
        trace_error("%s, resource allocation failure\n", __func__);
        pp_resource_release(response);
        return 0;
    }

    fr->fd = dup(ul->fd);
    fr->type = PP_FILE_REF_TYPE_FD;

    pp_resource_release(file_ref);
    pp_resource_release(response);

    return file_ref;
}
PP_Resource
ppb_url_loader_get_response_info(PP_Resource loader)
{
    struct pp_url_loader_s *ul = pp_resource_acquire(loader, PP_RESOURCE_URL_LOADER);
    if (!ul) {
        trace_error("%s, bad resource\n", __func__);
        return 0;
    }
    PP_Resource response_info = pp_resource_allocate(PP_RESOURCE_URL_RESPONSE_INFO, ul->instance);
    struct pp_url_response_info_s *ri = pp_resource_acquire(response_info,
                                                            PP_RESOURCE_URL_RESPONSE_INFO);
    if (!ri) {
        trace_error("%s, resource allocation failure\n", __func__);
        pp_resource_release(loader);
        return 0;
    }

    pp_resource_ref(loader);
    ri->url_loader_id = loader;
    ri->url_loader = ul;

    pp_resource_release(response_info);
    pp_resource_release(loader);
    return response_info;
}
int32_t
ppb_flash_message_loop_run(PP_Resource flash_message_loop)
{
    struct pp_flash_message_loop_s *fml =
                        pp_resource_acquire(flash_message_loop, PP_RESOURCE_FLASH_MESSAGE_LOOP);
    if (!fml) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    PP_Resource message_loop = ppb_message_loop_get_current();
    fml->running = 1;
    fml->message_loop = message_loop;
    fml->depth = ppb_message_loop_get_depth(message_loop) + 1;

    pp_resource_ref(flash_message_loop);        // prevent destroy of running loop
    pp_resource_release(flash_message_loop);

    // launching nested loop without depth increase to prevent hang up of previously pushed tasks
    ppb_message_loop_run_int(message_loop, ML_NESTED);

    fml = pp_resource_acquire(flash_message_loop, PP_RESOURCE_FLASH_MESSAGE_LOOP);
    if (fml) {
        fml->running = 0;
        pp_resource_release(flash_message_loop);
    }

    pp_resource_unref(flash_message_loop);
    return PP_OK;
}
PP_Resource
ppb_audio_get_current_config(PP_Resource audio)
{
    struct pp_audio_s *a = pp_resource_acquire(audio, PP_RESOURCE_AUDIO);
    if (!a) {
        trace_error("%s, bad resource\n", __func__);
        return 0;
    }

    PP_Resource audio_config = pp_resource_allocate(PP_RESOURCE_AUDIO_CONFIG, a->instance);
    struct pp_audio_config_s *ac = pp_resource_acquire(audio_config, PP_RESOURCE_AUDIO_CONFIG);
    if (!ac) {
        trace_error("%s, resource allocation failure\n", __func__);
        audio_config = 0;
        goto err;
    }

    ac->sample_rate = a->sample_rate;
    ac->sample_frame_count = a->sample_frame_count;

    pp_resource_release(audio_config);

err:
    pp_resource_release(audio);
    return audio_config;
}
PP_Resource
ppb_host_resolver_get_net_address_1_0(PP_Resource host_resolver, uint32_t index)
{
    PP_Resource net_address = 0;
    struct pp_host_resolver_s *hr = pp_resource_acquire(host_resolver, PP_RESOURCE_HOST_RESOLVER);
    if (!hr) {
        trace_error("%s, bad resource\n", __func__);
        goto err_1;
    }

    if (index >= hr->addr_count)
        goto err_2;

    net_address = pp_resource_allocate(PP_RESOURCE_NET_ADDRESS, hr->instance);
    struct pp_net_address_s *na = pp_resource_acquire(net_address, PP_RESOURCE_NET_ADDRESS);
    if (!na) {
        trace_error("%s, resource allocation failed\n", __func__);
        goto err_2;
    }

    memcpy(&na->addr, &hr->addrs[index], sizeof(struct PP_NetAddress_Private));
    pp_resource_release(net_address);

err_2:
    pp_resource_release(host_resolver);
err_1:
    return net_address;
}
void
ppb_video_decoder_assign_picture_buffers(PP_Resource video_decoder, uint32_t no_of_buffers,
                                         const struct PP_PictureBuffer_Dev buffers[])
{
    struct pp_video_decoder_s *vd = pp_resource_acquire(video_decoder, PP_RESOURCE_VIDEO_DECODER);
    if (!vd) {
        trace_error("%s, bad resource\n", __func__);
        goto err_1;
    }

    struct pp_graphics3d_s *g3d = pp_resource_acquire(vd->graphics3d, PP_RESOURCE_GRAPHICS3D);
    if (!g3d) {
        trace_error("%s, bad graphics3d context\n", __func__);
        goto err_2;
    }

    vd->buffers = malloc(no_of_buffers * sizeof(*vd->buffers));
    if (!vd->buffers) {
        trace_error("%s, memory allocation failure\n", __func__);
        goto err_3;
    }

    vd->buffer_count = no_of_buffers;
    for (uintptr_t k = 0; k < no_of_buffers; k ++) {
        vd->buffers[k].id =         buffers[k].id;
        vd->buffers[k].width =      buffers[k].size.width;
        vd->buffers[k].height =     buffers[k].size.height;
        vd->buffers[k].texture_id = buffers[k].texture_id;
        vd->buffers[k].used =       0;

        pthread_mutex_lock(&display.lock);
        vd->buffers[k].pixmap = XCreatePixmap(display.x, DefaultRootWindow(display.x),
                                              buffers[k].size.width, buffers[k].size.height,
                                              g3d->depth);
        int tfp_pixmap_attrs[] = {
            GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
            GLX_MIPMAP_TEXTURE_EXT, GL_FALSE,
            GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
            GL_NONE
        };
        vd->buffers[k].glx_pixmap = glXCreatePixmap(display.x, g3d->fb_config,
                                                    vd->buffers[k].pixmap, tfp_pixmap_attrs);
        pthread_mutex_unlock(&display.lock);
        if (vd->buffers[k].glx_pixmap == None) {
            trace_error("%s, failed to create GLX pixmap\n", __func__);
            goto err_3;
        }
    }

err_3:
    pp_resource_release(vd->graphics3d);
err_2:
    pp_resource_release(video_decoder);
err_1:
    ;
}
PP_Bool
ppb_browser_font_trusted_draw_text_at(PP_Resource font, PP_Resource image_data,
                                      const struct PP_BrowserFont_Trusted_TextRun *text,
                                      const struct PP_Point *position, uint32_t color,
                                      const struct PP_Rect *clip, PP_Bool image_data_is_opaque)
{
    (void)image_data_is_opaque; // TODO: is it worth implementing?
    struct pp_browser_font_s *bf = pp_resource_acquire(font, PP_RESOURCE_BROWSER_FONT);
    if (!bf)
        return PP_FALSE;
    struct pp_image_data_s *id = pp_resource_acquire(image_data, PP_RESOURCE_IMAGE_DATA);
    if (!id) {
        pp_resource_release(font);
        return PP_FALSE;
    }

    cairo_t *cr = cairo_create(id->cairo_surf);
    if (clip) {
        cairo_rectangle(cr, clip->point.x, clip->point.y, clip->size.width, clip->size.height);
        cairo_clip(cr);
    }

    PangoFontMetrics *m = pango_font_get_metrics(bf->font, NULL);
    int32_t ascent = pango_font_metrics_get_ascent(m) / PANGO_SCALE;
    cairo_surface_mark_dirty(id->cairo_surf);
    if (position)
        cairo_move_to(cr, position->x, position->y - ascent);
    else
        cairo_move_to(cr, 0, 0);
    pango_font_metrics_unref(m);

    cairo_set_source_rgba(cr, ((color >> 16) & 0xffu) / 255.0,
                              ((color >> 8) & 0xffu) / 255.0,
                              ((color >> 0) & 0xffu) / 255.0,
                              ((color >> 24) & 0xffu) / 255.0);

    PangoLayout *layout = pango_cairo_create_layout(cr);
    uint32_t len = 0;
    const char *s = "";
    if (text->text.type == PP_VARTYPE_STRING)
        s = ppb_var_var_to_utf8(text->text, &len);

    // TODO: factor into rtl direction
    pango_layout_set_font_description(layout, bf->font_desc);
    pango_layout_set_text(layout, s, len);
    pango_cairo_layout_path(cr, layout);
    cairo_fill(cr);
    g_object_unref(layout);
    cairo_surface_flush(id->cairo_surf);
    cairo_destroy(cr);

    pp_resource_release(font);
    pp_resource_release(image_data);
    return PP_FALSE;
}
static
PP_Resource
do_ppb_audio_create(PP_Instance instance, PP_Resource audio_config,
                    PPB_Audio_Callback_1_0 audio_callback_1_0,
                    PPB_Audio_Callback     audio_callback_1_1, void *user_data)
{
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return 0;
    }

    if (!audio_callback_1_0 && !audio_callback_1_1)
        return PP_ERROR_BADARGUMENT;

    PP_Resource audio = pp_resource_allocate(PP_RESOURCE_AUDIO, pp_i);
    struct pp_audio_s *a = pp_resource_acquire(audio, PP_RESOURCE_AUDIO);
    if (!a) {
        trace_error("%s, resource allocation failure\n", __func__);
        return 0;
    }

    struct pp_audio_config_s *ac = pp_resource_acquire(audio_config, PP_RESOURCE_AUDIO_CONFIG);
    if (!ac) {
        trace_error("%s, bad audio config\n", __func__);
        goto err;
    }

    a->sample_rate = ac->sample_rate;
    a->sample_frame_count = ac->sample_frame_count;
    pp_resource_release(audio_config);

    a->callback_1_0 = audio_callback_1_0;
    a->callback_1_1 = audio_callback_1_1;
    a->user_data = user_data;
    a->stream_ops = audio_select_implementation();
    if (a->stream_ops == NULL) {
        trace_error("%s, no viable audio implementation\n", __func__);
        goto err;
    }

    a->stream = a->stream_ops->create_playback_stream(a->sample_rate, a->sample_frame_count,
                                                      playback_cb, a);
    if (!a->stream) {
        trace_error("%s, can't create playback stream\n", __func__);
        goto err;
    }

    pp_resource_release(audio);
    return audio;
err:
    pp_resource_release(audio);
    pp_resource_expunge(audio);
    return 0;
}
PP_Bool
ppb_instance_bind_graphics(PP_Instance instance, PP_Resource device)
{
    PP_Bool retval;
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return PP_FALSE;
    }

    if (device == 0) {
        // unbind
        pthread_mutex_lock(&pp_i->lock);
        pp_i->graphics = 0;
        pthread_mutex_unlock(&pp_i->lock);
        return PP_TRUE;
    }

    struct pp_graphics2d_s *g2d = pp_resource_acquire(device, PP_RESOURCE_GRAPHICS2D);
    struct pp_graphics3d_s *g3d = pp_resource_acquire(device, PP_RESOURCE_GRAPHICS3D);

    if (g2d) {
        if (pp_i != g2d->instance) {
            retval = PP_FALSE;
            goto done;
        }

        pthread_mutex_lock(&pp_i->lock);
        pp_i->graphics = device;
        pthread_mutex_unlock(&pp_i->lock);
        retval = PP_TRUE;
    } else if (g3d) {
        if (pp_i != g3d->instance) {
            retval = PP_FALSE;
            goto done;
        }

        pthread_mutex_lock(&pp_i->lock);
        pp_i->graphics = device;
        pthread_mutex_unlock(&pp_i->lock);
        pp_resource_release(device);
        retval = PP_TRUE;
    } else {
        trace_warning("%s, unsupported graphics resource %d on instance %d\n", __func__,
                      device, instance);
        retval = PP_FALSE;
    }

done:
    pp_resource_release(device);
    return retval;
}
PP_Resource
ppb_image_data_create(PP_Instance instance, PP_ImageDataFormat format,
                      const struct PP_Size *size, PP_Bool init_to_zero)
{
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return 0;
    }
    PP_Resource image_data = pp_resource_allocate(PP_RESOURCE_IMAGE_DATA, pp_i);
    struct pp_image_data_s *id = pp_resource_acquire(image_data, PP_RESOURCE_IMAGE_DATA);
    if (!id) {
        trace_error("%s, failed to create image data resource\n", __func__);
        return 0;
    }

    id->format = format;
    id->width = size->width;
    id->height = size->height;
    id->stride = id->width * 4;

    (void)init_to_zero; // ignore flag, always clear memory
    id->data = calloc(id->stride * id->height, 1);
    if (!id->data) {
        pp_resource_release(image_data);
        ppb_core_release_resource(image_data);
        trace_error("%s, can't allocate memory for image\n", __func__);
        return 0;
    }

    id->cairo_surf = cairo_image_surface_create_for_data((void *)id->data, CAIRO_FORMAT_ARGB32,
                                                         id->width, id->height, id->stride);
    pp_resource_release(image_data);
    return image_data;
}
PP_Resource
ppb_keyboard_input_event_create_1_2(PP_Instance instance, PP_InputEvent_Type type,
                                    PP_TimeTicks time_stamp, uint32_t modifiers, uint32_t key_code,
                                    struct PP_Var character_text, struct PP_Var code)
{
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return 0;
    }
    PP_Resource input_event = pp_resource_allocate(PP_RESOURCE_INPUT_EVENT, pp_i);
    struct pp_input_event_s *ie = pp_resource_acquire(input_event, PP_RESOURCE_INPUT_EVENT);
    if (!ie) {
        trace_error("%s, can't allocate memory\n", __func__);
        return 0;
    }
    ie->event_class = PP_INPUTEVENT_CLASS_KEYBOARD;
    ie->type = type;
    ie->time_stamp = time_stamp;
    ie->modifiers = modifiers;
    ie->key_code = key_code;
    ie->character_text = character_text;
    ie->code = code;
    ppb_var_add_ref(character_text);
    ppb_var_add_ref(code);

    pp_resource_release(input_event);
    return input_event;
}
PP_Resource
ppb_wheel_input_event_create(PP_Instance instance, PP_TimeTicks time_stamp, uint32_t modifiers,
                             const struct PP_FloatPoint *wheel_delta,
                             const struct PP_FloatPoint *wheel_ticks, PP_Bool scroll_by_page)
{
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return 0;
    }
    PP_Resource input_event = pp_resource_allocate(PP_RESOURCE_INPUT_EVENT, pp_i);
    struct pp_input_event_s *ie = pp_resource_acquire(input_event, PP_RESOURCE_INPUT_EVENT);
    if (!ie) {
        trace_error("%s, can't allocate memory\n", __func__);
        return 0;
    }
    ie->event_class = PP_INPUTEVENT_CLASS_WHEEL;
    ie->time_stamp = time_stamp;
    ie->modifiers = modifiers;
    ie->wheel_delta.x = wheel_delta ? wheel_delta->x : 0;
    ie->wheel_delta.y = wheel_delta ? wheel_delta->y : 0;
    ie->wheel_ticks.x = wheel_ticks ? wheel_ticks->x : 0;
    ie->wheel_ticks.y = wheel_ticks ? wheel_ticks->y : 0;
    ie->scroll_by_page = scroll_by_page;

    pp_resource_release(input_event);
    return input_event;
}
PP_Resource
ppb_mouse_input_event_create(PP_Instance instance, PP_InputEvent_Type type, PP_TimeTicks time_stamp,
                             uint32_t modifiers, PP_InputEvent_MouseButton mouse_button,
                             const struct PP_Point *mouse_position, int32_t click_count,
                             const struct PP_Point *mouse_movement)
{
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return 0;
    }
    PP_Resource input_event = pp_resource_allocate(PP_RESOURCE_INPUT_EVENT, pp_i);
    struct pp_input_event_s *ie = pp_resource_acquire(input_event, PP_RESOURCE_INPUT_EVENT);
    if (!ie) {
        trace_error("%s, can't allocate memory\n", __func__);
        return 0;
    }
    ie->event_class = PP_INPUTEVENT_CLASS_MOUSE;
    ie->type = type;
    ie->time_stamp = time_stamp;
    ie->modifiers = modifiers;
    ie->mouse_button = mouse_button;
    ie->mouse_position.x = mouse_position ? mouse_position->x : 0;
    ie->mouse_position.y = mouse_position ? mouse_position->y : 0;
    ie->click_count = click_count;
    ie->mouse_movement.x = mouse_movement ? mouse_movement->x : 0;
    ie->mouse_movement.y = mouse_movement ? mouse_movement->y : 0;

    pp_resource_release(input_event);
    return input_event;
}
PP_Resource
ppb_video_capture_create(PP_Instance instance)
{
    const struct PPP_VideoCapture_Dev_0_1 *ppp_video_capture_dev;
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return 0;
    }

    ppp_video_capture_dev = ppp_get_interface(PPP_VIDEO_CAPTURE_DEV_INTERFACE_0_1);
    if (!ppp_video_capture_dev) {
        trace_error("%s, no viable %s\n", __func__, PPP_VIDEO_CAPTURE_DEV_INTERFACE_0_1);
        return 0;
    }

    PP_Resource video_capture = pp_resource_allocate(PP_RESOURCE_VIDEO_CAPTURE, pp_i);
    struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
    if (!vc) {
        trace_error("%s, resource allocation failure\n", __func__);
        return 0;
    }

    vc->fd = -1;
    vc->ppp_video_capture_dev = ppp_video_capture_dev;

    pp_resource_release(video_capture);
    return video_capture;
}
int32_t
ppb_host_resolver_resolve(PP_Resource host_resolver, const char *host, uint16_t port,
                          const struct PP_HostResolver_Private_Hint *hint,
                          struct PP_CompletionCallback callback)
{
    struct pp_host_resolver_s *hr = pp_resource_acquire(host_resolver, PP_RESOURCE_HOST_RESOLVER);
    if (!hr) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    hr->host = nullsafe_strdup(host);

    struct async_network_task_s *task = async_network_task_create();

    task->type =        ASYNC_NETWORK_HOST_RESOLVE;
    task->resource =    host_resolver;
    task->host =        nullsafe_strdup(host);
    task->port =        port;
    task->callback =    callback;
    task->callback_ml = ppb_message_loop_get_current();

    pp_resource_release(host_resolver);
    async_network_task_push(task);

    return PP_OK_COMPLETIONPENDING;
}
PP_Resource
ppb_url_request_info_create(PP_Instance instance)
{
    PP_Resource request_info = pp_resource_allocate(PP_RESOURCE_URL_REQUEST_INFO, instance);
    struct pp_url_request_info_s *ri =
                            pp_resource_acquire(request_info, PP_RESOURCE_URL_REQUEST_INFO);

    ri->method = PP_METHOD_UNKNOWN;
    ri->url = NULL;
    ri->headers = NULL;
    ri->stream_to_file = PP_FALSE;
    ri->follow_redirects = PP_TRUE;
    ri->record_download_progress = PP_FALSE;
    ri->record_upload_progress = PP_FALSE;
    ri->custom_referrer_url = NULL;
    ri->allow_cross_origin_requests = PP_FALSE;
    ri->allow_credentials = PP_FALSE;
    ri->custom_content_transfer_encoding = NULL;
    ri->prefetch_buffer_upper_threshold = -1;
    ri->prefetch_buffer_lower_threshold = -1;
    ri->custom_user_agent = NULL;

    pp_resource_release(request_info);
    return request_info;
}
int32_t
ppb_flash_menu_show(PP_Resource menu_id, const struct PP_Point *location, int32_t *selected_id,
                    struct PP_CompletionCallback callback)
{
    struct pp_flash_menu_s *fm = pp_resource_acquire(menu_id, PP_RESOURCE_FLASH_MENU);
    if (!fm) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }
    struct pp_instance_s *pp_i = fm->instance;

    if (popup_menu_sentinel)
        trace_error("%s, two context menus at the same time\n", __func__);

    (void)location; // TODO: handle location

    popup_menu_sentinel = 1;
    popup_menu_canceled = 1;
    popup_menu_ccb = callback;
    popup_menu_result = selected_id;

    pthread_mutex_lock(&display.lock);
    // creating and showing menu together with its closing generates pair of focus events,
    // FocusOut and FocusIn, which should not be passed to the plugin instance. Otherwise they
    // will tamper with text selection.
    pp_i->ignore_focus_events_cnt = 2;
    pthread_mutex_unlock(&display.lock);

    ppb_core_call_on_browser_thread(pp_i->id, menu_popup_ptac, fm->menu);

    pp_resource_release(menu_id);
    return PP_OK_COMPLETIONPENDING;
}
void
ppb_graphics2d_paint_image_data(PP_Resource graphics_2d, PP_Resource image_data,
                                const struct PP_Point *top_left, const struct PP_Rect *src_rect)
{
    struct pp_graphics2d_s *g2d = pp_resource_acquire(graphics_2d, PP_RESOURCE_GRAPHICS2D);
    if (!g2d) {
        trace_error("%s, bad resource\n", __func__);
        return;
    }

    struct g2d_paint_task_s *pt = g_slice_alloc(sizeof(*pt));
    pt->type = gpt_paint_id;
    pp_resource_ref(image_data);
    pt->image_data = image_data;
    pt->src_is_set = !!src_rect;

    if (top_left) {
        memcpy(&pt->ofs, top_left, sizeof(*top_left));
    } else {
        pt->ofs.x = pt->ofs.y = 0;
    }
    if (src_rect)
        memcpy(&pt->src, src_rect, sizeof(*src_rect));

    g2d->task_list = g_list_append(g2d->task_list, pt);
    pp_resource_release(graphics_2d);
}
PP_Bool
ppb_url_request_info_append_data_to_body(PP_Resource request, const void *data, uint32_t len)
{
    struct pp_url_request_info_s *ri = pp_resource_acquire(request, PP_RESOURCE_URL_REQUEST_INFO);
    if (!ri) {
        trace_error("%s, bad resource\n", __func__);
        return PP_FALSE;
    }

    PP_Bool retval = PP_FALSE;
    struct post_data_item_s pdi = { 0 };
    pdi.data = g_memdup(data, len);
    if (!pdi.data) {
        retval = PP_FALSE;
        goto err;
    }

    pdi.len = len;
    g_array_append_val(ri->post_data, pdi);
    retval = PP_TRUE;

err:
    pp_resource_release(request);
    return retval;
}
PP_Resource
ppb_url_request_info_create(PP_Instance instance)
{
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return 0;
    }
    PP_Resource request_info = pp_resource_allocate(PP_RESOURCE_URL_REQUEST_INFO, pp_i);
    struct pp_url_request_info_s *ri =
        pp_resource_acquire(request_info, PP_RESOURCE_URL_REQUEST_INFO);
    if (!ri) {
        trace_error("%s, resource allocation failure\n", __func__);
        return 0;
    }

    ri->method = PP_METHOD_UNKNOWN;
    ri->url = NULL;
    ri->headers = NULL;
    ri->stream_to_file = PP_FALSE;
    ri->follow_redirects = PP_TRUE;
    ri->record_download_progress = PP_FALSE;
    ri->record_upload_progress = PP_FALSE;
    ri->custom_referrer_url = NULL;
    ri->allow_cross_origin_requests = PP_FALSE;
    ri->allow_credentials = PP_FALSE;
    ri->custom_content_transfer_encoding = NULL;
    ri->prefetch_buffer_upper_threshold = -1;
    ri->prefetch_buffer_lower_threshold = -1;
    ri->custom_user_agent = NULL;
    ri->post_data = post_data_new();

    pp_resource_release(request_info);
    return request_info;
}
PP_Resource
ppb_device_ref_create(PP_Instance instance, struct PP_Var name, struct PP_Var longname,
                      PP_DeviceType_Dev type)
{
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return 0;
    }

    PP_Resource device_ref = pp_resource_allocate(PP_RESOURCE_DEVICE_REF, pp_i);
    struct pp_device_ref_s *dr = pp_resource_acquire(device_ref, PP_RESOURCE_DEVICE_REF);
    if (!dr) {
        trace_error("%s, resource allocation failure\n", __func__);
        return 0;
    }

    // no type checking is perfomed as it's an internal function
    dr->name = ppb_var_add_ref2(name);
    dr->longname = ppb_var_add_ref2(longname);
    dr->type = type;

    pp_resource_release(device_ref);
    return device_ref;
}
PP_Bool
ppb_url_loader_get_download_progress(PP_Resource loader, int64_t *bytes_received,
                                     int64_t *total_bytes_to_be_received)
{
    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_FALSE;
    }

    *total_bytes_to_be_received = ul->response_size;
    *bytes_received = 0;
    if (ul->fd >= 0) {
        struct stat sb;
        if (fstat(ul->fd, &sb) != 0) {
            pp_resource_release(loader);
            *bytes_received = -1;
            return PP_FALSE;
        }
        *bytes_received = sb.st_size;
    }

    pp_resource_release(loader);
    return PP_TRUE;
}
int32_t
ppb_url_loader_read_response_body(PP_Resource loader, void *buffer, int32_t bytes_to_read,
                                  struct PP_CompletionCallback callback)
{
    struct url_loader_read_task_s *rt;
    int32_t read_bytes = PP_ERROR_FAILED;
    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;
    }

    if (ul->fd == -1) {
        trace_error("%s, fd==-1\n", __func__);
        pp_resource_release(loader);
        return PP_ERROR_FAILED;
    }

    if (ul->read_tasks) {
        // schedule task instead of immediate reading if there is another task
        // in the queue already
        goto schedule_read_task;
    }

    read_bytes = -1;
    off_t ofs = lseek(ul->fd, ul->read_pos, SEEK_SET);
    if (ofs != (off_t)-1)
        read_bytes = RETRY_ON_EINTR(read(ul->fd, buffer, bytes_to_read));

    if (read_bytes < 0)
        read_bytes = PP_ERROR_FAILED;
    else
        ul->read_pos += read_bytes;

    if (read_bytes == 0 && !ul->finished_loading) {
        // no data ready, schedule read task
        goto schedule_read_task;
    }

    pp_resource_release(loader);
    if (callback.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL)
        return read_bytes;

    ppb_message_loop_post_work_with_result(ppb_message_loop_get_current(), callback, 0, read_bytes,
                                           0, __func__);
    return PP_OK_COMPLETIONPENDING;

schedule_read_task:
    rt = g_slice_alloc(sizeof(*rt));
    rt->url_loader =    loader;
    rt->buffer =        buffer;
    rt->bytes_to_read = bytes_to_read;
    rt->ccb =           callback;
    rt->ccb_ml =        ppb_message_loop_get_current();

    ul->read_tasks = g_list_append(ul->read_tasks, rt);
    pp_resource_release(loader);
    return PP_OK_COMPLETIONPENDING;
}
void
ppb_image_data_unmap(PP_Resource image_data)
{
    struct pp_image_data_s *id = pp_resource_acquire(image_data, PP_RESOURCE_IMAGE_DATA);
    if (!id)
        return;
    pp_resource_release(image_data);
}
PP_Resource
ppb_graphics2d_create(PP_Instance instance, const struct PP_Size *size, PP_Bool is_always_opaque)
{
    struct pp_instance_s *pp_i = tables_get_pp_instance(instance);
    if (!pp_i) {
        trace_error("%s, bad instance\n", __func__);
        return 0;
    }
    PP_Resource graphics_2d = pp_resource_allocate(PP_RESOURCE_GRAPHICS2D, pp_i);
    struct pp_graphics2d_s *g2d = pp_resource_acquire(graphics_2d, PP_RESOURCE_GRAPHICS2D);
    if (!g2d) {
        trace_error("%s, can't create graphics2d resource\n", __func__);
        return 0;
    }

    g2d->is_always_opaque = is_always_opaque;
    g2d->scale = config.device_scale;
    g2d->width =  size->width;
    g2d->height = size->height;
    g2d->stride = 4 * size->width;

    g2d->scaled_width = g2d->width * g2d->scale + 0.5;
    g2d->scaled_height = g2d->height * g2d->scale + 0.5;
    g2d->scaled_stride = 4 * g2d->scaled_width;

    g2d->data = calloc(g2d->stride * g2d->height, 1);
    g2d->second_buffer = calloc(g2d->scaled_stride * g2d->scaled_height, 1);
    if (!g2d->data || !g2d->second_buffer) {
        trace_warning("%s, can't allocate memory\n", __func__);
        free_and_nullify(g2d->data);
        free_and_nullify(g2d->second_buffer);
        pp_resource_release(graphics_2d);
        ppb_core_release_resource(graphics_2d);
        return 0;
    }
    g2d->cairo_surf = cairo_image_surface_create_for_data((unsigned char *)g2d->data,
                            CAIRO_FORMAT_ARGB32, g2d->width, g2d->height, g2d->stride);
    g2d->task_list = NULL;

    if (pp_i->is_transparent) {
        // we need XRender picture (which in turn requires X Pixmap) to alpha blend
        // our images with existing pixmap provided by the browser. This is only needed
        // is instance is transparent, therefore depth is always 32-bit.
        pthread_mutex_lock(&display.lock);
        g2d->pixmap = XCreatePixmap(display.x, DefaultRootWindow(display.x), g2d->scaled_width,
                                    g2d->scaled_height, 32);
        XFlush(display.x);
        g2d->xr_pict = XRenderCreatePicture(display.x, g2d->pixmap, display.pictfmt_argb32, 0, 0);
        g2d->gc = XCreateGC(display.x, g2d->pixmap, 0, 0);
        XFlush(display.x);
        pthread_mutex_unlock(&display.lock);
    }

    pp_resource_release(graphics_2d);
    return graphics_2d;
}
uint32_t
ppb_audio_config_get_sample_frame_count(PP_Resource config)
{
    struct pp_audio_config_s *ac = pp_resource_acquire(config, PP_RESOURCE_AUDIO_CONFIG);
    if (!ac)
        return 0;
    uint32_t sample_frame_count = ac->sample_frame_count;
    pp_resource_release(config);
    return sample_frame_count;
}
PP_AudioSampleRate
ppb_audio_config_get_sample_rate(PP_Resource config)
{
    struct pp_audio_config_s *ac = pp_resource_acquire(config, PP_RESOURCE_AUDIO_CONFIG);
    if (!ac)
        return PP_AUDIOSAMPLERATE_NONE;
    PP_AudioSampleRate sample_rate = ac->sample_rate;
    pp_resource_release(config);
    return sample_rate;
}
void
ppb_image_data_unmap(PP_Resource image_data)
{
    struct pp_image_data_s *id = pp_resource_acquire(image_data, PP_RESOURCE_IMAGE_DATA);
    if (!id) {
        trace_error("%s, bad resource\n", __func__);
        return;
    }
    pp_resource_release(image_data);
}
void
ppb_tcp_socket_disconnect(PP_Resource tcp_socket)
{
    struct pp_tcp_socket_s *ts = pp_resource_acquire(tcp_socket, PP_RESOURCE_TCP_SOCKET);
    if (!ts) {
        trace_error("%s, bad resource\n", __func__);
        return;
    }
    ppb_tcp_socket_destroy(ts);
    pp_resource_release(tcp_socket);
}
PP_InputEvent_Type
ppb_input_event_get_type(PP_Resource event)
{
    struct pp_input_event_s *ie = pp_resource_acquire(event, PP_RESOURCE_INPUT_EVENT);
    if (!ie) {
        trace_error("%s, bad resource\n", __func__);
        return PP_INPUTEVENT_TYPE_UNDEFINED;
    }
    PP_InputEvent_Type t = ie->type;
    pp_resource_release(event);
    return t;
}