ewk_js.cpp   [plain text]


/*
    Copyright (C) 2011 ProFUSION embedded systems

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include "config.h"
#include "ewk_js.h"

#if ENABLE(NETSCAPE_PLUGIN_API)

#include "NP_jsobject.h"
#include "ewk_logging.h"
#include "ewk_private.h"
#include "npruntime.h"
#include "npruntime_impl.h"
#include <string.h>

#define EINA_MAGIC_CHECK_OR_RETURN(o, ...) \
    if (!EINA_MAGIC_CHECK(o, EWK_JS_OBJECT_MAGIC)) { \
        EINA_MAGIC_FAIL(o, EWK_JS_OBJECT_MAGIC); \
        return __VA_ARGS__; \
    }

struct _Ewk_JS_Class {
    const Ewk_JS_Class_Meta* meta;
    Eina_Hash* methods; // Key=NPIdentifier(name), value=pointer to meta->methods.
    Eina_Hash* properties; // Key=NPIdentifier(name), value=pointer to meta->properties.
    Ewk_JS_Default default_prop;
};

static Eina_Bool ewk_js_npvariant_to_variant(Ewk_JS_Variant* data, const NPVariant* result);

static Eina_Bool ewk_js_variant_to_npvariant(const Ewk_JS_Variant* data, NPVariant* result)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(data, false);
    EINA_SAFETY_ON_NULL_RETURN_VAL(result, false);
    char* string_value;

    switch (data->type) {
    case EWK_JS_VARIANT_VOID:
        VOID_TO_NPVARIANT(*result);
        break;
    case EWK_JS_VARIANT_NULL:
        NULL_TO_NPVARIANT(*result);
        break;
    case EWK_JS_VARIANT_INT32:
        INT32_TO_NPVARIANT(data->value.i, *result);
        break;
    case EWK_JS_VARIANT_DOUBLE:
        DOUBLE_TO_NPVARIANT(data->value.d, *result);
        break;
    case EWK_JS_VARIANT_STRING:
        string_value = strdup(data->value.s);
        if (string_value)
            STRINGZ_TO_NPVARIANT(string_value, *result);
        else
            return false;
        break;
    case EWK_JS_VARIANT_BOOL:
        BOOLEAN_TO_NPVARIANT(data->value.b, *result);
        break;
    case EWK_JS_VARIANT_OBJECT:
        OBJECT_TO_NPVARIANT(reinterpret_cast<NPObject*>(data->value.o), *result);
        break;
    default:
        return false;
    }

    return true;
}

// These methods are used by NPAI, thats the reason to use bool instead of Eina_Bool.
static bool ewk_js_property_has(NPObject* npObject, NPIdentifier name)
{
    Ewk_JS_Object* object = reinterpret_cast<Ewk_JS_Object*>(npObject);

    EINA_SAFETY_ON_NULL_RETURN_VAL(npObject, false);
    EINA_MAGIC_CHECK_OR_RETURN(object, false);

    if (!_NPN_IdentifierIsString(name)) {
        ERR("int NPIdentifier is not supported.");
        return false;
    }

    char* prop_name = _NPN_UTF8FromIdentifier(name);
    bool fail = eina_hash_find(object->properties, prop_name); // FIXME: should search methods too?
    free(prop_name);

    return fail;
}

static bool ewk_js_property_get(NPObject* npObject, NPIdentifier name, NPVariant* result)
{
    Ewk_JS_Object* object = reinterpret_cast<Ewk_JS_Object*>(npObject);
    Ewk_JS_Variant* value;
    Ewk_JS_Property* prop;
    bool fail = false;

    EINA_SAFETY_ON_NULL_RETURN_VAL(npObject, false);
    EINA_MAGIC_CHECK_OR_RETURN(object, false);

    if (!_NPN_IdentifierIsString(name)) {
        ERR("int NPIdentifier is not supported.");
        return false;
    }

    value = static_cast<Ewk_JS_Variant*>(malloc(sizeof(Ewk_JS_Variant)));
    if (!value) {
        ERR("Could not allocate memory for ewk_js_variant");
        return false;
    }

    prop = static_cast<Ewk_JS_Property*>(eina_hash_find(object->cls->properties, name));
    if (prop && prop->get) { // Class has property and property has getter.
        fail = prop->get(object, prop->name, value);
        if (!fail)
            fail = ewk_js_variant_to_npvariant(value, result);
    } else if (object->cls->default_prop.get) { // Default getter exists.
        fail = object->cls->default_prop.get(object, prop->name, value);
        if (!fail)
            fail = ewk_js_variant_to_npvariant(value, result);
    } else { // Fallback to objects hash map.
        char* prop_name = _NPN_UTF8FromIdentifier(name);
        free(value);
        value = static_cast<Ewk_JS_Variant*>(eina_hash_find(object->properties, prop_name));
        free(prop_name);
        if (value)
            return ewk_js_variant_to_npvariant(value, result);
    }

    free(value);
    return fail;
}

static bool ewk_js_property_set(NPObject* npObject, NPIdentifier name, const NPVariant* npValue)
{
    Ewk_JS_Object* object = reinterpret_cast<Ewk_JS_Object*>(npObject);
    Ewk_JS_Variant* value;
    Ewk_JS_Property* prop;
    bool fail = false;

    EINA_SAFETY_ON_NULL_RETURN_VAL(npObject, false);
    EINA_MAGIC_CHECK_OR_RETURN(object, false);

    if (!_NPN_IdentifierIsString(name)) {
        ERR("int NPIdentifier is not supported.");
        return fail;
    }

    value = static_cast<Ewk_JS_Variant*>(malloc(sizeof(Ewk_JS_Variant)));
    if (!value) {
        ERR("Could not allocate memory for ewk_js_variant");
        return false;
    }

    ewk_js_npvariant_to_variant(value, npValue);
    char* prop_name = _NPN_UTF8FromIdentifier(name);
    prop = static_cast<Ewk_JS_Property*>(eina_hash_find(object->cls->properties, prop_name));
    if (prop && prop->set)
        fail = prop->set(object, prop->name, value); // Class has property and property has setter.
    else if (object->cls->default_prop.set)
        fail = object->cls->default_prop.set(object, prop_name, value); // Default getter exists.
    else { // Fallback to objects hash map.
        void* old = eina_hash_set(object->properties, prop_name, value);
        free(old);
        fail = true;
    }

    free(prop_name);
    return fail;
}

static bool ewk_js_property_remove(NPObject* npObject, NPIdentifier name)
{
    Ewk_JS_Object* object = reinterpret_cast<Ewk_JS_Object*>(npObject);
    Ewk_JS_Property* prop;
    bool fail = false;

    EINA_SAFETY_ON_NULL_RETURN_VAL(npObject, false);
    EINA_MAGIC_CHECK_OR_RETURN(object, false);

    if (!_NPN_IdentifierIsString(name)) {
        ERR("int NPIdentifier is not supported.");
        return fail;
    }

    char* prop_name = _NPN_UTF8FromIdentifier(name);
    prop = static_cast<Ewk_JS_Property*>(eina_hash_find(object->cls->properties, prop_name));
    if (prop && prop->del)
        fail = prop->del(object, prop->name); // Class has property and property has getter.
    else if (object->cls->default_prop.del)
        fail = object->cls->default_prop.del(object, prop_name);
    else
        fail = eina_hash_del(object->properties, prop_name, 0);

    free(prop_name);
    return fail;
}

static bool ewk_js_properties_enumerate(NPObject* npObject, NPIdentifier** value, uint32_t* count)
{
    Ewk_JS_Object* object = reinterpret_cast<Ewk_JS_Object*>(npObject);
    Eina_Iterator* it;
    char* key;
    int i = 0;

    EINA_SAFETY_ON_NULL_RETURN_VAL(npObject, false);
    EINA_MAGIC_CHECK_OR_RETURN(object, false);

    *count = eina_hash_population(object->properties);
    *value = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier) * *count));
    if (!*value) {
        ERR("Could not allocate memory for NPIdentifier");
        return false;
    }

    it = eina_hash_iterator_key_new(object->properties);
    EINA_ITERATOR_FOREACH(it, key)
        (*value)[i++] = _NPN_GetStringIdentifier(key);

    eina_iterator_free(it);
    return true;
}

static bool ewk_js_method_has(NPObject* npObject, NPIdentifier name)
{
    Ewk_JS_Object* object = reinterpret_cast<Ewk_JS_Object*>(npObject);

    EINA_SAFETY_ON_NULL_RETURN_VAL(npObject, false);
    EINA_MAGIC_CHECK_OR_RETURN(object, false);

    if (!_NPN_IdentifierIsString(name)) {
        ERR("int NPIdentifier is not supported.");
        return false;
    }
    return eina_hash_find(object->cls->methods, name); // Returns pointer if found(true), 0(false) otherwise.
}

static bool ewk_js_method_invoke(NPObject* npObject, NPIdentifier name, const NPVariant* npArgs, uint32_t npArgCount, NPVariant* result)
{
    Ewk_JS_Object* object = reinterpret_cast<Ewk_JS_Object*>(npObject);
    Ewk_JS_Method* method;
    Ewk_JS_Variant* args;
    Ewk_JS_Variant* ret_val;

    EINA_SAFETY_ON_NULL_RETURN_VAL(npObject, false);
    EINA_MAGIC_CHECK_OR_RETURN(object, false);

    if (!_NPN_IdentifierIsString(name)) {
        ERR("int NPIdentifier is not supported.");
        return false;
    }

    method = static_cast<Ewk_JS_Method*>(eina_hash_find(object->cls->methods, name));
    if (!method)
        return false;

    args = static_cast<Ewk_JS_Variant*>(malloc(sizeof(Ewk_JS_Variant)  *npArgCount));
    if (!args) {
        ERR("Could not allocate memory for ewk_js_variant");
        return false;
    }

    for (uint32_t i = 0; i < npArgCount; i++)
        ewk_js_npvariant_to_variant(&args[i], &npArgs[i]);
    ret_val = method->invoke(object, args, npArgCount);
    ewk_js_variant_to_npvariant(ret_val, result);

    ewk_js_variant_free(ret_val);

    return true;
}

static Eina_Bool ewk_js_npobject_property_get(Ewk_JS_Object* jsObject, const char* name, Ewk_JS_Variant* value)
{
    NPIdentifier id = _NPN_GetStringIdentifier(name);
    NPVariant var;
    bool fail = _NPN_GetProperty(0, reinterpret_cast<NPObject*>(jsObject), id, &var);
    if (!fail)
        fail = ewk_js_npvariant_to_variant(value, &var);
    return fail;
}

static Eina_Bool ewk_js_npobject_property_set(Ewk_JS_Object* jsObject, const char* name, const Ewk_JS_Variant* value)
{
    NPIdentifier id = _NPN_GetStringIdentifier(name);
    NPVariant var;
    bool fail = ewk_js_variant_to_npvariant(value, &var);
    if (fail)
        fail = _NPN_SetProperty(0, reinterpret_cast<NPObject*>(jsObject), id, &var);
    return fail;
}

static Eina_Bool ewk_js_npobject_property_del(Ewk_JS_Object* jsObject, const char* name)
{
    NPIdentifier id = _NPN_GetStringIdentifier(name);
    return _NPN_RemoveProperty(0, reinterpret_cast<NPObject*>(jsObject), id);
}

static void ewk_js_property_free(Ewk_JS_Property* prop)
{
    free(const_cast<char*>(prop->name));
    if (prop->value.type == EWK_JS_VARIANT_STRING)
        free(prop->value.value.s);
    else if (prop->value.type == EWK_JS_VARIANT_OBJECT)
        ewk_js_object_free(prop->value.value.o);
    free(prop);
}

/**
 * Create a Ewk_JS_Class to be used in @a ewk_js_object_new.
 *
 * @param meta @a Ewk_JS_Class_Meta that describes the class to be created.
 *
 * @return The Ewk_JS_Class created.
 */
Ewk_JS_Class* ewk_js_class_new(const Ewk_JS_Class_Meta* jsMetaClass)
{
    Ewk_JS_Class* cls;

    EINA_SAFETY_ON_NULL_RETURN_VAL(jsMetaClass, 0);

    cls = static_cast<Ewk_JS_Class*>(malloc(sizeof(Ewk_JS_Class)));
    if (!cls) {
        ERR("Could not allocate memory for ewk_js_class");
        return 0;
    }

    cls->meta = jsMetaClass;
    cls->default_prop = cls->meta->default_prop;

    // Don't free methods since they point to meta class methods(will be freed when meta class is freed).
    cls->methods = eina_hash_pointer_new(0);
    for (int i = 0; cls->meta->methods && cls->meta->methods[i].name; i++) {
        NPIdentifier id = _NPN_GetStringIdentifier(cls->meta->methods[i].name);
        eina_hash_add(cls->methods, id, &cls->meta->methods[i]);
    }

    // Don't free properties since they point to cls->meta class properties(will be freed when cls->meta class is freed).
    cls->properties = eina_hash_pointer_new(0);
    for (int i = 0; cls->meta->properties && cls->meta->properties[i].name; i++) {
        NPIdentifier id = _NPN_GetStringIdentifier(cls->meta->properties[i].name);
        eina_hash_add(cls->properties, id, &cls->meta->properties[i]);
    }

    return cls;
}

/**
 * Release resources allocated by @a cls.
 *
 * @param cls @a Ewk_JS_Class to be released.
 */
void ewk_js_class_free(Ewk_JS_Class* jsClass)
{
    EINA_SAFETY_ON_NULL_RETURN(jsClass);
    eina_hash_free(jsClass->methods);
    eina_hash_free(jsClass->properties);
    free(jsClass);
}

static NPClass EWK_NPCLASS = {
    NP_CLASS_STRUCT_VERSION,
    0, // NPAllocateFunctionPtr
    0, // NPDeallocateFunctionPtr
    0, // NPInvalidateFunctionPtr
    ewk_js_method_has, // NPHasMethodFunctionPtr
    ewk_js_method_invoke, // NPInvokeFunctionPtr
    0, // NPInvokeDefaultFunctionPtr
    ewk_js_property_has, // NPHasPropertyFunctionPtr
    ewk_js_property_get, // NPGetPropertyFunctionPtr
    ewk_js_property_set, // NPSetPropertyFunctionPtr
    ewk_js_property_remove, // NPRemovePropertyFunctionPtr
    ewk_js_properties_enumerate, // NPEnumerationFunctionPtr
    0 // NPConstructFunction
};

static Ewk_JS_Object* ewk_js_npobject_to_object(NPObject* npObject)
{
    NPIdentifier* values;
    uint32_t np_props_count;
    Ewk_JS_Class* cls;
    Ewk_JS_Object* object;
    Eina_Iterator* it;
    Ewk_JS_Property* prop;
    JavaScriptObject* jso;

    if (EINA_MAGIC_CHECK(reinterpret_cast<Ewk_JS_Object*>(npObject), EWK_JS_OBJECT_MAGIC))
        return reinterpret_cast<Ewk_JS_Object*>(npObject);

    if (!_NPN_Enumerate(0, npObject, &values, &np_props_count))
        return 0;

    cls = static_cast<Ewk_JS_Class*>(malloc(sizeof(Ewk_JS_Class)));
    if (!cls) {
        ERR("Could not allocate memory for ewk_js_class");
        return 0;
    }

    cls->meta = 0;
    Ewk_JS_Default def = {
        ewk_js_npobject_property_set,
        ewk_js_npobject_property_get,
        ewk_js_npobject_property_del
    };
    cls->default_prop = def;
    cls->methods = eina_hash_pointer_new(0);
    cls->properties = eina_hash_pointer_new(reinterpret_cast<Eina_Free_Cb>(ewk_js_property_free));
    for (uint32_t i = 0; i < np_props_count; i++) {
        if (_NPN_HasProperty(0, npObject, values[i])) {
            NPVariant var;
            Ewk_JS_Property* prop = static_cast<Ewk_JS_Property*>(calloc(sizeof(Ewk_JS_Property), 1));
            if (!prop) {
                ERR("Could not allocate memory for ewk_js_property");
                goto error;
            }

            _NPN_GetProperty(0, npObject, values[i], &var);
            ewk_js_npvariant_to_variant(&(prop->value), &var);
            prop->name = _NPN_UTF8FromIdentifier(values[i]);
            eina_hash_add(cls->properties, values[i], prop);
        }
    }

    // Can't use ewk_js_object_new(cls) because it expects cls->meta to exist.
    object = static_cast<Ewk_JS_Object*>(malloc(sizeof(Ewk_JS_Object)));
    if (!object) {
        ERR("Could not allocate memory for ewk_js_object");
        goto error;
    }

    free(values);
    EINA_MAGIC_SET(object, EWK_JS_OBJECT_MAGIC);
    object->name = 0;
    object->cls = cls;
    object->view = 0;

    jso = reinterpret_cast<JavaScriptObject*>(npObject);
    if (!strcmp("Array", jso->imp->methodTable()->className(jso->imp).ascii().data()))
        object->type = EWK_JS_OBJECT_ARRAY;
    else if (!strcmp("Function", jso->imp->methodTable()->className(jso->imp).ascii().data()))
        object->type = EWK_JS_OBJECT_FUNCTION;
    else
        object->type = EWK_JS_OBJECT_OBJECT;

    if (eina_hash_population(cls->properties) < 25)
        object->properties = eina_hash_string_small_new(0);
    else
        object->properties = eina_hash_string_superfast_new(0);

    it = eina_hash_iterator_data_new(cls->properties);
    EINA_ITERATOR_FOREACH(it, prop) {
        const char* key = prop->name;
        Ewk_JS_Variant* value = &prop->value;
        eina_hash_add(object->properties, key, value);
    }

    eina_iterator_free(it);
    object->base = *reinterpret_cast<JavaScriptObject*>(npObject);

    return object;

error:
    ewk_js_class_free(cls);
    free(values);
    return 0;
}

static Eina_Bool ewk_js_npvariant_to_variant(Ewk_JS_Variant* data, const NPVariant* result)
{
    int sz;
    EINA_SAFETY_ON_NULL_RETURN_VAL(data, false);
    EINA_SAFETY_ON_NULL_RETURN_VAL(result, false);
    switch (result->type) {
    case NPVariantType_Void:
        data->type = EWK_JS_VARIANT_VOID;
        data->value.o = 0;
        break;
    case NPVariantType_Null:
        data->type = EWK_JS_VARIANT_NULL;
        data->value.o = 0;
        break;
    case NPVariantType_Int32:
        data->type = EWK_JS_VARIANT_INT32;
        data->value.i = NPVARIANT_TO_INT32(*result);
        break;
    case NPVariantType_Double:
        data->type = EWK_JS_VARIANT_DOUBLE;
        data->value.d = NPVARIANT_TO_DOUBLE(*result);
        break;
    case NPVariantType_String:
        sz = NPVARIANT_TO_STRING(*result).UTF8Length;
        data->value.s = static_cast<char*>(malloc(sizeof(char) * (sz + 1)));
        if (!data->value.s)
            return false;
        memcpy(data->value.s, NPVARIANT_TO_STRING(*result).UTF8Characters, sz);
        data->value.s[sz] = '\0';
        data->type = EWK_JS_VARIANT_STRING;
        break;
    case NPVariantType_Bool:
        data->type = EWK_JS_VARIANT_BOOL;
        data->value.b = NPVARIANT_TO_BOOLEAN(*result);
        break;
    case NPVariantType_Object:
        data->type = EWK_JS_VARIANT_OBJECT;
        data->value.o = ewk_js_npobject_to_object(NPVARIANT_TO_OBJECT(*result));
        break;
    default:
        return false;
    }

    return true;
}
#endif // ENABLE(NETSCAPE_PLUGIN_API)

Ewk_JS_Object* ewk_js_object_new(const Ewk_JS_Class_Meta* jsMetaClass)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    Ewk_JS_Object* object;

    EINA_SAFETY_ON_NULL_RETURN_VAL(jsMetaClass, 0);

    object = static_cast<Ewk_JS_Object*>(malloc(sizeof(Ewk_JS_Object)));
    if (!object) {
        ERR("Could not allocate memory for ewk_js_object");
        return 0;
    }

    EINA_MAGIC_SET(object, EWK_JS_OBJECT_MAGIC);
    object->cls = ewk_js_class_new(jsMetaClass);
    object->view = 0;
    object->name = 0;
    object->type = EWK_JS_OBJECT_OBJECT;

    if (eina_hash_population(object->cls->properties) < 25)
        object->properties = eina_hash_string_small_new(reinterpret_cast<Eina_Free_Cb>(ewk_js_variant_free));
    else
        object->properties = eina_hash_string_superfast_new(reinterpret_cast<Eina_Free_Cb>(ewk_js_variant_free));

    for (int i = 0; object->cls->meta->properties && object->cls->meta->properties[i].name; i++) {
        Ewk_JS_Property prop = object->cls->meta->properties[i];
        const char* key = object->cls->meta->properties[i].name;
        Ewk_JS_Variant* value = static_cast<Ewk_JS_Variant*>(malloc(sizeof(Ewk_JS_Variant)));
        if (!value) {
            ERR("Could not allocate memory for ewk_js_variant");
            goto error;
        }
        if (prop.get)
            prop.get(object, key, value);
        else {
            value->type = prop.value.type;
            switch (value->type) {
            case EWK_JS_VARIANT_VOID:
            case EWK_JS_VARIANT_NULL:
                value->value.o = 0;
                break;
            case EWK_JS_VARIANT_STRING:
                value->value.s = strdup(prop.value.value.s);
                break;
            case EWK_JS_VARIANT_BOOL:
                value->value.b = prop.value.value.b;
                break;
            case EWK_JS_VARIANT_INT32:
                value->value.i = prop.value.value.i;
                break;
            case EWK_JS_VARIANT_DOUBLE:
                value->value.d = prop.value.value.d;
                break;
            case EWK_JS_VARIANT_OBJECT:
                value->value.o = prop.value.value.o;
                break;
            }
        }
        eina_hash_add(object->properties, key, value);
    }

    object->base.object.referenceCount = 1;
    object->base.object._class = &EWK_NPCLASS;
    return object;

error:
    ewk_js_object_free(object);
    return 0;
#else
    return 0;
#endif
}

void ewk_js_object_free(Ewk_JS_Object* jsObject)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    EINA_SAFETY_ON_NULL_RETURN(jsObject);
    EINA_MAGIC_CHECK_OR_RETURN(jsObject);
    Eina_Bool script_obj = !jsObject->cls->meta;

    eina_hash_free(jsObject->properties);
    eina_stringshare_del(jsObject->name);

    ewk_js_class_free(const_cast<Ewk_JS_Class*>(jsObject->cls));

    EINA_MAGIC_SET(jsObject, EINA_MAGIC_NONE);

    if (script_obj)
        free(jsObject);
#endif
}

Evas_Object* ewk_js_object_view_get(const Ewk_JS_Object* jsObject)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    EINA_SAFETY_ON_NULL_RETURN_VAL(jsObject, 0);
    EINA_MAGIC_CHECK_OR_RETURN(jsObject, 0);
    return jsObject->view;
#else
    return 0;
#endif
}

Eina_Hash* ewk_js_object_properties_get(const Ewk_JS_Object* jsObject)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    EINA_SAFETY_ON_NULL_RETURN_VAL(jsObject, 0);
    EINA_MAGIC_CHECK_OR_RETURN(jsObject, 0);
    return jsObject->properties;
#else
    return 0;
#endif
}

const char* ewk_js_object_name_get(const Ewk_JS_Object* jsObject)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    EINA_SAFETY_ON_NULL_RETURN_VAL(jsObject, 0);
    EINA_MAGIC_CHECK_OR_RETURN(jsObject, 0);
    return jsObject->name;
#else
    return 0;
#endif
}

Eina_Bool ewk_js_object_invoke(Ewk_JS_Object* jsObject, Ewk_JS_Variant* args, int argCount, Ewk_JS_Variant* result)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    NPVariant* np_args;
    NPVariant np_result;
    bool fail = false;

    EINA_MAGIC_CHECK_OR_RETURN(jsObject, false);
    if (ewk_js_object_type_get(jsObject) != EWK_JS_OBJECT_FUNCTION)
        return false;
    if (argCount)
        EINA_SAFETY_ON_NULL_RETURN_VAL(args, false);

    np_args = static_cast<NPVariant*>(malloc(sizeof(NPVariant)  *argCount));
    if (!np_args) {
        ERR("Could not allocate memory to method arguments");
        return false;
    }

    for (int i = 0; i < argCount; i++)
        if (!ewk_js_variant_to_npvariant(&args[i], &np_args[i]))
            goto end;

    if (!(fail = _NPN_InvokeDefault(0, reinterpret_cast<NPObject*>(jsObject), np_args, argCount, &np_result)))
        goto end;
    if (result)
        fail = ewk_js_npvariant_to_variant(result, &np_result);

end:
    free(np_args);
    return fail;
#else
    return false;
#endif
}

Ewk_JS_Object_Type ewk_js_object_type_get(Ewk_JS_Object* jsObject)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    EINA_SAFETY_ON_NULL_RETURN_VAL(jsObject, EWK_JS_OBJECT_OBJECT);
    EINA_MAGIC_CHECK_OR_RETURN(jsObject, EWK_JS_OBJECT_OBJECT);

    return jsObject->type;
#else
    return EWK_JS_OBJECT_INVALID;
#endif
}

void ewk_js_object_type_set(Ewk_JS_Object* jsObject, Ewk_JS_Object_Type type)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    EINA_SAFETY_ON_NULL_RETURN(jsObject);
    EINA_MAGIC_CHECK_OR_RETURN(jsObject);

    jsObject->type = type;
#endif
}

void ewk_js_variant_free(Ewk_JS_Variant* jsVariant)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    EINA_SAFETY_ON_NULL_RETURN(jsVariant);
    if (jsVariant->type == EWK_JS_VARIANT_STRING)
        free(jsVariant->value.s);
    else if (jsVariant->type == EWK_JS_VARIANT_OBJECT)
        ewk_js_object_free(jsVariant->value.o);
    free(jsVariant);
#endif
}

void ewk_js_variant_array_free(Ewk_JS_Variant* jsVariant, int count)
{
#if ENABLE(NETSCAPE_PLUGIN_API)
    EINA_SAFETY_ON_NULL_RETURN(jsVariant);
    for (int i = 0; i < count; i++) {
        if (jsVariant[i].type == EWK_JS_VARIANT_STRING)
            free(jsVariant[i].value.s);
        else if (jsVariant[i].type == EWK_JS_VARIANT_OBJECT)
            ewk_js_object_free(jsVariant[i].value.o);
    }
    free(jsVariant);
#endif
}