| #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 isBoolean() const { |
| return isBoolean(variant); |
| } |
| |
| static int isBoolean(const NPVariant& variant) { |
| return NPVARIANT_IS_BOOLEAN(variant); |
| } |
| |
| bool getAsBoolean() const { |
| return getAsBoolean(variant); |
| } |
| |
| static bool getAsBoolean(const NPVariant& variant) { |
| return NPVARIANT_TO_BOOLEAN(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; |
| } |
| |
| bool isBoolean() const { |
| return NPVariantProxy::isBoolean(variant); |
| } |
| |
| int isInt() const { |
| return NPVariantProxy::isInt(variant); |
| } |
| |
| int isObject() const { |
| return NPVariantProxy::isObject(variant); |
| } |
| |
| int isString() const { |
| return NPVariantProxy::isString(variant); |
| } |
| |
| bool getAsBoolean() const { |
| return NPVariantProxy::getAsBoolean(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; |
| } |
| |
| // Convenience method for C++ code |
| NPVariantWrapper& operator=(int intval) { |
| NPVariantProxy::assignFrom(variant, intval); |
| 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 |