| #ifndef _H_NPVariantWrapper | |
| #define _H_NPVariantWrapper | |
| #include <string> | |
| #include <ostream> | |
| #ifdef sun | |
| // Sun's cstring doesn't define strlen/etc | |
| #include <string.h> | |
| #endif | |
| #include <cstring> | |
| #include <stdio.h> | |
| #include "Debug.h" | |
| #include "Platform.h" | |
| #include "mozincludes.h" | |
| #include "Value.h" | |
| #include "LocalObjectTable.h" | |
| #include "Plugin.h" | |
| #include "JavaObject.h" | |
| /** | |
| * Contains NPVariantProxy, NPVariantWrapper, and NPVariantArray | |
| */ | |
| /** | |
| * Wraps an NPVariant and provides various conversion functions. The variant | |
| * provided retained at create time. | |
| */ | |
| class NPVariantProxy { | |
| friend class NPVariantArray; | |
| private: | |
| ScriptableInstance& plugin; | |
| NPVariant& variant; | |
| public: | |
| NPVariantProxy(ScriptableInstance& plugin, NPVariant& variant) | |
| : plugin(plugin), variant(variant) | |
| { | |
| VOID_TO_NPVARIANT(variant); | |
| } | |
| NPVariantProxy(ScriptableInstance& plugin, NPVariant& variant, bool noinit) | |
| : plugin(plugin), variant(variant) | |
| { | |
| } | |
| ~NPVariantProxy() { | |
| } | |
| operator NPVariant() const { | |
| return variant; | |
| } | |
| const NPVariant* operator->() const { | |
| return &variant; | |
| } | |
| const NPVariant* address() const { | |
| return &variant; | |
| } | |
| int isInt() const { | |
| return isInt(variant); | |
| } | |
| static int isInt(const NPVariant& variant) { | |
| return NPVARIANT_IS_INT32(variant); | |
| } | |
| int getAsInt() const { | |
| return getAsInt(variant); | |
| } | |
| static int getAsInt(const NPVariant& variant) { | |
| if (NPVARIANT_IS_INT32(variant)) { | |
| return NPVARIANT_TO_INT32(variant); | |
| } | |
| Debug::log(Debug::Error) << "getAsInt: variant not int" << Debug::flush; | |
| return 0; | |
| } | |
| int isNull() const { | |
| return isNull(variant); | |
| } | |
| static int isNull(const NPVariant& variant) { | |
| return NPVARIANT_IS_NULL(variant); | |
| } | |
| int isObject() const { | |
| return isObject(variant); | |
| } | |
| static int isObject(const NPVariant& variant) { | |
| return NPVARIANT_IS_OBJECT(variant); | |
| } | |
| NPObject* getAsObject() const { | |
| return getAsObject(variant); | |
| } | |
| static NPObject* getAsObject(const NPVariant& variant) { | |
| if (NPVARIANT_IS_OBJECT(variant)) { | |
| return NPVARIANT_TO_OBJECT(variant); | |
| } | |
| Debug::log(Debug::Error) << "getAsObject: variant not object" << Debug::flush; | |
| return 0; | |
| } | |
| int isString() const { | |
| return isString(variant); | |
| } | |
| static int isString(const NPVariant& variant) { | |
| return NPVARIANT_IS_STRING(variant); | |
| } | |
| const NPString* getAsNPString() const { | |
| return getAsNPString(variant); | |
| } | |
| static const NPString* getAsNPString(const NPVariant& variant) { | |
| if (NPVARIANT_IS_STRING(variant)) { | |
| return &NPVARIANT_TO_STRING(variant); | |
| } | |
| Debug::log(Debug::Error) << "getAsNPString: variant not string" << Debug::flush; | |
| return 0; | |
| } | |
| Value getAsValue(ScriptableInstance& scriptInstance, bool unwrapJava = true) const { | |
| return getAsValue(variant, scriptInstance, unwrapJava); | |
| } | |
| static Value getAsValue(const NPVariant& variant, ScriptableInstance& scriptInstance, | |
| bool unwrapJava = true) { | |
| Value val; | |
| if (NPVARIANT_IS_VOID(variant)) { | |
| val.setUndefined(); | |
| } else if (NPVARIANT_IS_NULL(variant)) { | |
| val.setNull(); | |
| } else if (NPVARIANT_IS_BOOLEAN(variant)) { | |
| val.setBoolean(NPVARIANT_TO_BOOLEAN(variant)); | |
| } else if (NPVARIANT_IS_INT32(variant)) { | |
| val.setInt(NPVARIANT_TO_INT32(variant)); | |
| } else if (NPVARIANT_IS_DOUBLE(variant)) { | |
| val.setDouble(NPVARIANT_TO_DOUBLE(variant)); | |
| } else if (NPVARIANT_IS_STRING(variant)) { | |
| NPString str = NPVARIANT_TO_STRING(variant); | |
| val.setString(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str)); | |
| } else if (NPVARIANT_IS_OBJECT(variant)) { | |
| NPObject* obj = NPVARIANT_TO_OBJECT(variant); | |
| if (unwrapJava && JavaObject::isInstance(obj)) { | |
| JavaObject* jObj = static_cast<JavaObject*>(obj); | |
| val.setJavaObject(jObj->getObjectId()); | |
| } else { | |
| NPVariant result; | |
| VOID_TO_NPVARIANT(result); | |
| if (scriptInstance.tryGetStringPrimitive(obj, result)) { | |
| NPString str = NPVARIANT_TO_STRING(result); | |
| val.setString(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str)); | |
| release(result); | |
| } else { | |
| val.setJsObjectId(scriptInstance.getLocalObjectRef(obj)); | |
| } | |
| } | |
| } else { | |
| Debug::log(Debug::Error) << "Unsupported NPVariant type " << variant.type << Debug::flush; | |
| } | |
| return val; | |
| } | |
| /** | |
| * The incoming variant is not altered, and is not even required to have | |
| * its contents retained. Any object will get an extra refcount on it | |
| * when copied to this variant, and strings will be copied. | |
| */ | |
| NPVariantProxy& operator=(const NPVariant& newval) { | |
| assignFrom(variant, newval); | |
| return *this; | |
| } | |
| /** | |
| * The incoming variant is not altered, and is not even required to have | |
| * its contents retained. Any object will get an extra refcount on it | |
| * when copied to this variant, and strings will be copied. | |
| */ | |
| static void assignFrom(NPVariant& variant, const NPVariant& newval) { | |
| release(variant); | |
| variant = newval; | |
| if (NPVARIANT_IS_STRING(newval)) { | |
| int n = variant.value.stringValue.UTF8Length; | |
| char* strBytes = reinterpret_cast<char*>(NPN_MemAlloc(n)); | |
| memcpy(strBytes, variant.value.stringValue.UTF8Characters, n); | |
| variant.value.stringValue.UTF8Characters = strBytes; | |
| } else { | |
| retain(variant); | |
| } | |
| } | |
| NPVariantProxy& operator=(NPObject* obj) { | |
| assignFrom(variant, obj); | |
| return *this; | |
| } | |
| static void assignFrom(NPVariant& variant, NPObject* obj) { | |
| release(variant); | |
| OBJECT_TO_NPVARIANT(obj, variant); | |
| retain(variant); | |
| } | |
| // Convenience method for C++ code | |
| NPVariantProxy& operator=(int intVal) { | |
| assignFrom(variant, intVal); | |
| return *this; | |
| } | |
| // Convenience method for C++ code | |
| static void assignFrom(NPVariant& variant, int intVal) { | |
| NPVariant newvar; | |
| INT32_TO_NPVARIANT(intVal, newvar); | |
| assignFrom(variant, newvar); | |
| } | |
| // Convenience method for C++ code | |
| NPVariantProxy& operator=(const std::string& strval) { | |
| assignFrom(variant, strval); | |
| return *this; | |
| } | |
| // Convenience method for C++ code | |
| static void assignFrom(NPVariant& variant, const std::string& strval) { | |
| NPVariant newvar; | |
| STDSTRING_TO_NPVARIANT(strval, newvar); | |
| assignFrom(variant, newvar); | |
| } | |
| // Convenience method for C++ code | |
| NPVariantProxy& operator=(const char* strval) { | |
| assignFrom(variant, strval); | |
| return *this; | |
| } | |
| // Convenience method for C++ code | |
| static void assignFrom(NPVariant& variant, const char* strval) { | |
| NPVariant newvar; | |
| STRINGZ_TO_NPVARIANT(strval, newvar); | |
| assignFrom(variant, newvar); | |
| } | |
| NPVariantProxy& operator=(const Value& newval) { | |
| assignFrom(plugin, variant, newval); | |
| return *this; | |
| } | |
| static void assignFrom(ScriptableInstance& plugin, NPVariant& variant, const Value& newval) { | |
| NPVariant newvar; | |
| VOID_TO_NPVARIANT(newvar); | |
| if (newval.isBoolean()) { | |
| BOOLEAN_TO_NPVARIANT(newval.getBoolean(), newvar); | |
| } else if (newval.isByte()) { | |
| INT32_TO_NPVARIANT(newval.getByte(), newvar); | |
| } else if (newval.isChar()) { | |
| INT32_TO_NPVARIANT(newval.getChar(), newvar); | |
| } else if (newval.isShort()) { | |
| INT32_TO_NPVARIANT(newval.getShort(), newvar); | |
| } else if (newval.isInt()) { | |
| int value = newval.getInt(); | |
| // Firefox NPAPI bug: 32-bit ints get mapped to int jsvals, regardless of range. | |
| // However, int jsvals are 31 bits, so we need to use a double if the value is | |
| // not representable in a 31 bit signed 2's-complement value. | |
| if (value >= 0x40000000 || value < -0x40000000) { | |
| DOUBLE_TO_NPVARIANT(static_cast<double>(value), newvar); | |
| } else { | |
| INT32_TO_NPVARIANT(value, newvar); | |
| } | |
| } else if (newval.isFloat()) { | |
| DOUBLE_TO_NPVARIANT(newval.getFloat(), newvar); | |
| } else if (newval.isDouble()) { | |
| DOUBLE_TO_NPVARIANT(newval.getDouble(), newvar); | |
| } else if (newval.isNull()) { | |
| NULL_TO_NPVARIANT(newvar); | |
| } else if (newval.isUndefined()) { | |
| VOID_TO_NPVARIANT(newvar); | |
| } else if (newval.isString()) { | |
| assignFrom(variant, newval.getString()); | |
| return; | |
| } else if (newval.isJavaObject()) { | |
| if (1) { | |
| JavaObject* jObj = plugin.createJavaWrapper(newval.getJavaObjectId()); | |
| NPObject* obj = jObj; | |
| OBJECT_TO_NPVARIANT(obj, newvar); | |
| } else { | |
| VOID_TO_NPVARIANT(newvar); | |
| } | |
| } else if (newval.isJsObject()) { | |
| OBJECT_TO_NPVARIANT(plugin.getLocalObject(newval.getJsObjectId()), | |
| newvar); | |
| } else { | |
| Debug::log(Debug::Error) << "Unsupported NPVariant type " << newval.getType() << Debug::flush; | |
| } | |
| assignFrom(variant, newvar); | |
| } | |
| std::string toString() const { | |
| return toString(variant); | |
| } | |
| static std::string toString(const NPVariant& variant) { | |
| std::string retval; | |
| // TODO(jat): remove sprintfs | |
| char buf[40]; | |
| NPObject* npObj; | |
| switch (variant.type) { | |
| case NPVariantType_Void: | |
| retval = "undef"; | |
| break; | |
| case NPVariantType_Null: | |
| retval = "null"; | |
| break; | |
| case NPVariantType_Bool: | |
| retval = "bool("; | |
| retval += (NPVARIANT_TO_BOOLEAN(variant) ? "true" : "false"); | |
| retval += ')'; | |
| break; | |
| case NPVariantType_Int32: | |
| retval = "int("; | |
| snprintf(buf, sizeof(buf), "%d)", NPVARIANT_TO_INT32(variant)); | |
| retval += buf; | |
| break; | |
| case NPVariantType_Double: | |
| retval = "double("; | |
| snprintf(buf, sizeof(buf), "%g)", NPVARIANT_TO_DOUBLE(variant)); | |
| retval += buf; | |
| break; | |
| case NPVariantType_String: | |
| { | |
| retval = "string("; | |
| NPString str = NPVARIANT_TO_STRING(variant); | |
| retval += std::string(str.UTF8Characters, str.UTF8Length); | |
| retval += ')'; | |
| } | |
| break; | |
| case NPVariantType_Object: | |
| npObj = NPVARIANT_TO_OBJECT(variant); | |
| if (JavaObject::isInstance(npObj)) { | |
| JavaObject* javaObj = static_cast<JavaObject*>(npObj); | |
| snprintf(buf, sizeof(buf), "javaObj(id=%d, ", javaObj->getObjectId()); | |
| } else { | |
| snprintf(buf, sizeof(buf), "jsObj(class=%p, ", npObj->_class); | |
| } | |
| retval = buf; | |
| snprintf(buf, sizeof(buf), "%p)", npObj); | |
| retval += buf; | |
| break; | |
| default: | |
| snprintf(buf, sizeof(buf), "Unknown type %d", variant.type); | |
| retval = buf; | |
| break; | |
| } | |
| return retval; | |
| } | |
| public: | |
| void release() { | |
| release(variant); | |
| } | |
| static void release(NPVariant& variant) { | |
| NPN_ReleaseVariantValue(&variant); | |
| } | |
| void retain() { | |
| retain(variant); | |
| } | |
| static void retain(NPVariant& variant) { | |
| if (NPVARIANT_IS_OBJECT(variant)) { | |
| NPN_RetainObject(NPVARIANT_TO_OBJECT(variant)); | |
| } | |
| } | |
| }; | |
| inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariant& var) { | |
| return dbg << NPVariantProxy::toString(var); | |
| } | |
| inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariantProxy& var) { | |
| return dbg << var.toString(); | |
| } | |
| /** | |
| * Variation of NPVariantProxy that provides its own variant and always frees it | |
| * when the wrapper goes away. | |
| */ | |
| class NPVariantWrapper { | |
| private: | |
| ScriptableInstance& plugin; | |
| NPVariant variant; | |
| public: | |
| NPVariantWrapper(ScriptableInstance& plugin) : plugin(plugin) { | |
| VOID_TO_NPVARIANT(variant); | |
| } | |
| ~NPVariantWrapper() { | |
| release(); | |
| } | |
| operator NPVariant() const { | |
| return variant; | |
| } | |
| const NPVariant* operator->() const { | |
| return &variant; | |
| } | |
| const NPVariant* address() const { | |
| return &variant; | |
| } | |
| /** | |
| * Get the address for use as a return value. Since the value can be trashed, | |
| * we need to release any data we currently hold. | |
| */ | |
| NPVariant* addressForReturn() { | |
| NPVariantProxy::release(variant); | |
| VOID_TO_NPVARIANT(variant); | |
| NPVariantProxy::retain(variant); // does nothing, present for consistency | |
| return &variant; | |
| } | |
| int isInt() const { | |
| return NPVariantProxy::isInt(variant); | |
| } | |
| int isObject() const { | |
| return NPVariantProxy::isObject(variant); | |
| } | |
| int isString() const { | |
| return NPVariantProxy::isString(variant); | |
| } | |
| int getAsInt() const { | |
| return NPVariantProxy::getAsInt(variant); | |
| } | |
| NPObject* getAsObject() const { | |
| return NPVariantProxy::getAsObject(variant); | |
| } | |
| const NPString* getAsNPString() const { | |
| return NPVariantProxy::getAsNPString(variant); | |
| } | |
| Value getAsValue(ScriptableInstance& scriptInstance, bool unwrapJava = true) const { | |
| return NPVariantProxy::getAsValue(variant, scriptInstance, unwrapJava); | |
| } | |
| /** | |
| * The incoming variant is not altered, and is not even required to have | |
| * its contents retained. Any object will get an extra refcount on it | |
| * when copied to this variant, and strings will be copied. | |
| */ | |
| NPVariantWrapper& operator=(const NPVariant& newval) { | |
| NPVariantProxy::assignFrom(variant, newval); | |
| return *this; | |
| } | |
| NPVariantWrapper& operator=(const Value& newval) { | |
| NPVariantProxy::assignFrom(plugin, variant, newval); | |
| return *this; | |
| } | |
| NPVariantWrapper& operator=(NPObject* obj) { | |
| NPVariantProxy::assignFrom(variant, obj); | |
| return *this; | |
| } | |
| // Convenience method for C++ code | |
| NPVariantWrapper& operator=(const std::string& strval) { | |
| NPVariantProxy::assignFrom(variant, strval); | |
| return *this; | |
| } | |
| // Convenience method for C++ code | |
| NPVariantWrapper& operator=(const char* strval) { | |
| NPVariantProxy::assignFrom(variant, strval); | |
| return *this; | |
| } | |
| void release() { | |
| NPVariantProxy::release(variant); | |
| } | |
| void retain() { | |
| NPVariantProxy::retain(variant); | |
| } | |
| std::string toString() const { | |
| return NPVariantProxy::toString(variant); | |
| } | |
| }; | |
| inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariantWrapper& var) { | |
| dbg << var.toString(); | |
| return dbg; | |
| } | |
| /** | |
| * Maintains an array of NPVariants and cleans them up when it is destroyed. | |
| */ | |
| class NPVariantArray { | |
| private: | |
| ScriptableInstance& plugin; | |
| int size; | |
| NPVariant* args; | |
| public: | |
| NPVariantArray(ScriptableInstance& plugin, int size) : plugin(plugin), size(size) { | |
| args = new NPVariant[size]; | |
| for (int i = 0; i < size; ++i) { | |
| VOID_TO_NPVARIANT(args[i]); | |
| } | |
| } | |
| ~NPVariantArray() { | |
| for (int i = 0; i < size; ++i) { | |
| NPN_ReleaseVariantValue(&args[i]); | |
| } | |
| delete [] args; | |
| } | |
| const NPVariant* getArray() const { | |
| return args; | |
| } | |
| int getSize() const { | |
| return size; | |
| } | |
| const NPVariant& operator[](int idx) const { | |
| if (idx >= size) { | |
| printf("NPVariantArray[idx=%d] const: size=%d\n", idx, size); | |
| } | |
| return args[idx]; | |
| } | |
| NPVariantProxy operator[](int idx) { | |
| if (idx >= size) { | |
| printf("NPVariantArray[idx=%d]: size=%d\n", idx, size); | |
| } | |
| return NPVariantProxy(plugin, args[idx], true); | |
| } | |
| }; | |
| inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariantArray& var) { | |
| dbg << "["; | |
| for (int i = 0; i < var.getSize(); ++i) { | |
| dbg << " " << var[i]; | |
| } | |
| dbg << " ]"; | |
| return dbg; | |
| } | |
| #endif |