1314 lines
36 KiB
C
1314 lines
36 KiB
C
/*
|
|
* npruntime.c - Scripting plugins support
|
|
*
|
|
* nspluginwrapper (C) 2005-2009 Gwenole Beauchesne
|
|
* (C) 2011 David Benjamin
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
|
|
#include <assert.h>
|
|
#include <glib.h> /* <glib/ghash.h> */
|
|
#include "utils.h"
|
|
#include "npw-common.h"
|
|
#include "npw-malloc.h"
|
|
|
|
#define DEBUG 1
|
|
#include "debug.h"
|
|
|
|
|
|
// Defined in npw-{wrapper,viewer}.c
|
|
extern rpc_connection_t *g_rpc_connection attribute_hidden;
|
|
|
|
// Defined in npw-viewer.c
|
|
#if defined(ENABLE_THREAD_CHECK) && NPW_IS_PLUGIN
|
|
extern bool thread_check(void);
|
|
#else
|
|
#define thread_check() true
|
|
#endif
|
|
|
|
|
|
/* ====================================================================== */
|
|
/* === Helpers === */
|
|
/* ====================================================================== */
|
|
|
|
static inline bool get_use_npruntime_cache_env(void)
|
|
{
|
|
const gchar *env = getenv("NPW_NPRUNTIME_CACHE");
|
|
return env == NULL || (strcmp(env, "no") != 0 && strcmp(env, "0") != 0);
|
|
}
|
|
|
|
bool npruntime_use_cache(void)
|
|
{
|
|
static int use_cache = -1;
|
|
if (G_UNLIKELY(use_cache < 0))
|
|
use_cache = get_use_npruntime_cache_env();
|
|
return use_cache;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
/* === NPObject stubs === */
|
|
/* ====================================================================== */
|
|
|
|
static GHashTable *g_stubs = NULL; // uint32_t -> (NPObjectStub *)
|
|
|
|
typedef struct _NPObjectStub {
|
|
NPObject *npobject;
|
|
uint32_t id;
|
|
} NPObjectStub;
|
|
|
|
uint32_t npobject_create_stub(NPObject *npobj)
|
|
{
|
|
npw_return_val_if_fail(npobj != NULL, 0);
|
|
|
|
static uint32_t next_id = 0;
|
|
|
|
// Allocate an id. Client and server get separate id spaces because
|
|
// of type field.
|
|
uint32_t id = ++next_id;
|
|
|
|
D(bug("npobject_create_stub: npobj=%p, id=0x%x\n", npobj, id));
|
|
NPObjectStub *stub = g_new0(NPObjectStub, 1);
|
|
stub->npobject = NPN_RetainObject(npobj);
|
|
stub->id = id;
|
|
g_hash_table_insert(g_stubs, GINT_TO_POINTER(stub->id), stub);
|
|
|
|
return stub->id;
|
|
}
|
|
|
|
static NPObjectStub *npobject_lookup_stub(uint32_t id)
|
|
{
|
|
return g_hash_table_lookup(g_stubs, GINT_TO_POINTER(id));
|
|
}
|
|
|
|
NPObject *npobject_lookup_local(uint32_t id)
|
|
{
|
|
NPObjectStub *stub = npobject_lookup_stub(id);
|
|
return stub ? stub->npobject : NULL;
|
|
}
|
|
|
|
static void npobject_destroy_stub_obj(NPObjectStub *stub)
|
|
{
|
|
D(bugiI("npobject_destroy_stub: id=0x%x\n", stub->id));
|
|
g_hash_table_remove(g_stubs, GINT_TO_POINTER(stub->id));
|
|
NPN_ReleaseObject(stub->npobject);
|
|
g_free(stub);
|
|
D(bugiD("npobject_destroy_stub done\n"));
|
|
}
|
|
|
|
void npobject_destroy_stub(uint32_t id)
|
|
{
|
|
NPObjectStub *stub = npobject_lookup_stub(id);
|
|
assert(stub != NULL);
|
|
npobject_destroy_stub_obj(stub);
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
/* === NPObject proxies === */
|
|
/* ====================================================================== */
|
|
|
|
static GHashTable *g_proxies = NULL; // uint32_t -> (NPObjectProxy *)
|
|
|
|
static NPClass npclass_bridge;
|
|
|
|
typedef struct _NPObjectProxy {
|
|
NPObject parent;
|
|
uint32_t id;
|
|
bool is_valid;
|
|
} NPObjectProxy;
|
|
|
|
static NPObjectProxy *npobject_get_proxy(NPObject *npobj)
|
|
{
|
|
if (npobj->_class != &npclass_bridge)
|
|
return NULL;
|
|
return (NPObjectProxy *)npobj;
|
|
}
|
|
|
|
NPObject *npobject_create_proxy(NPP instance, uint32_t id)
|
|
{
|
|
D(bugiI("npobject_create_proxy: NPP=%p, id=0x%x\n", instance, id));
|
|
|
|
NPObject *object = NPN_CreateObject(instance, &npclass_bridge);
|
|
NPObjectProxy *proxy = npobject_get_proxy(object);
|
|
proxy->id = id;
|
|
proxy->is_valid = true;
|
|
// There isn't a huge need to track them by id. Any, really. But it
|
|
// does let us invalidate them all.
|
|
g_hash_table_insert(g_proxies, GINT_TO_POINTER(id), proxy);
|
|
D(bugiD("npobject_create_proxy done: obj=%p\n", object));
|
|
return object;
|
|
}
|
|
|
|
bool npobject_is_proxy(NPObject *npobj)
|
|
{
|
|
return npobject_get_proxy(npobj) != NULL;
|
|
}
|
|
|
|
uint32_t npobject_get_proxy_id(NPObject *npobj)
|
|
{
|
|
NPObjectProxy *proxy = npobject_get_proxy(npobj);
|
|
if (proxy == NULL)
|
|
return 0;
|
|
return proxy->id;
|
|
}
|
|
|
|
static void npclass_invoke_Deallocate(NPObjectProxy *proxy);
|
|
|
|
void npobject_destroy_proxy(NPObject *npobj, bool release_stub)
|
|
{
|
|
// Unregister the proxy.
|
|
D(bugiI("npobject_destroy_proxy: npobj=%p, release_stub=%d\n",
|
|
npobj, release_stub));
|
|
NPObjectProxy *proxy = npobject_get_proxy(npobj);
|
|
assert(proxy != NULL);
|
|
if (release_stub && proxy->is_valid) {
|
|
npclass_invoke_Deallocate(proxy);
|
|
}
|
|
g_hash_table_remove(g_proxies, GINT_TO_POINTER(proxy->id));
|
|
free(npobj);
|
|
D(bugiD("npobject_destroy_proxy done\n"));
|
|
}
|
|
|
|
static inline bool is_valid_npobject_proxy(NPObject *npobj)
|
|
{
|
|
if (npobj == NULL)
|
|
return false;
|
|
NPObjectProxy *proxy = npobject_get_proxy(npobj);
|
|
if (proxy == NULL)
|
|
return false;
|
|
if (!proxy->is_valid)
|
|
npw_printf("ERROR: NPObject proxy %p is no longer valid!\n", npobj);
|
|
return proxy->is_valid;
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
/* === NPClass Bridge === */
|
|
/* ====================================================================== */
|
|
|
|
static NPObject *g_NPClass_Allocate(NPP npp, NPClass *aclass);
|
|
static void g_NPClass_Deallocate(NPObject *npobj);
|
|
static void g_NPClass_Invalidate(NPObject *npobj);
|
|
static bool g_NPClass_HasMethod(NPObject *npobj, NPIdentifier name);
|
|
static bool g_NPClass_Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
|
|
static bool g_NPClass_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);
|
|
static bool g_NPClass_HasProperty(NPObject *npobj, NPIdentifier name);
|
|
static bool g_NPClass_GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result);
|
|
static bool g_NPClass_SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value);
|
|
static bool g_NPClass_RemoveProperty(NPObject *npobj, NPIdentifier name);
|
|
static bool g_NPClass_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count);
|
|
static bool g_NPClass_Construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);
|
|
|
|
static NPClass npclass_bridge = {
|
|
NPW_NP_CLASS_STRUCT_VERSION,
|
|
g_NPClass_Allocate,
|
|
g_NPClass_Deallocate,
|
|
g_NPClass_Invalidate,
|
|
g_NPClass_HasMethod,
|
|
g_NPClass_Invoke,
|
|
g_NPClass_InvokeDefault,
|
|
g_NPClass_HasProperty,
|
|
g_NPClass_GetProperty,
|
|
g_NPClass_SetProperty,
|
|
g_NPClass_RemoveProperty,
|
|
g_NPClass_Enumerate,
|
|
g_NPClass_Construct
|
|
};
|
|
|
|
static inline bool is_valid_npobject_class(NPObject *npobj)
|
|
{
|
|
if (npobj == NULL || npobj->_class == NULL)
|
|
return false;
|
|
#if NPW_IS_PLUGIN
|
|
// Reject requests on invalidated objects.
|
|
if (!npobject_is_registered(npobj)) {
|
|
npw_printf("ERROR: accessed invalidated NPObject\n");
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// NPClass::Allocate
|
|
NPObject *g_NPClass_Allocate(NPP npp, NPClass *aclass)
|
|
{
|
|
return malloc(sizeof(NPObjectProxy));
|
|
}
|
|
|
|
// NPClass::Deallocate
|
|
static int npclass_handle_Deallocate(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_Deallocate\n"));
|
|
|
|
uint32_t id;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_UINT32, &id,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Deallocate() get args", error);
|
|
return error;
|
|
}
|
|
|
|
D(bugiI("NPClass:Deallocate: id=0x%x\n", id));
|
|
NPObjectStub *stub = npobject_lookup_stub(id);
|
|
if (stub != NULL) {
|
|
npobject_destroy_stub_obj(stub);
|
|
}
|
|
D(bugiD("NPClass:Deallocate done\n"));
|
|
|
|
return rpc_method_send_reply(connection, RPC_TYPE_INVALID);
|
|
}
|
|
|
|
static void npclass_invoke_Deallocate(NPObjectProxy *proxy)
|
|
{
|
|
npw_return_if_fail(rpc_method_invoke_possible(g_rpc_connection));
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_DEALLOCATE,
|
|
RPC_TYPE_UINT32, proxy->id,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Deallocate() invoke", error);
|
|
return;
|
|
}
|
|
|
|
// FIXME: This really could be asynchronous...
|
|
error = rpc_method_wait_for_reply(g_rpc_connection, RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Deallocate() wait for reply", error);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void g_NPClass_Deallocate(NPObject *npobj)
|
|
{
|
|
npobject_destroy_proxy(npobj, true);
|
|
}
|
|
|
|
void g_NPClass_Invalidate(NPObject *npobj)
|
|
{
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::Invalidate not called from the main thread\n");
|
|
return;
|
|
}
|
|
|
|
D(bugiI("NPClass::Invalidate(npobj %p)\n", npobj));
|
|
// Just invalidate the proxy itself. There may be multiple proxies
|
|
// for a plugin-side NPObject. We'll invalidate them viewer-side.
|
|
NPObjectProxy *proxy = npobject_get_proxy(npobj);
|
|
proxy->is_valid = false;
|
|
// Release the underlying stub now, otherwise the is_valid check
|
|
// will refuse to do so. The stub is of no use to us now.
|
|
npclass_invoke_Deallocate(proxy);
|
|
D(bugiD("NPClass::Invalidate done\n"));
|
|
}
|
|
|
|
// NPClass::HasMethod
|
|
static int npclass_handle_HasMethod(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_HasMethod\n"));
|
|
|
|
NPObject *npobj;
|
|
NPIdentifier name;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_NP_OBJECT, &npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::HasMethod() get args", error);
|
|
return error;
|
|
}
|
|
|
|
uint32_t ret = false;
|
|
if (npobj && is_valid_npobject_class(npobj) && npobj->_class->hasMethod) {
|
|
D(bugiI("NPClass::HasMethod(npobj %p, name id %p)\n", npobj, name));
|
|
ret = npobj->_class->hasMethod(npobj, name);
|
|
D(bugiD("NPClass::HasMethod return: %d\n", ret));
|
|
}
|
|
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
|
|
return rpc_method_send_reply(connection,
|
|
RPC_TYPE_UINT32, ret,
|
|
RPC_TYPE_INVALID);
|
|
}
|
|
|
|
static bool npclass_invoke_HasMethod(NPObject *npobj, NPIdentifier name)
|
|
{
|
|
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), false);
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_HAS_METHOD,
|
|
RPC_TYPE_NP_OBJECT, npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::HasMethod() invoke", error);
|
|
return false;
|
|
}
|
|
|
|
uint32_t ret;
|
|
error = rpc_method_wait_for_reply(g_rpc_connection, RPC_TYPE_UINT32, &ret, RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::HasMethod() wait for reply", error);
|
|
return false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool g_NPClass_HasMethod(NPObject *npobj, NPIdentifier name)
|
|
{
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return false;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::HasMethod not called from the main thread\n");
|
|
return false;
|
|
}
|
|
|
|
D(bugiI("NPClass::HasMethod(npobj %p, name id %p)\n", npobj, name));
|
|
bool ret = npclass_invoke_HasMethod(npobj, name);
|
|
D(bugiD("NPClass::HasMethod return: %d\n", ret));
|
|
return ret;
|
|
}
|
|
|
|
// NPClass::Invoke
|
|
static int npclass_handle_Invoke(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_Invoke\n"));
|
|
|
|
NPObject *npobj;
|
|
NPIdentifier name;
|
|
uint32_t argCount;
|
|
NPVariant *args;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_NP_OBJECT, &npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_ARRAY, RPC_TYPE_NP_VARIANT, &argCount, &args,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Invoke() get args", error);
|
|
return error;
|
|
}
|
|
|
|
uint32_t ret = false;
|
|
NPVariant result;
|
|
VOID_TO_NPVARIANT(result);
|
|
if (npobj && is_valid_npobject_class(npobj) && npobj->_class->invoke) {
|
|
D(bugiI("NPClass::Invoke(npobj %p, name id %p)\n", npobj, name));
|
|
print_npvariant_args(args, argCount);
|
|
ret = npobj->_class->invoke(npobj, name, args, argCount, &result);
|
|
gchar *result_str = string_of_NPVariant(&result);
|
|
D(bugiD("NPClass::Invoke return: %d (%s)\n", ret, result_str));
|
|
g_free(result_str);
|
|
}
|
|
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
if (args) {
|
|
for (int i = 0; i < argCount; i++)
|
|
NPN_ReleaseVariantValue(&args[i]);
|
|
free(args);
|
|
}
|
|
|
|
return rpc_method_send_reply(connection,
|
|
RPC_TYPE_UINT32, ret,
|
|
RPC_TYPE_NP_VARIANT_PASS_REF, &result,
|
|
RPC_TYPE_INVALID);
|
|
}
|
|
|
|
static bool npclass_invoke_Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount,
|
|
NPVariant *result)
|
|
{
|
|
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), false);
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_INVOKE,
|
|
RPC_TYPE_NP_OBJECT, npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_ARRAY, RPC_TYPE_NP_VARIANT, argCount, args,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Invoke() invoke", error);
|
|
return false;
|
|
}
|
|
|
|
uint32_t ret;
|
|
error = rpc_method_wait_for_reply(g_rpc_connection,
|
|
RPC_TYPE_UINT32, &ret,
|
|
RPC_TYPE_NP_VARIANT_PASS_REF, result,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Invoke() wait for reply", error);
|
|
return false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool g_NPClass_Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount,
|
|
NPVariant *result)
|
|
{
|
|
if (result == NULL)
|
|
return false;
|
|
VOID_TO_NPVARIANT(*result);
|
|
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return false;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::Invoke not called from the main thread\n");
|
|
return false;
|
|
}
|
|
|
|
D(bugiI("NPClass::Invoke(npobj %p, name id %p)\n", npobj, name));
|
|
print_npvariant_args(args, argCount);
|
|
bool ret = npclass_invoke_Invoke(npobj, name, args, argCount, result);
|
|
gchar *result_str = string_of_NPVariant(result);
|
|
D(bugiD("NPClass::Invoke return: %d (%s)\n", ret, result_str));
|
|
g_free(result_str);
|
|
return ret;
|
|
}
|
|
|
|
// NPClass::InvokeDefault
|
|
static int npclass_handle_InvokeDefault(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_InvokeDefault\n"));
|
|
|
|
NPObject *npobj;
|
|
uint32_t argCount;
|
|
NPVariant *args;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_NP_OBJECT, &npobj,
|
|
RPC_TYPE_ARRAY, RPC_TYPE_NP_VARIANT, &argCount, &args,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::InvokeDefault() get args", error);
|
|
return error;
|
|
}
|
|
|
|
uint32_t ret = false;
|
|
NPVariant result;
|
|
VOID_TO_NPVARIANT(result);
|
|
if (npobj && is_valid_npobject_class(npobj) && npobj->_class->invokeDefault) {
|
|
D(bugiI("NPClass::InvokeDefault(npobj %p)\n", npobj));
|
|
print_npvariant_args(args, argCount);
|
|
ret = npobj->_class->invokeDefault(npobj, args, argCount, &result);
|
|
gchar *result_str = string_of_NPVariant(&result);
|
|
D(bugiD("NPClass::InvokeDefault return: %d (%s)\n", ret, result_str));
|
|
g_free(result_str);
|
|
}
|
|
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
if (args) {
|
|
for (int i = 0; i < argCount; i++)
|
|
NPN_ReleaseVariantValue(&args[i]);
|
|
free(args);
|
|
}
|
|
|
|
return rpc_method_send_reply(connection,
|
|
RPC_TYPE_UINT32, ret,
|
|
RPC_TYPE_NP_VARIANT_PASS_REF, &result,
|
|
RPC_TYPE_INVALID);
|
|
}
|
|
|
|
static bool npclass_invoke_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount,
|
|
NPVariant *result)
|
|
{
|
|
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), false);
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_INVOKE_DEFAULT,
|
|
RPC_TYPE_NP_OBJECT, npobj,
|
|
RPC_TYPE_ARRAY, RPC_TYPE_NP_VARIANT, argCount, args,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::InvokeDefault() invoke", error);
|
|
return false;
|
|
}
|
|
|
|
uint32_t ret;
|
|
error = rpc_method_wait_for_reply(g_rpc_connection,
|
|
RPC_TYPE_UINT32, &ret,
|
|
RPC_TYPE_NP_VARIANT_PASS_REF, result,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::InvokeDefault() wait for reply", error);
|
|
return false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool g_NPClass_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount,
|
|
NPVariant *result)
|
|
{
|
|
if (result == NULL)
|
|
return false;
|
|
VOID_TO_NPVARIANT(*result);
|
|
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return false;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::InvokeDefault not called from the main thread\n");
|
|
return false;
|
|
}
|
|
|
|
D(bugiI("NPClass::InvokeDefault(npobj %p)\n", npobj));
|
|
print_npvariant_args(args, argCount);
|
|
bool ret = npclass_invoke_InvokeDefault(npobj, args, argCount, result);
|
|
gchar *result_str = string_of_NPVariant(result);
|
|
D(bugiD("NPClass::InvokeDefault return: %d (%s)\n", ret, result_str));
|
|
g_free(result_str);
|
|
return ret;
|
|
}
|
|
|
|
// NPClass::HasProperty
|
|
static int npclass_handle_HasProperty(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_HasProperty\n"));
|
|
|
|
NPObject *npobj;
|
|
NPIdentifier name;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_NP_OBJECT, &npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::HasProperty() get args", error);
|
|
return error;
|
|
}
|
|
|
|
uint32_t ret = false;
|
|
if (npobj && is_valid_npobject_class(npobj) && npobj->_class->hasProperty) {
|
|
D(bugiI("NPClass::HasProperty(npobj %p, name id %p)\n", npobj, name));
|
|
ret = npobj->_class->hasProperty(npobj, name);
|
|
D(bugiD("NPClass::HasProperty return: %d\n", ret));
|
|
}
|
|
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
|
|
return rpc_method_send_reply(connection,
|
|
RPC_TYPE_UINT32, ret,
|
|
RPC_TYPE_INVALID);
|
|
}
|
|
|
|
static bool npclass_invoke_HasProperty(NPObject *npobj, NPIdentifier name)
|
|
{
|
|
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), false);
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_HAS_PROPERTY,
|
|
RPC_TYPE_NP_OBJECT, npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::HasProperty() invoke", error);
|
|
return false;
|
|
}
|
|
|
|
uint32_t ret;
|
|
error = rpc_method_wait_for_reply(g_rpc_connection, RPC_TYPE_UINT32, &ret, RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::HasProperty() wait for reply", error);
|
|
return false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool g_NPClass_HasProperty(NPObject *npobj, NPIdentifier name)
|
|
{
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return false;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::HasProperty not called from the main thread\n");
|
|
return false;
|
|
}
|
|
|
|
D(bugiI("NPClass::HasProperty(npobj %p, name id %p)\n", npobj, name));
|
|
bool ret = npclass_invoke_HasProperty(npobj, name);
|
|
D(bugiD("NPClass::HasProperty return: %d\n", ret));
|
|
return ret;
|
|
}
|
|
|
|
// NPClass::GetProperty
|
|
static int npclass_handle_GetProperty(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_GetProperty\n"));
|
|
|
|
NPObject *npobj;
|
|
NPIdentifier name;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_NP_OBJECT, &npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::GetProperty() get args", error);
|
|
return error;
|
|
}
|
|
|
|
uint32_t ret = false;
|
|
NPVariant result;
|
|
VOID_TO_NPVARIANT(result);
|
|
if (npobj && is_valid_npobject_class(npobj) && npobj->_class->getProperty) {
|
|
D(bugiI("NPClass::GetProperty(npobj %p, name id %p)\n", npobj, name));
|
|
ret = npobj->_class->getProperty(npobj, name, &result);
|
|
gchar *result_str = string_of_NPVariant(&result);
|
|
D(bugiD("NPClass::GetProperty return: %d (%s)\n", ret, result_str));
|
|
g_free(result_str);
|
|
}
|
|
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
|
|
return rpc_method_send_reply(connection,
|
|
RPC_TYPE_UINT32, ret,
|
|
RPC_TYPE_NP_VARIANT_PASS_REF, &result,
|
|
RPC_TYPE_INVALID);
|
|
}
|
|
|
|
static bool npclass_invoke_GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result)
|
|
{
|
|
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), false);
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_GET_PROPERTY,
|
|
RPC_TYPE_NP_OBJECT, npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::GetProperty() invoke", error);
|
|
return false;
|
|
}
|
|
|
|
uint32_t ret;
|
|
error = rpc_method_wait_for_reply(g_rpc_connection,
|
|
RPC_TYPE_UINT32, &ret,
|
|
RPC_TYPE_NP_VARIANT_PASS_REF, result,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::GetProperty() wait for reply", error);
|
|
return false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool g_NPClass_GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result)
|
|
{
|
|
if (result == NULL)
|
|
return false;
|
|
VOID_TO_NPVARIANT(*result);
|
|
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return false;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::GetProperty not called from the main thread\n");
|
|
return false;
|
|
}
|
|
|
|
D(bugiI("NPClass::GetProperty(npobj %p, name id %p)\n", npobj, name));
|
|
bool ret = npclass_invoke_GetProperty(npobj, name, result);
|
|
gchar *result_str = string_of_NPVariant(result);
|
|
D(bugiD("NPClass::GetProperty return: %d (%s)\n", ret, result_str));
|
|
g_free(result_str);
|
|
return ret;
|
|
}
|
|
|
|
// NPClass::SetProperty
|
|
static int npclass_handle_SetProperty(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_SetProperty\n"));
|
|
|
|
NPObject *npobj;
|
|
NPIdentifier name;
|
|
NPVariant value;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_NP_OBJECT, &npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_NP_VARIANT, &value,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::SetProperty() get args", error);
|
|
return error;
|
|
}
|
|
|
|
uint32_t ret = false;
|
|
if (npobj && is_valid_npobject_class(npobj) && npobj->_class->setProperty) {
|
|
D(bugiI("NPClass::SetProperty(npobj %p, name id %p)\n", npobj, name));
|
|
ret = npobj->_class->setProperty(npobj, name, &value);
|
|
D(bugiD("NPClass::SetProperty return: %d\n", ret));
|
|
}
|
|
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
NPN_ReleaseVariantValue(&value);
|
|
|
|
return rpc_method_send_reply(connection,
|
|
RPC_TYPE_UINT32, ret,
|
|
RPC_TYPE_INVALID);
|
|
}
|
|
|
|
static bool npclass_invoke_SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value)
|
|
{
|
|
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), false);
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_SET_PROPERTY,
|
|
RPC_TYPE_NP_OBJECT, npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_NP_VARIANT, value,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::SetProperty() invoke", error);
|
|
return false;
|
|
}
|
|
|
|
uint32_t ret;
|
|
error = rpc_method_wait_for_reply(g_rpc_connection,
|
|
RPC_TYPE_UINT32, &ret,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::SetProperty() wait for reply", error);
|
|
return false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool g_NPClass_SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value)
|
|
{
|
|
if (value == NULL) {
|
|
npw_printf("WARNING: NPClass::SetProperty() called with a NULL value\n");
|
|
return false;
|
|
}
|
|
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return false;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::SetProperty not called from the main thread\n");
|
|
return false;
|
|
}
|
|
|
|
D(bugiI("NPClass::SetProperty(npobj %p, name id %p)\n", npobj, name));
|
|
bool ret = npclass_invoke_SetProperty(npobj, name, value);
|
|
D(bugiD("NPClass::SetProperty return: %d\n", ret));
|
|
return ret;
|
|
}
|
|
|
|
// NPClass::RemoveProperty
|
|
static int npclass_handle_RemoveProperty(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_RemoveProperty\n"));
|
|
|
|
NPObject *npobj;
|
|
NPIdentifier name;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_NP_OBJECT, &npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::RemoveProperty() get args", error);
|
|
return error;
|
|
}
|
|
|
|
uint32_t ret = false;
|
|
if (npobj && is_valid_npobject_class(npobj) && npobj->_class->removeProperty) {
|
|
D(bugiI("NPClass::RemoveProperty(npobj %p, name id %p)\n", npobj, name));
|
|
ret = npobj->_class->removeProperty(npobj, name);
|
|
D(bugiD("NPClass::RemoveProperty return: %d\n", ret));
|
|
}
|
|
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
|
|
return rpc_method_send_reply(connection,
|
|
RPC_TYPE_UINT32, ret,
|
|
RPC_TYPE_INVALID);
|
|
}
|
|
|
|
static bool npclass_invoke_RemoveProperty(NPObject *npobj, NPIdentifier name)
|
|
{
|
|
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), false);
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_REMOVE_PROPERTY,
|
|
RPC_TYPE_NP_OBJECT, npobj,
|
|
RPC_TYPE_NP_IDENTIFIER, &name,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::RemoveProperty() invoke", error);
|
|
return false;
|
|
}
|
|
|
|
uint32_t ret;
|
|
error = rpc_method_wait_for_reply(g_rpc_connection, RPC_TYPE_UINT32, &ret, RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::RemoveProperty() wait for reply", error);
|
|
return false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool g_NPClass_RemoveProperty(NPObject *npobj, NPIdentifier name)
|
|
{
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return false;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::RemoveProperty not called from the main thread\n");
|
|
return false;
|
|
}
|
|
|
|
D(bugiI("NPClass::RemoveProperty(npobj %p, name id %p)\n", npobj, name));
|
|
bool ret = npclass_invoke_RemoveProperty(npobj, name);
|
|
D(bugiD("NPClass::RemoveProperty return: %d\n", ret));
|
|
return ret;
|
|
}
|
|
|
|
// NPClass::Enumerate
|
|
static int npclass_handle_Enumerate(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_Enumerate\n"));
|
|
|
|
NPObject *npobj;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_NP_OBJECT, &npobj,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Enumerate() get args", error);
|
|
return error;
|
|
}
|
|
|
|
uint32_t ret = false;
|
|
NPIdentifier *idents = NULL;
|
|
uint32_t argCount = 0;
|
|
if (npobj && is_valid_npobject_class(npobj) &&
|
|
NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) &&
|
|
npobj->_class->enumerate) {
|
|
D(bugiI("NPClass::Enumerate(npobj %p)\n", npobj));
|
|
ret = npobj->_class->enumerate(npobj, &idents, &argCount);
|
|
// TODO: Print the identifiers to debug
|
|
D(bugiD("NPClass::Enumerate return: %d (%d identifiers)\n", ret, argCount));
|
|
}
|
|
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
|
|
int rpc_ret = rpc_method_send_reply(connection,
|
|
RPC_TYPE_UINT32, ret,
|
|
RPC_TYPE_ARRAY, RPC_TYPE_NP_IDENTIFIER, argCount, idents,
|
|
RPC_TYPE_INVALID);
|
|
if (idents) NPN_MemFree(idents);
|
|
|
|
return rpc_ret;
|
|
}
|
|
|
|
static bool npclass_invoke_Enumerate(NPObject *npobj,
|
|
NPIdentifier **idents, uint32_t *count)
|
|
{
|
|
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), false);
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_ENUMERATE,
|
|
RPC_TYPE_NP_OBJECT, npobj,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Enumerate() invoke", error);
|
|
return false;
|
|
}
|
|
|
|
uint32_t ret;
|
|
uint32_t myCount = 0;
|
|
NPIdentifier *myIdentifiers = NULL;
|
|
error = rpc_method_wait_for_reply(g_rpc_connection,
|
|
RPC_TYPE_UINT32, &ret,
|
|
RPC_TYPE_ARRAY, RPC_TYPE_NP_IDENTIFIER, &myCount, &myIdentifiers,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Enumerate() wait for reply", error);
|
|
return false;
|
|
}
|
|
|
|
*count = myCount;
|
|
if (ret) {
|
|
ret = NPW_ReallocData(myIdentifiers,
|
|
sizeof(**idents) * myCount,
|
|
(void**)idents) == NPERR_NO_ERROR;
|
|
}
|
|
if (myIdentifiers)
|
|
free(myIdentifiers);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool g_NPClass_Enumerate(NPObject *npobj,
|
|
NPIdentifier **idents, uint32_t *count)
|
|
{
|
|
if (count == NULL || idents == NULL)
|
|
return false;
|
|
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return false;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::Enumerate not called from the main thread\n");
|
|
return false;
|
|
}
|
|
|
|
D(bugiI("NPClass::Enumerate(npobj %p)\n", npobj));
|
|
bool ret = npclass_invoke_Enumerate(npobj, idents, count);
|
|
// TODO: print the identifiers to debug
|
|
D(bugiD("NPClass::Enumerate return: %d (%d)\n", ret, *count));
|
|
return ret;
|
|
}
|
|
|
|
// NPClass::Construct
|
|
static int npclass_handle_Construct(rpc_connection_t *connection)
|
|
{
|
|
D(bug("npclass_handle_Construct\n"));
|
|
|
|
NPObject *npobj;
|
|
uint32_t argCount;
|
|
NPVariant *args;
|
|
int error = rpc_method_get_args(connection,
|
|
RPC_TYPE_NP_OBJECT, &npobj,
|
|
RPC_TYPE_ARRAY, RPC_TYPE_NP_VARIANT, &argCount, &args,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Construct() get args", error);
|
|
return error;
|
|
}
|
|
|
|
uint32_t ret = false;
|
|
NPVariant result;
|
|
VOID_TO_NPVARIANT(result);
|
|
if (npobj && is_valid_npobject_class(npobj) && npobj->_class->construct) {
|
|
D(bugiI("NPClass::Construct(npobj %p)\n", npobj));
|
|
print_npvariant_args(args, argCount);
|
|
ret = npobj->_class->construct(npobj, args, argCount, &result);
|
|
gchar *result_str = string_of_NPVariant(&result);
|
|
D(bugiD("NPClass::Construct return: %d (%s)\n", ret, result_str));
|
|
g_free(result_str);
|
|
}
|
|
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
if (args) {
|
|
for (int i = 0; i < argCount; i++)
|
|
NPN_ReleaseVariantValue(&args[i]);
|
|
free(args);
|
|
}
|
|
|
|
return rpc_method_send_reply(connection,
|
|
RPC_TYPE_UINT32, ret,
|
|
RPC_TYPE_NP_VARIANT_PASS_REF, &result,
|
|
RPC_TYPE_INVALID);
|
|
}
|
|
|
|
static bool npclass_invoke_Construct(NPObject *npobj, const NPVariant *args, uint32_t argCount,
|
|
NPVariant *result)
|
|
{
|
|
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), false);
|
|
|
|
int error = rpc_method_invoke(g_rpc_connection,
|
|
RPC_METHOD_NPCLASS_CONSTRUCT,
|
|
RPC_TYPE_NP_OBJECT, npobj,
|
|
RPC_TYPE_ARRAY, RPC_TYPE_NP_VARIANT, argCount, args,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Construct() invoke", error);
|
|
return false;
|
|
}
|
|
|
|
uint32_t ret;
|
|
error = rpc_method_wait_for_reply(g_rpc_connection,
|
|
RPC_TYPE_UINT32, &ret,
|
|
RPC_TYPE_NP_VARIANT_PASS_REF, result,
|
|
RPC_TYPE_INVALID);
|
|
|
|
if (error != RPC_ERROR_NO_ERROR) {
|
|
npw_perror("NPClass::Construct() wait for reply", error);
|
|
return false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool g_NPClass_Construct(NPObject *npobj, const NPVariant *args, uint32_t argCount,
|
|
NPVariant *result)
|
|
{
|
|
if (result == NULL)
|
|
return false;
|
|
VOID_TO_NPVARIANT(*result);
|
|
|
|
if (!is_valid_npobject_proxy(npobj))
|
|
return false;
|
|
|
|
if (!thread_check()) {
|
|
npw_printf("WARNING: NPClass::Construct not called from the main thread\n");
|
|
return false;
|
|
}
|
|
|
|
D(bugiI("NPClass::Construct(npobj %p)\n", npobj));
|
|
print_npvariant_args(args, argCount);
|
|
bool ret = npclass_invoke_Construct(npobj, args, argCount, result);
|
|
gchar *result_str = string_of_NPVariant(result);
|
|
D(bugiD("NPClass::Construct return: %d (%s)\n", ret, result_str));
|
|
g_free(result_str);
|
|
return ret;
|
|
}
|
|
|
|
int npclass_add_method_descriptors(rpc_connection_t *connection)
|
|
{
|
|
static const rpc_method_descriptor_t vtable[] = {
|
|
{ RPC_METHOD_NPCLASS_HAS_METHOD, npclass_handle_HasMethod },
|
|
{ RPC_METHOD_NPCLASS_INVOKE, npclass_handle_Invoke },
|
|
{ RPC_METHOD_NPCLASS_INVOKE_DEFAULT, npclass_handle_InvokeDefault },
|
|
{ RPC_METHOD_NPCLASS_HAS_PROPERTY, npclass_handle_HasProperty },
|
|
{ RPC_METHOD_NPCLASS_GET_PROPERTY, npclass_handle_GetProperty },
|
|
{ RPC_METHOD_NPCLASS_SET_PROPERTY, npclass_handle_SetProperty },
|
|
{ RPC_METHOD_NPCLASS_REMOVE_PROPERTY, npclass_handle_RemoveProperty },
|
|
{ RPC_METHOD_NPCLASS_ENUMERATE, npclass_handle_Enumerate },
|
|
{ RPC_METHOD_NPCLASS_CONSTRUCT, npclass_handle_Construct },
|
|
{ RPC_METHOD_NPCLASS_DEALLOCATE, npclass_handle_Deallocate },
|
|
};
|
|
return rpc_connection_add_method_descriptors(g_rpc_connection, vtable,
|
|
sizeof(vtable) / sizeof(vtable[0]));
|
|
}
|
|
|
|
#if NPW_IS_PLUGIN
|
|
/* ====================================================================== */
|
|
/* === NPObject Registration === */
|
|
/* ====================================================================== */
|
|
|
|
static GHashTable *g_npobj_owners = NULL;
|
|
|
|
void npobject_register(NPObject *npobj, void *plugin)
|
|
{
|
|
assert(!npobject_is_registered(npobj));
|
|
g_hash_table_insert(g_npobj_owners, npobj, plugin);
|
|
}
|
|
|
|
bool npobject_is_registered(NPObject *npobj)
|
|
{
|
|
return g_hash_table_lookup_extended(g_npobj_owners, npobj, NULL, NULL);
|
|
}
|
|
|
|
void *npobject_get_owner(NPObject *npobj)
|
|
{
|
|
return g_hash_table_lookup(g_npobj_owners, npobj);
|
|
}
|
|
|
|
void npobject_unregister(NPObject *npobj)
|
|
{
|
|
g_hash_table_remove(g_npobj_owners, npobj);
|
|
}
|
|
#endif
|
|
|
|
/* ====================================================================== */
|
|
/* === NPObject Bridge System === */
|
|
/* ====================================================================== */
|
|
|
|
bool npobject_bridge_new(void)
|
|
{
|
|
g_stubs = g_hash_table_new(NULL, NULL);
|
|
g_proxies = g_hash_table_new(NULL, NULL);
|
|
#if NPW_IS_PLUGIN
|
|
g_npobj_owners = g_hash_table_new(NULL, NULL);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
void npobject_bridge_destroy(void)
|
|
{
|
|
if (g_stubs) {
|
|
g_hash_table_destroy(g_stubs);
|
|
g_stubs = NULL;
|
|
}
|
|
if (g_proxies) {
|
|
g_hash_table_destroy(g_proxies);
|
|
g_proxies = NULL;
|
|
}
|
|
#if NPW_IS_PLUGIN
|
|
if (g_npobj_owners) {
|
|
g_hash_table_destroy(g_npobj_owners);
|
|
g_npobj_owners = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if NPW_IS_BROWSER
|
|
static void proxy_deactivate_func(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
NPObjectProxy *proxy = (NPObjectProxy *)value;
|
|
proxy->is_valid = false;
|
|
// No need to release the stub. At this point the viewer is dead.
|
|
}
|
|
|
|
static void stub_destroy_func(gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
NPObjectStub *stub = (NPObjectStub *)value;
|
|
npobject_destroy_stub_obj(stub);
|
|
}
|
|
|
|
void npruntime_deactivate(void)
|
|
{
|
|
// Invalidate any proxies the wrapper may still be holding on to.
|
|
g_hash_table_foreach(g_proxies, proxy_deactivate_func, NULL);
|
|
g_hash_table_foreach(g_stubs, stub_destroy_func, NULL);
|
|
// Reset both tables.
|
|
npobject_bridge_destroy();
|
|
npobject_bridge_new();
|
|
}
|
|
#endif
|
|
|
|
|
|
/* ====================================================================== */
|
|
/* === NPVariant helpers === */
|
|
/* ====================================================================== */
|
|
|
|
void
|
|
npvariant_clear(NPVariant *variant)
|
|
{
|
|
switch (variant->type) {
|
|
case NPVariantType_Void:
|
|
case NPVariantType_Null:
|
|
case NPVariantType_Bool:
|
|
case NPVariantType_Int32:
|
|
case NPVariantType_Double:
|
|
break;
|
|
case NPVariantType_String:
|
|
{
|
|
NPString *s = &NPVARIANT_TO_STRING(*variant);
|
|
if (s->UTF8Characters)
|
|
NPN_MemFree((void *)s->UTF8Characters);
|
|
break;
|
|
}
|
|
case NPVariantType_Object:
|
|
{
|
|
NPObject *npobj = NPVARIANT_TO_OBJECT(*variant);
|
|
if (npobj)
|
|
NPN_ReleaseObject(npobj);
|
|
break;
|
|
}
|
|
}
|
|
VOID_TO_NPVARIANT(*variant);
|
|
}
|
|
|
|
// Make sure to deallocate with g_free() since it comes from a GString
|
|
gchar *
|
|
string_of_NPVariant(const NPVariant *arg)
|
|
{
|
|
#if DEBUG
|
|
if (arg == NULL)
|
|
return NULL;
|
|
GString *str = g_string_new(NULL);
|
|
switch (arg->type)
|
|
{
|
|
case NPVariantType_Void:
|
|
g_string_append_printf(str, "void");
|
|
break;
|
|
case NPVariantType_Null:
|
|
g_string_append_printf(str, "null");
|
|
break;
|
|
case NPVariantType_Bool:
|
|
g_string_append(str, arg->value.boolValue ? "true" : "false");
|
|
break;
|
|
case NPVariantType_Int32:
|
|
g_string_append_printf(str, "%d", arg->value.intValue);
|
|
break;
|
|
case NPVariantType_Double:
|
|
g_string_append_printf(str, "%f", arg->value.doubleValue);
|
|
break;
|
|
case NPVariantType_String:
|
|
g_string_append_c(str, '\'');
|
|
g_string_append_len(str,
|
|
arg->value.stringValue.UTF8Characters,
|
|
arg->value.stringValue.UTF8Length);
|
|
g_string_append_c(str, '\'');
|
|
break;
|
|
case NPVariantType_Object:
|
|
g_string_append_printf(str, "<object %p>", arg->value.objectValue);
|
|
break;
|
|
default:
|
|
g_string_append_printf(str, "<invalid type %d>", arg->type);
|
|
break;
|
|
}
|
|
return g_string_free(str, FALSE);
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
print_npvariant_args(const NPVariant *args, uint32_t nargs)
|
|
{
|
|
#if DEBUG
|
|
GString *str = g_string_new(NULL);
|
|
for (int i = 0; i < nargs; i++) {
|
|
if (i > 0)
|
|
g_string_append(str, ", ");
|
|
gchar *argstr = string_of_NPVariant(&args[i]);
|
|
g_string_append(str, argstr);
|
|
g_free(argstr);
|
|
}
|
|
D(bug("%u args (%s)\n", nargs, str->str));
|
|
g_string_free(str, TRUE);
|
|
#endif
|
|
}
|